+
+
diff --git a/build/wp8/LibLinphone_no_tunnel.vcxproj b/build/wp8/LibLinphone_no_tunnel.vcxproj
new file mode 100644
index 000000000..4c31ffbb1
--- /dev/null
+++ b/build/wp8/LibLinphone_no_tunnel.vcxproj
@@ -0,0 +1,221 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ ARM
+
+
+ Release
+ Win32
+
+
+ Release
+ ARM
+
+
+
+ {08dd0d38-d9b5-4626-b60d-b4d76b571142}
+ LibLinphone
+ en-US
+ 11.0
+
+
+
+ DynamicLibrary
+ true
+ v110_wp80
+ false
+
+
+ DynamicLibrary
+ false
+ true
+ v110_wp80
+ false
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\
+
+
+ false
+
+
+
+ Level4
+ $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;$(ProjectDir)..\..\..\sqlite\;$(ProjectDir);%(AdditionalIncludeDirectories)
+ __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="\\linphone\\plugins";UNICODE;_XKEYCHECK_H;HAVE_ZLIB;HAVE_CONFIG_H;%(PreprocessorDefinitions)
+ Default
+ NotUsing
+ false
+ $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
+
+
+ Console
+ false
+ false
+ belle-sip_no_tunnel.lib;mediastreamer2.lib;ws2_32.lib;ortp.lib;gsm.lib;speex.lib;speexdsp.lib;libxml2.lib;sqlite.lib;zlib.lib;%(AdditionalDependencies)
+ $(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)
+ $(TargetDir)$(TargetName).lib
+
+
+ version.bat
+
+
+ Batch script to get the git version
+
+
+
+
+ _DEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions)
+
+
+ true
+
+
+ false
+
+
+
+
+ NDEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions)
+ MaxSpeed
+ true
+ true
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+ false
+
+
+
+
+ {1db09afe-fc9b-472e-a746-0e33f8ef8883}
+
+
+ {4c225a82-800b-427b-ba7b-61686a9b347f}
+
+
+ {9924ac72-f96c-4e56-94d9-2b025da43c6b}
+
+
+ {072fad20-7007-4da2-b2e7-16ce2b219f67}
+
+
+ {36b528f9-fb79-4078-a16b-0a7442581bb7}
+
+
+ {d22bd217-d0f8-4274-9b3a-f3f35f46482c}
+
+
+ {b16b81a9-bef2-44c9-b603-1065183ae844}
+
+
+ {0565952a-ea62-46a2-8261-f5b4b490da42}
+
+
+ {a45d63b9-60de-476c-8836-f8eedbe139d0}
+
+
+ {027bad0e-9179-48c1-9733-7aa7e2c2ec70}
+
+
+ {ffc7b532-0502-4d88-ac98-9e89071cbc97}
+
+
+ {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b}
+
+
+ {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f}
+
+
+
+
+
diff --git a/build/wp8/libxml2/install_headers.bat b/build/wp8/libxml2/install_headers.bat
new file mode 100644
index 000000000..d33f20c04
--- /dev/null
+++ b/build/wp8/libxml2/install_headers.bat
@@ -0,0 +1,6 @@
+SET curdir=%CD%
+SET incdir=..\..\..\..\libxml2\include\libxml
+SET installdir=%1\libxml
+
+Xcopy /I /Y %incdir%\*.h %installdir%\
+Xcopy /I /Y xmlversion.h %installdir%\
diff --git a/build/wp8/libxml2/libxml2.sln b/build/wp8/libxml2/libxml2.sln
new file mode 100644
index 000000000..b10b61b6b
--- /dev/null
+++ b/build/wp8/libxml2/libxml2.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Phone
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxml2", "libxml2.vcxproj", "{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|Win32 = Debug|Win32
+ Release|ARM = Release|ARM
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.ActiveCfg = Debug|ARM
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|ARM.Build.0 = Debug|ARM
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Win32.Build.0 = Debug|Win32
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.ActiveCfg = Release|ARM
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|ARM.Build.0 = Release|ARM
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.ActiveCfg = Release|Win32
+ {5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/build/wp8/libxml2/libxml2.vcxproj b/build/wp8/libxml2/libxml2.vcxproj
new file mode 100644
index 000000000..0e1f21c63
--- /dev/null
+++ b/build/wp8/libxml2/libxml2.vcxproj
@@ -0,0 +1,159 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ ARM
+
+
+ Release
+ Win32
+
+
+ Release
+ ARM
+
+
+
+ {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b}
+ libxml2
+ en-US
+ 11.0
+
+
+
+ DynamicLibrary
+ true
+ v110_wp80
+ false
+
+
+ DynamicLibrary
+ false
+ true
+ v110_wp80
+ false
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\
+
+
+ false
+
+
+
+ Level4
+ $(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories)
+ _WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions)
+ LIBXML_MODULES_ENABLED
+ Default
+ NotUsing
+ false
+ $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
+ $(ProjectDir)libxml2_port.h
+
+
+ Console
+ false
+ false
+ $(TargetDir)$(TargetName).lib
+ Ws2_32.lib;%(AdditionalDependencies)
+
+
+ install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ true
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ MaxSpeed
+ true
+ true
+ true
+
+
+ false
+
+
+
+
+ true
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/wp8/libxml2/libxml2_port.h b/build/wp8/libxml2/libxml2_port.h
new file mode 100644
index 000000000..bde826374
--- /dev/null
+++ b/build/wp8/libxml2/libxml2_port.h
@@ -0,0 +1,19 @@
+
+#ifndef LIBXML2_PORT_H
+#define LIBXML2_PORT_H
+
+#define CreateMutex(a, b, c) CreateMutexExW(a, c, ((b) ? CREATE_MUTEX_INITIAL_OWNER : 0), 0)
+
+#define GetVersionEx(osvi) (((osvi)->dwPlatformId = 0) != 0)
+
+#define InitializeCriticalSection(cs) InitializeCriticalSectionEx(cs, 0, 0)
+
+#define WaitForSingleObject(hHandle, dwMilliseconds) WaitForSingleObjectEx(hHandle, dwMilliseconds, 0)
+
+#define Sleep(ms) { \
+ HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); \
+ if (!sleepEvent) return; \
+ WaitForSingleObjectEx(sleepEvent, ms, FALSE); \
+}
+
+#endif /* LIBXML2_PORT_H */
diff --git a/build/wp8/libxml2/xmlversion.h b/build/wp8/libxml2/xmlversion.h
new file mode 100644
index 000000000..40c192eba
--- /dev/null
+++ b/build/wp8/libxml2/xmlversion.h
@@ -0,0 +1,476 @@
+/*
+ * Summary: compile-time version informations
+ * Description: compile-time version informations for the XML library
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_VERSION_H__
+#define __XML_VERSION_H__
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * use those to be sure nothing nasty will happen if
+ * your library and includes mismatch
+ */
+#ifndef LIBXML2_COMPILING_MSCCDEF
+XMLPUBFUN void XMLCALL xmlCheckVersion(int version);
+#endif /* LIBXML2_COMPILING_MSCCDEF */
+
+/**
+ * LIBXML_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXML_DOTTED_VERSION "2.8.0"
+
+/**
+ * LIBXML_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBXML_VERSION 20800
+
+/**
+ * LIBXML_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBXML_VERSION_STRING "20800"
+
+/**
+ * LIBXML_VERSION_EXTRA:
+ *
+ * extra version information, used to show a CVS compilation
+ */
+#define LIBXML_VERSION_EXTRA ""
+
+/**
+ * LIBXML_TEST_VERSION:
+ *
+ * Macro to check that the libxml version in use is compatible with
+ * the version the software has been compiled against
+ */
+#define LIBXML_TEST_VERSION xmlCheckVersion(20800);
+
+#ifndef VMS
+#if 0
+/**
+ * WITH_TRIO:
+ *
+ * defined if the trio support need to be configured in
+ */
+#define WITH_TRIO
+#else
+/**
+ * WITHOUT_TRIO:
+ *
+ * defined if the trio support should not be configured in
+ */
+#define WITHOUT_TRIO
+#endif
+#else /* VMS */
+/**
+ * WITH_TRIO:
+ *
+ * defined if the trio support need to be configured in
+ */
+#define WITH_TRIO 1
+#endif /* VMS */
+
+/**
+ * LIBXML_THREAD_ENABLED:
+ *
+ * Whether the thread support is configured in
+ */
+#if 1
+#if defined(_REENTRANT) || defined(__MT__) || \
+ (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0 >= 199506L))
+#define LIBXML_THREAD_ENABLED
+#endif
+#endif
+
+/**
+ * LIBXML_TREE_ENABLED:
+ *
+ * Whether the DOM like tree manipulation API support is configured in
+ */
+#if 1
+#define LIBXML_TREE_ENABLED
+#endif
+
+/**
+ * LIBXML_OUTPUT_ENABLED:
+ *
+ * Whether the serialization/saving support is configured in
+ */
+#if 1
+#define LIBXML_OUTPUT_ENABLED
+#endif
+
+/**
+ * LIBXML_PUSH_ENABLED:
+ *
+ * Whether the push parsing interfaces are configured in
+ */
+#if 1
+#define LIBXML_PUSH_ENABLED
+#endif
+
+/**
+ * LIBXML_READER_ENABLED:
+ *
+ * Whether the xmlReader parsing interface is configured in
+ */
+#if 1
+#define LIBXML_READER_ENABLED
+#endif
+
+/**
+ * LIBXML_PATTERN_ENABLED:
+ *
+ * Whether the xmlPattern node selection interface is configured in
+ */
+#if 1
+#define LIBXML_PATTERN_ENABLED
+#endif
+
+/**
+ * LIBXML_WRITER_ENABLED:
+ *
+ * Whether the xmlWriter saving interface is configured in
+ */
+#if 1
+#define LIBXML_WRITER_ENABLED
+#endif
+
+/**
+ * LIBXML_SAX1_ENABLED:
+ *
+ * Whether the older SAX1 interface is configured in
+ */
+#if 1
+#define LIBXML_SAX1_ENABLED
+#endif
+
+/**
+ * LIBXML_FTP_ENABLED:
+ *
+ * Whether the FTP support is configured in
+ */
+#if 1
+#define LIBXML_FTP_ENABLED
+#endif
+
+/**
+ * LIBXML_HTTP_ENABLED:
+ *
+ * Whether the HTTP support is configured in
+ */
+#if 1
+#define LIBXML_HTTP_ENABLED
+#endif
+
+/**
+ * LIBXML_VALID_ENABLED:
+ *
+ * Whether the DTD validation support is configured in
+ */
+#if 1
+#define LIBXML_VALID_ENABLED
+#endif
+
+/**
+ * LIBXML_HTML_ENABLED:
+ *
+ * Whether the HTML support is configured in
+ */
+#if 1
+#define LIBXML_HTML_ENABLED
+#endif
+
+/**
+ * LIBXML_LEGACY_ENABLED:
+ *
+ * Whether the deprecated APIs are compiled in for compatibility
+ */
+#if 1
+#define LIBXML_LEGACY_ENABLED
+#endif
+
+/**
+ * LIBXML_C14N_ENABLED:
+ *
+ * Whether the Canonicalization support is configured in
+ */
+#if 1
+#define LIBXML_C14N_ENABLED
+#endif
+
+/**
+ * LIBXML_CATALOG_ENABLED:
+ *
+ * Whether the Catalog support is configured in
+ */
+#if 0
+#define LIBXML_CATALOG_ENABLED
+#endif
+
+/**
+ * LIBXML_DOCB_ENABLED:
+ *
+ * Whether the SGML Docbook support is configured in
+ */
+#if 1
+#define LIBXML_DOCB_ENABLED
+#endif
+
+/**
+ * LIBXML_XPATH_ENABLED:
+ *
+ * Whether XPath is configured in
+ */
+#if 1
+#define LIBXML_XPATH_ENABLED
+#endif
+
+/**
+ * LIBXML_XPTR_ENABLED:
+ *
+ * Whether XPointer is configured in
+ */
+#if 1
+#define LIBXML_XPTR_ENABLED
+#endif
+
+/**
+ * LIBXML_XINCLUDE_ENABLED:
+ *
+ * Whether XInclude is configured in
+ */
+#if 1
+#define LIBXML_XINCLUDE_ENABLED
+#endif
+
+/**
+ * LIBXML_ICONV_ENABLED:
+ *
+ * Whether iconv support is available
+ */
+#if 0
+#define LIBXML_ICONV_ENABLED
+#endif
+
+/**
+ * LIBXML_ICU_ENABLED:
+ *
+ * Whether icu support is available
+ */
+#if 0
+#define LIBXML_ICU_ENABLED
+#endif
+
+/**
+ * LIBXML_ISO8859X_ENABLED:
+ *
+ * Whether ISO-8859-* support is made available in case iconv is not
+ */
+#if 0
+#define LIBXML_ISO8859X_ENABLED
+#endif
+
+/**
+ * LIBXML_DEBUG_ENABLED:
+ *
+ * Whether Debugging module is configured in
+ */
+#if 1
+#define LIBXML_DEBUG_ENABLED
+#endif
+
+/**
+ * DEBUG_MEMORY_LOCATION:
+ *
+ * Whether the memory debugging is configured in
+ */
+#if 0
+#define DEBUG_MEMORY_LOCATION
+#endif
+
+/**
+ * LIBXML_DEBUG_RUNTIME:
+ *
+ * Whether the runtime debugging is configured in
+ */
+#if 0
+#define LIBXML_DEBUG_RUNTIME
+#endif
+
+/**
+ * LIBXML_UNICODE_ENABLED:
+ *
+ * Whether the Unicode related interfaces are compiled in
+ */
+#if 1
+#define LIBXML_UNICODE_ENABLED
+#endif
+
+/**
+ * LIBXML_REGEXP_ENABLED:
+ *
+ * Whether the regular expressions interfaces are compiled in
+ */
+#if 1
+#define LIBXML_REGEXP_ENABLED
+#endif
+
+/**
+ * LIBXML_AUTOMATA_ENABLED:
+ *
+ * Whether the automata interfaces are compiled in
+ */
+#if 1
+#define LIBXML_AUTOMATA_ENABLED
+#endif
+
+/**
+ * LIBXML_EXPR_ENABLED:
+ *
+ * Whether the formal expressions interfaces are compiled in
+ */
+#if 1
+#define LIBXML_EXPR_ENABLED
+#endif
+
+/**
+ * LIBXML_SCHEMAS_ENABLED:
+ *
+ * Whether the Schemas validation interfaces are compiled in
+ */
+#if 1
+#define LIBXML_SCHEMAS_ENABLED
+#endif
+
+/**
+ * LIBXML_SCHEMATRON_ENABLED:
+ *
+ * Whether the Schematron validation interfaces are compiled in
+ */
+#if 1
+#define LIBXML_SCHEMATRON_ENABLED
+#endif
+
+/**
+ * LIBXML_MODULES_ENABLED:
+ *
+ * Whether the module interfaces are compiled in
+ */
+#if 0
+#define LIBXML_MODULES_ENABLED
+/**
+ * LIBXML_MODULE_EXTENSION:
+ *
+ * the string suffix used by dynamic modules (usually shared libraries)
+ */
+#define LIBXML_MODULE_EXTENSION ".dll"
+#endif
+
+/**
+ * LIBXML_ZLIB_ENABLED:
+ *
+ * Whether the Zlib support is compiled in
+ */
+#if 0
+#define LIBXML_ZLIB_ENABLED
+#endif
+
+/**
+ * LIBXML_LZMA_ENABLED:
+ *
+ * Whether the Lzma support is compiled in
+ */
+#if 0
+#define LIBXML_LZMA_ENABLED
+#endif
+
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * Macro used to signal to GCC unused function parameters
+ */
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+
+/**
+ * LIBXML_ATTR_ALLOC_SIZE:
+ *
+ * Macro used to indicate to GCC this is an allocator function
+ */
+
+#ifndef LIBXML_ATTR_ALLOC_SIZE
+# if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))
+# define LIBXML_ATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x)))
+# else
+# define LIBXML_ATTR_ALLOC_SIZE(x)
+# endif
+#else
+# define LIBXML_ATTR_ALLOC_SIZE(x)
+#endif
+
+/**
+ * LIBXML_ATTR_FORMAT:
+ *
+ * Macro used to indicate to GCC the parameter are printf like
+ */
+
+#ifndef LIBXML_ATTR_FORMAT
+# if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
+# define LIBXML_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args)))
+# else
+# define LIBXML_ATTR_FORMAT(fmt,args)
+# endif
+#else
+# define LIBXML_ATTR_FORMAT(fmt,args)
+#endif
+
+#else /* ! __GNUC__ */
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * Macro used to signal to GCC unused function parameters
+ */
+#define ATTRIBUTE_UNUSED
+/**
+ * LIBXML_ATTR_ALLOC_SIZE:
+ *
+ * Macro used to indicate to GCC this is an allocator function
+ */
+#define LIBXML_ATTR_ALLOC_SIZE(x)
+/**
+ * LIBXML_ATTR_FORMAT:
+ *
+ * Macro used to indicate to GCC the parameter are printf like
+ */
+#define LIBXML_ATTR_FORMAT(fmt,args)
+#endif /* __GNUC__ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
+
diff --git a/build/wp8/version.bat b/build/wp8/version.bat
new file mode 100644
index 000000000..55ee42831
--- /dev/null
+++ b/build/wp8/version.bat
@@ -0,0 +1,22 @@
+@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/zlib/zconf.h b/build/wp8/zlib/zconf.h
new file mode 100644
index 000000000..a3a6b54fc
--- /dev/null
+++ b/build/wp8/zlib/zconf.h
@@ -0,0 +1,513 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+/* #undef Z_PREFIX */
+#define Z_HAVE_UNISTD_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ * Even better than compiling with -DZ_PREFIX would be to use configure to set
+ * this permanently in zconf.h using "./configure --zprefix".
+ */
+#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */
+# define Z_PREFIX_SET
+
+/* all linked symbols */
+# define _dist_code z__dist_code
+# define _length_code z__length_code
+# define _tr_align z__tr_align
+# define _tr_flush_bits z__tr_flush_bits
+# define _tr_flush_block z__tr_flush_block
+# define _tr_init z__tr_init
+# define _tr_stored_block z__tr_stored_block
+# define _tr_tally z__tr_tally
+# define adler32 z_adler32
+# define adler32_combine z_adler32_combine
+# define adler32_combine64 z_adler32_combine64
+# ifndef Z_SOLO
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# endif
+# define crc32 z_crc32
+# define crc32_combine z_crc32_combine
+# define crc32_combine64 z_crc32_combine64
+# define deflate z_deflate
+# define deflateBound z_deflateBound
+# define deflateCopy z_deflateCopy
+# define deflateEnd z_deflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateInit_ z_deflateInit_
+# define deflateParams z_deflateParams
+# define deflatePending z_deflatePending
+# define deflatePrime z_deflatePrime
+# define deflateReset z_deflateReset
+# define deflateResetKeep z_deflateResetKeep
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateSetHeader z_deflateSetHeader
+# define deflateTune z_deflateTune
+# define deflate_copyright z_deflate_copyright
+# define get_crc_table z_get_crc_table
+# ifndef Z_SOLO
+# define gz_error z_gz_error
+# define gz_intmax z_gz_intmax
+# define gz_strwinerror z_gz_strwinerror
+# define gzbuffer z_gzbuffer
+# define gzclearerr z_gzclearerr
+# define gzclose z_gzclose
+# define gzclose_r z_gzclose_r
+# define gzclose_w z_gzclose_w
+# define gzdirect z_gzdirect
+# define gzdopen z_gzdopen
+# define gzeof z_gzeof
+# define gzerror z_gzerror
+# define gzflush z_gzflush
+# define gzgetc z_gzgetc
+# define gzgetc_ z_gzgetc_
+# define gzgets z_gzgets
+# define gzoffset z_gzoffset
+# define gzoffset64 z_gzoffset64
+# define gzopen z_gzopen
+# define gzopen64 z_gzopen64
+# ifdef _WIN32
+# define gzopen_w z_gzopen_w
+# endif
+# define gzprintf z_gzprintf
+# define gzvprintf z_gzvprintf
+# define gzputc z_gzputc
+# define gzputs z_gzputs
+# define gzread z_gzread
+# define gzrewind z_gzrewind
+# define gzseek z_gzseek
+# define gzseek64 z_gzseek64
+# define gzsetparams z_gzsetparams
+# define gztell z_gztell
+# define gztell64 z_gztell64
+# define gzungetc z_gzungetc
+# define gzwrite z_gzwrite
+# endif
+# define inflate z_inflate
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define inflateBackInit_ z_inflateBackInit_
+# define inflateCopy z_inflateCopy
+# define inflateEnd z_inflateEnd
+# define inflateGetHeader z_inflateGetHeader
+# define inflateInit2_ z_inflateInit2_
+# define inflateInit_ z_inflateInit_
+# define inflateMark z_inflateMark
+# define inflatePrime z_inflatePrime
+# define inflateReset z_inflateReset
+# define inflateReset2 z_inflateReset2
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateGetDictionary z_inflateGetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateUndermine z_inflateUndermine
+# define inflateResetKeep z_inflateResetKeep
+# define inflate_copyright z_inflate_copyright
+# define inflate_fast z_inflate_fast
+# define inflate_table z_inflate_table
+# ifndef Z_SOLO
+# define uncompress z_uncompress
+# endif
+# define zError z_zError
+# ifndef Z_SOLO
+# define zcalloc z_zcalloc
+# define zcfree z_zcfree
+# endif
+# define zlibCompileFlags z_zlibCompileFlags
+# define zlibVersion z_zlibVersion
+
+/* all zlib typedefs in zlib.h and zconf.h */
+# define Byte z_Byte
+# define Bytef z_Bytef
+# define alloc_func z_alloc_func
+# define charf z_charf
+# define free_func z_free_func
+# ifndef Z_SOLO
+# define gzFile z_gzFile
+# endif
+# define gz_header z_gz_header
+# define gz_headerp z_gz_headerp
+# define in_func z_in_func
+# define intf z_intf
+# define out_func z_out_func
+# define uInt z_uInt
+# define uIntf z_uIntf
+# define uLong z_uLong
+# define uLongf z_uLongf
+# define voidp z_voidp
+# define voidpc z_voidpc
+# define voidpf z_voidpf
+
+/* all zlib structs in zlib.h and zconf.h */
+# define gz_header_s z_gz_header_s
+# define internal_state z_internal_state
+
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+#if defined(ZLIB_CONST) && !defined(z_const)
+# define z_const const
+#else
+# define z_const
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+#ifndef Z_ARG /* function prototypes for stdarg */
+# if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# define Z_ARG(args) args
+# else
+# define Z_ARG(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
+# include
+# if (UINT_MAX == 0xffffffffUL)
+# define Z_U4 unsigned
+# elif (ULONG_MAX == 0xffffffffUL)
+# define Z_U4 unsigned long
+# elif (USHRT_MAX == 0xffffffffUL)
+# define Z_U4 unsigned short
+# endif
+#endif
+
+#ifdef Z_U4
+ typedef Z_U4 z_crc_t;
+#else
+ typedef unsigned long z_crc_t;
+#endif
+
+#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */
+# define Z_HAVE_UNISTD_H
+#endif
+
+#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */
+# define Z_HAVE_STDARG_H
+#endif
+
+#ifdef STDC
+# ifndef Z_SOLO
+# include /* for off_t */
+# endif
+#endif
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# ifndef Z_SOLO
+# include /* for va_list */
+# endif
+#endif
+
+#ifdef _WIN32
+# ifndef Z_SOLO
+# include /* for wchar_t */
+# endif
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
+# undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
+# define Z_HAVE_UNISTD_H
+#endif
+#ifndef Z_SOLO
+# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+# ifdef VMS
+# include /* for off_t */
+# endif
+# ifndef z_off_t
+# define z_off_t off_t
+# endif
+# endif
+#endif
+
+#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
+# define Z_LFS64
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
+# define Z_LARGE64
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
+# define Z_WANT64
+#endif
+
+#if !defined(SEEK_SET) && !defined(Z_SOLO)
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if !defined(_WIN32) && defined(Z_LARGE64)
+# define z_off64_t off64_t
+#else
+# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+# define z_off64_t __int64
+# else
+# define z_off64_t z_off_t
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+ #pragma map(deflateInit_,"DEIN")
+ #pragma map(deflateInit2_,"DEIN2")
+ #pragma map(deflateEnd,"DEEND")
+ #pragma map(deflateBound,"DEBND")
+ #pragma map(inflateInit_,"ININ")
+ #pragma map(inflateInit2_,"ININ2")
+ #pragma map(inflateEnd,"INEND")
+ #pragma map(inflateSync,"INSY")
+ #pragma map(inflateSetDictionary,"INSEDI")
+ #pragma map(compressBound,"CMBND")
+ #pragma map(inflate_table,"INTABL")
+ #pragma map(inflate_fast,"INFA")
+ #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/build/wp8/zlib/zlib.sln b/build/wp8/zlib/zlib.sln
new file mode 100644
index 000000000..97b0cc6a9
--- /dev/null
+++ b/build/wp8/zlib/zlib.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Phone
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|Win32 = Debug|Win32
+ Release|ARM = Release|ARM
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.ActiveCfg = Debug|ARM
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.Build.0 = Debug|ARM
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.Build.0 = Debug|Win32
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.ActiveCfg = Release|ARM
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.Build.0 = Release|ARM
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.ActiveCfg = Release|Win32
+ {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/build/wp8/zlib/zlib.vcxproj b/build/wp8/zlib/zlib.vcxproj
new file mode 100644
index 000000000..34c9aa46f
--- /dev/null
+++ b/build/wp8/zlib/zlib.vcxproj
@@ -0,0 +1,135 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ ARM
+
+
+ Release
+ Win32
+
+
+ Release
+ ARM
+
+
+
+ {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f}
+ zlib
+ en-US
+ 11.0
+
+
+
+ DynamicLibrary
+ true
+ v110_wp80
+ false
+
+
+ DynamicLibrary
+ false
+ true
+ v110_wp80
+ false
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\
+
+
+ false
+
+
+
+ Level4
+ $(ProjectDir);$(ProjectDir)..\..\..\..\zlib;%(AdditionalIncludeDirectories)
+ _WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;UNICODE;%(PreprocessorDefinitions)
+ Default
+ NotUsing
+ false
+ $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
+
+
+ Console
+ false
+ false
+ $(TargetDir)$(TargetName).lib
+ Ws2_32.lib;%(AdditionalDependencies)
+ $(ProjectDir)..\..\..\..\zlib\win32\zlib.def
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ true
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ MaxSpeed
+ true
+ true
+ true
+
+
+ false
+
+
+
+
+ true
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cmake/FindCUnit.cmake b/cmake/FindCUnit.cmake
new file mode 100644
index 000000000..10c1a10b6
--- /dev/null
+++ b/cmake/FindCUnit.cmake
@@ -0,0 +1,58 @@
+############################################################################
+# FindCUnit.txt
+# Copyright (C) 2015 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the CUnit include file and library
+#
+# CUNIT_FOUND - system has CUnit
+# CUNIT_INCLUDE_DIRS - the CUnit include directory
+# CUNIT_LIBRARIES - The libraries needed to use CUnit
+
+include(CheckIncludeFile)
+include(CheckLibraryExists)
+
+set(_CUNIT_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(CUNIT_INCLUDE_DIRS
+ NAMES CUnit/CUnit.h
+ HINTS _CUNIT_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(CUNIT_INCLUDE_DIRS)
+ set(HAVE_CUNIT_CUNIT_H 1)
+endif()
+
+find_library(CUNIT_LIBRARIES
+ NAMES cunit
+ HINTS ${_CUNIT_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(CUnit
+ DEFAULT_MSG
+ CUNIT_INCLUDE_DIRS CUNIT_LIBRARIES
+)
+
+mark_as_advanced(CUNIT_INCLUDE_DIRS CUNIT_LIBRARIES)
diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake
new file mode 100644
index 000000000..c65317a56
--- /dev/null
+++ b/cmake/FindIconv.cmake
@@ -0,0 +1,55 @@
+############################################################################
+# FindIconv.cmake
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the iconv include file and library
+#
+# ICONV_FOUND - system has libiconv
+# ICONV_INCLUDE_DIRS - the libiconv include directory
+# ICONV_LIBRARIES - The libraries needed to use libiconv
+
+set(_ICONV_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(ICONV_INCLUDE_DIRS
+ NAMES iconv.h
+ HINTS _ICONV_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(ICONV_INCLUDE_DIRS)
+ set(HAVE_ICONV_H 1)
+endif()
+
+find_library(ICONV_LIBRARIES
+ NAMES iconv
+ HINTS ${_ICONV_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Iconv
+ DEFAULT_MSG
+ ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H
+)
+
+mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H)
diff --git a/cmake/FindIntl.cmake b/cmake/FindIntl.cmake
new file mode 100644
index 000000000..35ec969cd
--- /dev/null
+++ b/cmake/FindIntl.cmake
@@ -0,0 +1,56 @@
+############################################################################
+# FindIntl.cmake
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the libintl include file and library
+#
+# INTL_FOUND - system has libintl
+# INTL_INCLUDE_DIRS - the libintl include directory
+# INTL_LIBRARIES - The libraries needed to use libintl
+
+set(_INTL_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(INTL_INCLUDE_DIRS
+ NAMES libintl.h
+ HINTS _INTL_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(INTL_INCLUDE_DIRS)
+ set(HAVE_LIBINTL_H 1)
+endif()
+
+set(INTL_ARGS INTL_INCLUDE_DIRS HAVE_LIBINTL_H)
+if(NOT UNIX OR APPLE)
+ find_library(INTL_LIBRARIES
+ NAMES intl
+ HINTS ${_INTL_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+ )
+ list(APPEND INTL_ARGS INTL_LIBRARIES)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Intl DEFAULT_MSG ${INTL_ARGS})
+
+mark_as_advanced(${INTL_ARGS})
diff --git a/cmake/FindNotify.cmake b/cmake/FindNotify.cmake
new file mode 100644
index 000000000..a9fb4d978
--- /dev/null
+++ b/cmake/FindNotify.cmake
@@ -0,0 +1,55 @@
+############################################################################
+# FindNotify.cmake
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the notify include file and library
+#
+# NOTIFY_FOUND - system has libnotify
+# NOTIFY_INCLUDE_DIRS - the libnotify include directory
+# NOTIFY_LIBRARIES - The libraries needed to use libnotify
+
+set(_NOTIFY_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(NOTIFY_INCLUDE_DIRS
+ NAMES libnotify/notify.h
+ HINTS _NOTIFY_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(NOTIFY_INCLUDE_DIRS)
+ set(HAVE_LIBNOTIFY_NOTIFY_H 1)
+endif()
+
+find_library(NOTIFY_LIBRARIES
+ NAMES notify
+ HINTS ${_NOTIFY_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Notify
+ DEFAULT_MSG
+ NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H
+)
+
+mark_as_advanced(NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H)
diff --git a/cmake/FindSqlite3.cmake b/cmake/FindSqlite3.cmake
new file mode 100644
index 000000000..7cab06bd1
--- /dev/null
+++ b/cmake/FindSqlite3.cmake
@@ -0,0 +1,55 @@
+############################################################################
+# FindSqlite3.cmake
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the sqlite3 include file and library
+#
+# SQLITE3_FOUND - system has sqlite3
+# SQLITE3_INCLUDE_DIRS - the sqlite3 include directory
+# SQLITE3_LIBRARIES - The libraries needed to use sqlite3
+
+set(_SQLITE3_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(SQLITE3_INCLUDE_DIRS
+ NAMES sqlite3.h
+ HINTS _SQLITE3_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(SQLITE3_INCLUDE_DIRS)
+ set(HAVE_SQLITE3_H 1)
+endif()
+
+find_library(SQLITE3_LIBRARIES
+ NAMES sqlite3
+ HINTS ${_SQLITE3_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Sqlite3
+ DEFAULT_MSG
+ SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H
+)
+
+mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H)
diff --git a/cmake/FindXML2.cmake b/cmake/FindXML2.cmake
new file mode 100644
index 000000000..9d0eebe14
--- /dev/null
+++ b/cmake/FindXML2.cmake
@@ -0,0 +1,55 @@
+############################################################################
+# FindXML2.txt
+# Copyright (C) 2015 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the libxml2 include file and library
+#
+# XML2_FOUND - system has libxml2
+# XML2_INCLUDE_DIRS - the libxml2 include directory
+# XML2_LIBRARIES - The libraries needed to use libxml2
+
+set(_XML2_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(XML2_INCLUDE_DIRS
+ NAMES libxml/xmlreader.h
+ HINTS _XML2_ROOT_PATHS
+ PATH_SUFFIXES include/libxml2
+)
+
+if(XML2_INCLUDE_DIRS)
+ set(HAVE_LIBXML_XMLREADER_H 1)
+endif()
+
+find_library(XML2_LIBRARIES
+ NAMES xml2
+ HINTS ${_XML2_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(XML2
+ DEFAULT_MSG
+ XML2_INCLUDE_DIRS XML2_LIBRARIES
+)
+
+mark_as_advanced(XML2_INCLUDE_DIRS XML2_LIBRARIES)
diff --git a/cmake/FindZlib.cmake b/cmake/FindZlib.cmake
new file mode 100644
index 000000000..3a0935f76
--- /dev/null
+++ b/cmake/FindZlib.cmake
@@ -0,0 +1,68 @@
+############################################################################
+# FindZlib.txt
+# Copyright (C) 2015 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# - Find the zlib include file and library
+#
+# ZLIB_FOUND - system has zlib
+# ZLIB_INCLUDE_DIRS - the zlib include directory
+# ZLIB_LIBRARIES - The libraries needed to use zlib
+
+set(_ZLIB_ROOT_PATHS
+ ${CMAKE_INSTALL_PREFIX}
+)
+
+find_path(ZLIB_INCLUDE_DIRS
+ NAMES zlib.h
+ HINTS _ZLIB_ROOT_PATHS
+ PATH_SUFFIXES include
+)
+
+if(ZLIB_INCLUDE_DIRS)
+ set(HAVE_ZLIB_H 1)
+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
+ )
+else()
+ find_library(ZLIB_LIBRARIES
+ NAMES z zlib zlibd
+ HINTS ${_ZLIB_ROOT_PATHS}
+ PATH_SUFFIXES bin lib
+ )
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Zlib
+ DEFAULT_MSG
+ ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H
+)
+
+mark_as_advanced(ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H)
diff --git a/cmake/LinphoneConfig.cmake.in b/cmake/LinphoneConfig.cmake.in
new file mode 100644
index 000000000..d7aa6cc22
--- /dev/null
+++ b/cmake/LinphoneConfig.cmake.in
@@ -0,0 +1,51 @@
+############################################################################
+# LinphoneConfig.cmake
+# Copyright (C) 2015 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+#
+# Config file for the belle-sip package.
+# It defines the following variables:
+#
+# LINPHONE_FOUND - system has linphone
+# LINPHONE_INCLUDE_DIRS - the linphone include directory
+# LINPHONE_LIBRARIES - The libraries needed to use linphone
+# 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")
+find_package(Mediastreamer2 REQUIRED)
+find_package(BelleSIP REQUIRED)
+if(@ENABLE_TUNNEL@)
+ find_package(Tunnel)
+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@)
+list(APPEND LINPHONE_INCLUDE_DIRS ${MEDIASTREAMER2_INCLUDE_DIRS} ${BELLESIP_INCLUDE_DIRS})
+list(APPEND LINPHONE_LIBRARIES ${MEDIASTREAMER2_LIBRARIES} ${BELLESIP_LIBRARIES})
+set(LINPHONE_CPPFLAGS "${MEDIASTREAMER2_CPPFLAGS}")
+set(LINPHONE_LDFLAGS "${MEDIASTREAMER2_LDFLAGS} ${BELLESIP_LDFLAGS}")
+if(TUNNEL_FOUND)
+ list(APPEND LINPHONE_INCLUDE_DIRS ${TUNNEL_INCLUDE_DIRS})
+ list(APPEND LINPHONE_LIBRARIES ${TUNNEL_LIBRARIES})
+endif()
+set(LINPHONE_FOUND 1)
diff --git a/config.h.cmake b/config.h.cmake
new file mode 100644
index 000000000..35d7e2ac9
--- /dev/null
+++ b/config.h.cmake
@@ -0,0 +1,46 @@
+/***************************************************************************
+* config.h.cmake
+* Copyright (C) 2014 Belledonne Communications, Grenoble France
+*
+****************************************************************************
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+****************************************************************************/
+
+#define LINPHONE_MAJOR_VERSION ${LINPHONE_MAJOR_VERSION}
+#define LINPHONE_MINOR_VERSION ${LINPHONE_MINOR_VERSION}
+#define LINPHONE_MICRO_VERSION ${LINPHONE_MICRO_VERSION}
+#define LINPHONE_VERSION "${LINPHONE_VERSION}"
+#define LIBLINPHONE_VERSION "${LINPHONE_VERSION}"
+
+#define LINPHONE_ALL_LANGS "${LINPHONE_ALL_LANGS}"
+
+#define LINPHONE_PLUGINS_DIR "${LINPHONE_PLUGINS_DIR}"
+#define LINPHONE_CONFIG_DIR "${LINPHONE_CONFIG_DIR}"
+
+#define GETTEXT_PACKAGE "${GETTEXT_PACKAGE}"
+
+#define PACKAGE_LOCALE_DIR "${PACKAGE_LOCALE_DIR}"
+#define PACKAGE_DATA_DIR "${PACKAGE_DATA_DIR}"
+#define PACKAGE_SOUND_DIR "${PACKAGE_SOUND_DIR}"
+
+#cmakedefine BUILD_WIZARD
+#cmakedefine HAVE_NOTIFY4
+#cmakedefine HAVE_ZLIB 1
+#cmakedefine HAVE_CU_GET_SUITE 1
+#cmakedefine HAVE_CU_CURSES 1
+#cmakedefine HAVE_LIBUDEV_H 0
+#cmakedefine ENABLE_NLS 1
diff --git a/configure.ac b/configure.ac
index 43efbff30..191c7fd7d 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.5.99.0],[linphone-developers@nongnu.org])
+AC_INIT([linphone],[3.8.5],[linphone-developers@nongnu.org])
AC_CANONICAL_SYSTEM
AC_CONFIG_SRCDIR([coreapi/linphonecore.c])
@@ -17,33 +17,40 @@ if test "$LINPHONE_EXTRA_VERSION" != "" ;then
LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION}
fi
-LIBLINPHONE_SO_CURRENT=5 dnl increment this number when you add/change/remove an interface
+LIBLINPHONE_SO_CURRENT=7 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
LIBLINPHONE_SO_VERSION=$LIBLINPHONE_SO_CURRENT:$LIBLINPHONE_SO_REVISION:$LIBLINPHONE_SO_AGE
+AC_SUBST(LIBLINPHONE_SO_CURRENT, $LIBLINPHONE_SO_CURRENT)
AC_SUBST(LIBLINPHONE_SO_VERSION, $LIBLINPHONE_SO_VERSION)
AC_SUBST(LINPHONE_VERSION)
AC_MSG_NOTICE([$PACKAGE_NAME-$PACKAGE_VERSION A full featured audio/video sip phone.])
AC_MSG_NOTICE([licensed under the terms of the General Public License (GPL)])
-AM_INIT_AUTOMAKE
+AM_INIT_AUTOMAKE([1.9 tar-pax subdir-objects])
AC_SUBST([LIBTOOL_DEPS])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
AC_SUBST([docdir], [${datadir}/doc])
AC_CONFIG_HEADERS(config.h)
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])
+
+gl_LD_OUTPUT_DEF
+
AC_ISC_POSIX
-AC_PROG_CC
-AC_PROG_CXX
AC_C_INLINE
AC_HEADER_STDC
AM_PROG_CC_C_O
AC_CHECK_PROGS(MD5SUM,[md5sum md5])
AM_CONDITIONAL(HAVE_MD5SUM,test -n $MD5SUM)
+ios_found=no
+
case $target in
*mingw32ce)
CFLAGS="$CFLAGS -D_WIN32_WCE -DORTP_STATIC -D_WIN32_WINNT=0x0501"
@@ -53,36 +60,48 @@ case $target in
mingwce_found=yes
;;
*mingw*)
+ dnl Workaround for mingw, whose compiler does not check in /usr/include ...
+ CPPFLAGS="$CPPFLAGS -I/usr/include"
+ LDFLAGS="$LDFLAGS -L/usr/lib"
CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 "
CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501"
LIBS="$LIBS -lws2_32"
GUI_FLAGS="-mwindows"
CONSOLE_FLAGS="-mconsole"
mingw_found=yes
+ AC_CHECK_TOOL(WINDRES, windres)
;;
- armv6-apple-darwin|armv7-apple-darwin|i386-apple-darwin|armv7s-apple-darwin)
- CFLAGS="$CFLAGS -DTARGET_OS_IPHONE "
- build_tests=no
+ armv6-apple-darwin|armv7-apple-darwin|i386-apple-darwin|armv7s-apple-darwin|aarch64-apple-darwin|*-apple-darwin.ios)
+ CFLAGS="$CFLAGS -DTARGET_OS_IPHONE=1 "
+ LIBS="$LIBS -framework CoreFoundation -framework AudioToolbox -framework CoreAudio -framework Foundation -framework QuartzCore -framework OpenGLES -framework UIKit -framework AVFoundation"
ios_found=yes
;;
x86_64-apple-darwin*|i686-apple-darwin*)
MSPLUGINS_CFLAGS=""
dnl use macport installation
- ACLOCAL_MACOS_FLAGS="-I /opt/local/share/aclocal"
+ AS_IF([test -d "/opt/local/share/aclocal"], [ACLOCAL_MACOS_FLAGS="-I /opt/local/share/aclocal"])
build_macos=yes
;;
esac
+AM_CONDITIONAL(BUILD_IOS, test x$ios_found = xyes)
+
AC_SUBST(ACLOCAL_MACOS_FLAGS)
AC_SUBST(CONSOLE_FLAGS)
AC_SUBST(GUI_FLAGS)
+case "$build_os" in
+ *darwin*)
+ HTTPS_CA_DIR=`openssl version -d | sed "s/OPENSSLDIR: \"\(.*\)\"/\1/"`
+ ;;
+esac
+
+AC_SUBST(HTTPS_CA_DIR)
+
dnl localization tools
IT_PROG_INTLTOOL([0.40], [no-xml])
-AM_CONDITIONAL(BUILD_TESTS,test x$build_tests != xno)
-
dnl Initialize libtool
LT_INIT([win32-dll shared disable-static])
@@ -113,13 +132,13 @@ AC_CONFIG_COMMANDS([libtool-hacking],
dnl Add the languages which your application supports here.
PKG_PROG_PKG_CONFIG
-ALL_LINGUAS="fr it de ja es pl cs nl sv pt_BR hu ru zh_CN nb_NO zh_TW he"
+ALL_LINGUAS=$(cd po && echo *.po | sed 's/\.po//g')
AC_SUBST(ALL_LINGUAS)
AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages])
if test "$mingw_found" != "yes" ; then
dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK.
-
+
dnl AM_GNU_GETTEXT pollutes CPPFLAGS: workaround this.
CPPFLAGS_save=$CPPFLAGS
AM_GNU_GETTEXT([external])
@@ -127,28 +146,79 @@ if test "$mingw_found" != "yes" ; then
CPPFLAGS=$CPPFLAGS_save
LIBS="$LIBS $LIBINTL"
else
- AC_DEFINE(ENABLE_NLS,1,[Tells whether localisation is possible])
- AC_DEFINE(HAVE_GETTEXT,1,[Tells wheter localisation is possible])
- LIBS="$LIBS -lintl"
+ if test "$USE_NLS" = "yes" ; then
+ AC_DEFINE(HAVE_INTL,1,[Tells wheter localisation is possible])
+ LIBS="$LIBS -lintl"
+ fi
fi
GETTEXT_PACKAGE=linphone
-AC_SUBST(GETTEXT_PACKAGE)
+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)
AC_CHECK_FUNCS([get_current_dir_name strndup stpcpy] )
AC_ARG_ENABLE(x11,
- [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=no)])],
+ [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=yes for MacOSX, no otherwise)])],
[case "${enableval}" in
yes) enable_x11=true ;;
no) enable_x11=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;;
esac],
- [enable_x11=true]
+ [case "$target_os" in
+ *darwin*) enable_x11=false ;; #disable x11 on MacOS by default
+ *) enable_x11=true ;;
+ esac]
)
+dnl conditional build of LDAP support
+AC_ARG_ENABLE(ldap,
+ [AS_HELP_STRING([--enable-ldap], [Enables LDAP support (default=no)])],
+ [case "${enableval}" in
+ yes) enable_ldap=true ;;
+ no) enable_ldap=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ldap) ;;
+ esac],
+ [enable_ldap=false]
+)
+
+if test "$enable_ldap" = "true"; then
+ PKG_CHECK_MODULES(LDAP, [openldap],[found_ldap=yes], [found_ldap=no])
+ if test "$found_ldap" = "no"; then
+ AC_CHECK_LIB(ldap,ldap_initialize, [LDAP_LIBS="-lldap -llber"],
+ [AC_MSG_ERROR([You need libldap for LDAP support])]
+ )
+ AC_CHECK_HEADERS(ldap.h, [foo=bar], [AC_MSG_ERROR( [ldap.h not found] ) ] )
+ found_ldap=yes
+ fi
+
+ PKG_CHECK_MODULES(SASL, [libsasl2],[found_sasl=yes],[found_sasl=no] )
+
+ if test "$found_sasl" = "no"; then
+ AC_CHECK_LIB(sasl2, sasl_client_init , [SASL_LIBS="-lsasl2"],
+ [AC_MSG_ERROR([You need SASL for LDAP support] ) ]
+ )
+ AC_CHECK_HEADERS(sasl/sasl.h,foo=bar, [AC_MSG_ERROR([sasl/sasl.h not found])])
+ found_sasl=yes
+ fi
+
+ AC_SUBST(LDAP_CFLAGS)
+ AC_SUBST(LDAP_LIBS)
+
+ AC_SUBST(SASL_CFLAGS)
+ AC_SUBST(SASL_LIBS)
+
+ if test "$found_ldap$found_sasl" = "yesyes"; then
+ AC_DEFINE(BUILD_LDAP,1,[Defined if LDAP build option enabled])
+ else
+ AC_MSG_ERROR([Cannot use LDAP due to previous errors])
+ fi
+
+fi
+
+AM_CONDITIONAL(BUILD_LDAP, test x$enable_ldap != xfalse)
+
dnl conditionnal build of console interface.
AC_ARG_ENABLE(console_ui,
[AS_HELP_STRING([--enable-console_ui=[yes/no]], [Turn on or off compilation of console interface (default=yes)])],
@@ -183,7 +253,12 @@ AC_ARG_ENABLE(upnp,
)
if test "$build_upnp" != "false" ; then
- PKG_CHECK_MODULES([LIBUPNP], [libupnp], [build_upnp=true],
+ PKG_CHECK_MODULES([LIBUPNP], [libupnp],
+ [if pkg-config --atleast-version=1.6 "libupnp < 1.7"; then
+ build_upnp=true
+ else
+ AC_MSG_ERROR([libupnp >= 1.6 < 1.5 required.])
+ fi],
[if test "$build_upnp" == "true" ; then
AC_MSG_ERROR([libupnp not found.])
else
@@ -198,21 +273,53 @@ if test "$build_upnp" != "false" ; then
AC_DEFINE(BUILD_UPNP, 1, [Define if upnp enabled])
fi
-dnl check libxml2 (needed for tools)
-if test "$build_tools" != "false" ; then
- PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[],
- [if test "$build_tools" = "true" ; then
- AC_MSG_ERROR([Could not found libxml2, tools cannot be compiled.])
+dnl check zlib
+AC_ARG_ENABLE(zlib,
+ [AS_HELP_STRING([--disable-zlib], [Disable ZLib support])],
+ [case "${enableval}" in
+ yes) build_zlib=true ;;
+ no) build_zlib=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-zlib) ;;
+ esac],
+ [build_zlib=auto]
+)
+if test "$build_zlib" != "false" ; then
+ PKG_CHECK_MODULES(ZLIB, [zlib], [found_zlib=yes], [found_zlib=no])
+ if test "x$found_zlib" = "xno" ; then
+ AC_CHECK_LIB(z, inflate,
+ [AC_CHECK_HEADER([zlib.h],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #include
+ #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1230)
+ // compile error
+ #endif
+ ]],[])],
+ [found_zlib=yes])])])
+ if test "x$found_zlib" = "xno" ; then
+ AC_MSG_NOTICE([zlib library and headers not found])
else
- build_tools=false
- fi]
- )
+ AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] )
+ ZLIB_LIBS='-lz'
+ AC_SUBST(ZLIB_LIBS)
+ fi
+ else
+ AC_MSG_NOTICE([ZLIB found])
+ AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] )
+ fi
+fi
+
+
+dnl check libxml2
+PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[libxml2_found=yes],foo=bar)
+if test "$libxml2_found" != "yes" ; then
+ AC_MSG_ERROR([libxml2 not found. Install it and try again (the package is usually named libxml2-dev in the Linux distributions)])
fi
AM_CONDITIONAL(BUILD_TOOLS, test x$build_tools != xfalse)
if test "$build_tools" != "false" ; then
build_tools=true
- AC_DEFINE(BUILD_TOOLS, 1, [Define if tools enabled] )
+ AC_DEFINE(BUILD_TOOLS, 1, [Define if tools enabled] )
fi
dnl conditionnal build of gtk interface.
@@ -229,13 +336,24 @@ AC_ARG_ENABLE(gtk_ui,
if test "$gtk_ui" = "true" ; then
PKG_CHECK_MODULES(LIBGTK, gtk+-2.0 >= 2.18.0 gthread-2.0)
if test "$enable_x11" = "false" ; then
- PKG_CHECK_MODULES(LIBGTKMAC,[ige-mac-integration >= 0.9.7 ])
+ PKG_CHECK_MODULES(LIBGTKMAC,[gtk-mac-integration >= 2.0.1], [found_gtkmac=true], [found_gtkmac=false])
+ if test "$found_gtkmac" != "true" ; then
+ dnl for newest macports, the name changed.
+ PKG_CHECK_MODULES(LIBGTKMAC,[gtk-mac-integration-gtk2 >= 2.0.1], [found_gtkmac=true], [found_gtkmac=false])
+ fi
+ if test "$found_gtkmac" != "true" ; then
+ AC_MSG_ERROR([gtk-mac-integration not found. Please install gtk-osx-application package.])
+ fi
AC_DEFINE([HAVE_GTK_OSX],[1],[Defined when gtk osx is used])
fi
+
+ PKG_CHECK_MODULES(LIBGLIB, [glib-2.0 >= 2.26.0], [build_status_notifier=yes], [build_status_notifier=no])
else
echo "GTK interface compilation is disabled."
fi
+AM_CONDITIONAL([BUILD_STATUS_NOTIFIER], [test "$build_status_notifier" = "yes"])
+
AC_ARG_ENABLE(notify,
[AS_HELP_STRING([--enable-notify=[yes/no]], [Enable libnotify support (default=yes)])],
[case "${enableval}" in
@@ -265,7 +383,6 @@ if test "$gtk_ui" = "true" ; then
AC_DEFINE([HAVE_NOTIFY1],[1],[NOTIFY1 support])
esac
else
- NotifyNotification *n;
echo "Libnotify support is disabled."
fi
fi
@@ -351,6 +468,25 @@ AC_ARG_ENABLE(debug,
esac],
[debug_enabled=no]
)
+AS_CASE([$debug_enabled],
+ [yes],[
+ CFLAGS="$CFLAGS -g -O0 -DDEBUG"
+ CXXFLAGS="$CXXFLAGS -g -O0 -DDEBUG"
+ OBJCFLAGS="$OBJCFLAGS -g -O0 -DDEBUG"
+ ],
+ [no],
+ [
+ case "$CFLAGS" in
+ *-O*)
+ ;;
+ *)
+ CFLAGS="$CFLAGS -O2 -g"
+ CXXFLAGS="$CXXFLAGS -O2 -g"
+ OBJCFLAGS="$OBJCFLAGS -O2 -g"
+ ;;
+ esac
+ ],
+ [AC_MSG_ERROR([Bad value ($debug_enabled) for --enable-debug. Valid values are yes or no.])])
dnl enable truespeech codec support
AC_ARG_ENABLE(truespeech,
@@ -382,13 +518,6 @@ AC_ARG_ENABLE(nonstandard-gsm,
[exotic_gsm=no]
)
-
-dnl support for RSVP (by Vincent Maury)
-AC_ARG_ENABLE(rsvp,
- [AS_HELP_STRING([--enable-rsvp], [Enable support for QoS reservations.])],
- AC_DEFINE(VINCENT_MAURY_RSVP,1,[Tell whether RSVP support should be compiled.])
-)
-
if test "x${prefix}" = "xNONE"; then
package_prefix=${ac_default_prefix}
else
@@ -407,7 +536,14 @@ if test "$relativeprefix" = "yes" ; then
fi
dnl Set PACKAGE_LOCALE_DIR in config.h.
-DATADIRNAME=share
+case "$target_os" in
+ *qnx*)
+ DATADIRNAME=app/native/assets
+ ;;
+ *)
+ DATADIRNAME=share
+ ;;
+esac
AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${package_prefix}/${DATADIRNAME}/locale",[Defines the place where locales can be found])
AC_DEFINE_UNQUOTED(PACKAGE_DATA_DIR, "${package_prefix}/${DATADIRNAME}",[Defines the place where data are found])
@@ -419,39 +555,6 @@ AC_DEFINE_UNQUOTED(PACKAGE_SOUND_DIR, "${package_prefix}/${DATADIRNAME}/sounds/l
dnl check if we have the getifaddrs() sytem call
AC_CHECK_FUNCS(getifaddrs)
-dnl check for osip2
-LP_CHECK_OSIP2
-
-dnl conditionnal build for ssl
-AC_ARG_ENABLE(ssl,
- [AS_HELP_STRING([--enable-ssl], [Turn on ssl support compiling. Required for sip tls. (default=false)])],
- [case "${enableval}" in
- yes) build_ssl=true ;;
- no) build_ssl=false ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-ssl) ;;
- esac],
- [build_ssl=false]
-)
-
-if test "$build_ssl" = "true"; then
- PKG_CHECK_MODULES(OPENSSL, libssl >= 0.9.8)
-fi
-dnl setup flags for exosip library
-LP_SETUP_EXOSIP
-
-dnl check exosip support of DSCP in exosip
-AC_MSG_CHECKING([for DSCP support in exosip])
-AC_TRY_COMPILE([#include ],
- [int dscp=0;eXosip_set_option(EXOSIP_OPT_SET_DSCP,&dscp);],
- has_exosip_dscp=yes,
- has_exosip_dscp=no
-)
-AC_MSG_RESULT($has_exosip_dscp)
-if test "$has_exosip_dscp" = "yes" ; then
- AC_DEFINE( HAVE_EXOSIP_DSCP, 1, [Define if exosip dscp available] )
-fi
-
-
if test "$console_ui" = "true" ; then
dnl check gnu readline
LP_CHECK_READLINE
@@ -477,7 +580,7 @@ fi
dnl conditionnal build of video support
AC_ARG_ENABLE(video,
- [AS_HELP_STRING([--enable-video], [Turn on video support compiling])],
+ [AS_HELP_STRING([--enable-video], [Turn on video support compiling (default=yes)])],
[case "${enableval}" in
yes) video=true ;;
no) video=false ;;
@@ -493,10 +596,10 @@ AC_ARG_WITH(ffmpeg,
)
if test "$video" = "true"; then
-
+
if test "$enable_x11" = "true"; then
AC_CHECK_HEADERS(X11/Xlib.h)
- if test "$build_macos" = "yes"; then
+ if test "$build_macos" = "yes"; then
X11_LIBS="-L/usr/X11/lib -lX11"
else
AC_CHECK_LIB(X11,XUnmapWindow, X11_LIBS="-lX11")
@@ -516,6 +619,7 @@ AC_ARG_ENABLE(alsa,
[alsa=true]
)
+dnl this options are just for passing to mediastreamer2 subproject
AC_ARG_ENABLE(zrtp,
[AS_HELP_STRING([--enable-zrtp], [Turn on zrtp support])],
[case "${enableval}" in
@@ -526,17 +630,81 @@ AC_ARG_ENABLE(zrtp,
[zrtp=false]
)
-
-AC_ARG_ENABLE(portaudio,
- [AS_HELP_STRING([--enable-portaudio], [Turn on portaudio native support compiling])],
+dnl this options are just for passing to mediastreamer2 subproject
+AC_ARG_ENABLE(dtls,
+ [AS_HELP_STRING([--enable-dtls], [Turn on srtp-dtls support - requires polarssl > 1.4])],
[case "${enableval}" in
- yes) portaudio=true ;;
- no) portaudio=false ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-portaudio) ;;
+ yes) dtls=true ;;
+ no) dtls=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-dtls) ;;
esac],
- [portaudio=false]
+ [dtls=false]
)
+AC_ARG_ENABLE(g729bCN,
+ [AS_HELP_STRING([--enable-g729bCN], [Turn on or off usage of G729AnnexB in RFC3389 implementation of Comfort Noise Payload (default=no)])],
+ [case "${enableval}" in
+ yes) g729bCN=true ;;
+ no) g729bCN=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-g729bCN) ;;
+ esac],
+ [g729bCN=false]
+)
+
+dnl Polarssl lib is requested for Lime
+AC_ARG_WITH( polarssl,
+ [ --with-polarssl Set prefix where polarssl can be found (ex:/usr, /usr/local)[[default=PREFIX or /usr if NONE]] ],
+ [ polarssl_prefix=${withval}],[ if test "$prefix" != "NONE"; then
+ polarssl_prefix=${prefix}
+ else
+ polarssl_prefix="/usr"
+ fi ])
+
+found_polarssl=no
+
+if test "$polarssl_prefix" != "none" ; then
+ if test "$polarssl_prefix" != "/usr" ; then
+ POLARSSL_CFLAGS="-I${polarssl_prefix}/include"
+ POLARSSL_LIBS="-L${polarssl_prefix}/lib"
+ fi
+ POLARSSL_LIBS="$POLARSSL_LIBS -lpolarssl"
+
+ CPPFLAGS_save=$CPPFLAGS
+ LIBS_save=$LIBS
+
+ CPPFLAGS="$CPPFLAGS $POLARSSL_CFLAGS"
+ LIBS="$LIBS $POLARSSL_LIBS"
+ AC_CHECK_HEADERS(polarssl/gcm.h,
+ [found_polarssl=yes; AC_MSG_NOTICE([polarssl usable])],
+ [POLARSSL_CFLAGS=""
+ POLARSSL_LIBS=""])
+ CPPFLAGS=$CPPFLAGS_save
+ LIBS=$LIBS_save
+fi
+
+dnl check for Lime support, need polarssl version >= 1.3 (with gcm.h)
+AC_ARG_ENABLE(lime,
+ [AS_HELP_STRING([--enable-lime], [Turn on or off compilation of Instant Messaging Encryption (default=auto)])],
+ [case "${enableval}" in
+ yes) lime=true ;;
+ no) lime=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-lime) ;;
+ esac],
+ [lime=false]
+)
+
+if test "$lime" != "false" ; then
+ if test "x$found_polarssl" != "xyes" ; then
+ if test "$lime" = "true" ; then
+ AC_MSG_ERROR("LIME requires POLARSSL in version >= 1.3")
+ fi
+ lime=false
+ else
+ AC_DEFINE(HAVE_LIME, 1, [Defined when LIME support is compiled])
+ lime=true
+ fi
+fi
+
dnl build console if required
AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue)
@@ -546,7 +714,6 @@ AM_CONDITIONAL(ARMBUILD, test x$use_arm_toolchain = xyes)
dnl compilation of gtk user interface
AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] )
AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes )
-AM_CONDITIONAL(BUILD_ZRTP, test x$zrtp = xtrue)
dnl check getenv
AH_TEMPLATE([HAVE_GETENV])
@@ -571,16 +738,6 @@ AC_ARG_ENABLE(assistant,
[build_wizard=check]
)
-dnl check libsoup (needed for wizard)
-if test "$build_wizard" != "false" ; then
- PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26],[],
- [if test "$build_wizard" = "true" ; then
- AC_MSG_ERROR([Could not found libsoup, assistant cannot be compiled.])
- else
- build_wizard=false
- fi]
- )
-fi
if test "$build_wizard" != "false" ; then
PKG_CHECK_MODULES(LIBGTKWIZARD, [gtk+-2.0 >= 2.22.0],[],
[if test "$build_wizard" = "true" ; then
@@ -590,12 +747,10 @@ if test "$build_wizard" != "false" ; then
fi]
)
fi
-AC_SUBST(LIBSOUP_CFLAGS)
-AC_SUBST(LIBSOUP_LIBS)
AM_CONDITIONAL(BUILD_WIZARD, test x$build_wizard != xfalse)
if test "$build_wizard" != "false" ; then
build_wizard=true
- AC_DEFINE(BUILD_WIZARD, 1, [Define if wizard enabled] )
+ AC_DEFINE(BUILD_WIZARD, 1, [Define if wizard enabled] )
fi
AC_CHECK_HEADERS(libudev.h)
@@ -607,89 +762,45 @@ AC_CHECK_LIB(udev,udev_new)
AC_ARG_ENABLE(strict,
- AC_HELP_STRING([--enable-strict], [Build with stricter options (gcc only) @<:@yes@:>@]),
+ AC_HELP_STRING([--enable-strict], [Build with stricter options @<:@yes@:>@]),
[strictness="${enableval}"],
[strictness=yes]
)
-STRICT_OPTIONS="-Wall "
+STRICT_OPTIONS="-Wall -Wuninitialized"
+STRICT_OPTIONS_CC="-Wdeclaration-after-statement "
+STRICT_OPTIONS_CXX=""
+#for clang
+
+case $CC in
+ *clang*)
+ STRICT_OPTIONS="$STRICT_OPTIONS -Qunused-arguments "
+ #disabled due to wrong optimization false positive with small string
+ #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903)
+ STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds "
+ ;;
+esac
+# because Darwin's gcc is actually clang, we need to check it...
+case "$target_os" in
+ *darwin*)
+ STRICT_OPTIONS="$STRICT_OPTIONS -Wno-error=unknown-warning-option -Qunused-arguments -Wno-tautological-compare -Wno-unused-function "
+ #disabled due to wrong optimization false positive with small string
+ #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903)
+ STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds "
+ ;;
+esac
if test "$strictness" = "yes" ; then
STRICT_OPTIONS="$STRICT_OPTIONS -Werror"
CFLAGS="$CFLAGS -fno-strict-aliasing"
fi
AC_SUBST(STRICT_OPTIONS)
+AC_SUBST(STRICT_OPTIONS_CC)
+AC_SUBST(STRICT_OPTIONS_CXX)
top_srcdir=`dirname $0`
-AC_ARG_ENABLE([external-mediastreamer],
- [AS_HELP_STRING([--enable-external-mediastreamer],[Use external mediastreamer library])],,
- [enable_external_mediastreamer=no]
-)
-
-AS_CASE($enable_external_mediastreamer,
- [yes],
- [PKG_CHECK_MODULES([MEDIASTREAMER], [mediastreamer])
- MS2_VERSION=`$PKG_CONFIG --modversion mediastreamer`],
- [no],
- [AC_CONFIG_SUBDIRS( mediastreamer2 )
- MEDIASTREAMER_DIR=${top_srcdir}/mediastreamer2
- MEDIASTREAMER_CFLAGS="-I\$(top_srcdir)/mediastreamer2/include"
- MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer_base.la \$(top_builddir)/mediastreamer2/src/libmediastreamer_voip.la"
-dnl need to temporary change quotes to allow square brackets
- changequote(<<, >>)
- MS2_VERSION=`grep -e '^.C_INIT(' $MEDIASTREAMER_DIR/configure.ac | sed -e 's:\([^(]\+\)(\[mediastreamer\],\[\(.*\)\]):\2:g'`
- changequote([, ])
- MS2_DIR=mediastreamer2],
- [AC_MSG_ERROR([bad value '${enable_external_mediastreamer}' for --enable-external-mediastreamer])]
-)
-
-AC_SUBST(MEDIASTREAMER_CFLAGS)
-AC_SUBST(MEDIASTREAMER_LIBS)
-AC_SUBST([MS2_VERSION])
-AC_SUBST([MS2_DIR])
-
-
-
-AC_ARG_ENABLE(tunnel,
- [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])],
- [case "${enableval}" in
- yes) enable_tunnel=true ;;
- no) enable_tunnel=false ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-tunnel) ;;
- esac],
- [enable_tunnel=false]
-)
-AM_CONDITIONAL(BUILD_TUNNEL, test x$enable_tunnel = xtrue)
-if test x$enable_tunnel = xtrue; then
- PKG_CHECK_MODULES(TUNNEL, tunnel >= 0.3.3)
- TUNNEL_CFLAGS+="-DTUNNEL_ENABLED"
- AC_SUBST(TUNNEL_CFLAGS)
- AC_SUBST(TUNNEL_LIBS)
-fi
-
-
-dnl check for db2html (docbook) to generate html user manual
-AC_CHECK_PROG(have_sgmltools, sgmltools, yes, no)
-AM_CONDITIONAL(ENABLE_MANUAL, test x$have_sgmltools$build_manual = xyesyes )
-
-dnl for external use of linphone libs
-LINPHONE_CFLAGS="-I${includedir} -I${includedir}/linphone"
-LINPHONE_LIBS="-L${libdir} -llinphone"
-
-if test x$mingw_found = xyes ; then
- LINPHONE_LIBS="$LINPHONE_LIBS $OSIP_LIBS"
-fi
-AC_SUBST(LINPHONE_CFLAGS)
-AC_SUBST(LINPHONE_LIBS)
-
-AC_DEFINE_UNQUOTED(LINPHONE_VERSION, "$PACKAGE_VERSION", [Linphone\'s version number])
-
-AC_DEFINE_UNQUOTED(LINPHONE_PLUGINS_DIR, "${package_prefix}/lib/liblinphone/plugins" ,[path of liblinphone plugins, not mediastreamer2 plugins])
-LINPHONE_PLUGINS_DIR="${package_prefix}/lib/liblinphone/plugins"
-AC_SUBST(LINPHONE_PLUGINS_DIR)
-
AC_ARG_ENABLE(external-ortp,
[AS_HELP_STRING([--enable-external-ortp], [Use external oRTP library])],
[case "${enableval}" in
@@ -701,7 +812,7 @@ AC_ARG_ENABLE(external-ortp,
)
if test "$external_ortp" = 'true'; then
- PKG_CHECK_MODULES([ORTP], [ortp])
+ PKG_CHECK_MODULES([ORTP], [ortp >= 0.24.0])
ORTP_VERSION=`$PKG_CONFIG --modversion ortp`
else
AC_CONFIG_SUBDIRS( oRTP )
@@ -723,38 +834,209 @@ AC_SUBST(ORTP_LIBS)
AC_SUBST([ORTP_VERSION])
AC_SUBST([ORTP_DIR])
-AC_ARG_ENABLE(tests_enabled,
+AC_ARG_ENABLE([external-mediastreamer],
+ [AS_HELP_STRING([--enable-external-mediastreamer],[Use external mediastreamer library])],,
+ [enable_external_mediastreamer=no]
+)
+
+AS_CASE($enable_external_mediastreamer,
+ [yes],
+ [PKG_CHECK_MODULES([MEDIASTREAMER], [mediastreamer >= 2.11.0])
+ MS2_VERSION=`$PKG_CONFIG --modversion mediastreamer`],
+ [no],
+ [AC_CONFIG_SUBDIRS( mediastreamer2 )
+ MEDIASTREAMER_DIR=${top_srcdir}/mediastreamer2
+ MEDIASTREAMER_CFLAGS="-I\$(top_srcdir)/mediastreamer2/include"
+ MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer_base.la \$(top_builddir)/mediastreamer2/src/libmediastreamer_voip.la"
+dnl need to temporary change quotes to allow square brackets
+ changequote(<<, >>)
+ MS2_VERSION=`grep -e '^.C_INIT(' $MEDIASTREAMER_DIR/configure.ac | sed -e 's:\([^(]\+\)(\[mediastreamer\],\[\(.*\)\]):\2:g'`
+ changequote([, ])
+ MS2_DIR=mediastreamer2],
+ [AC_MSG_ERROR([bad value '${enable_external_mediastreamer}' for --enable-external-mediastreamer])]
+)
+
+AC_SUBST(MEDIASTREAMER_CFLAGS)
+AC_SUBST(MEDIASTREAMER_LIBS)
+AC_SUBST([MS2_VERSION])
+AC_SUBST([MS2_DIR])
+
+
+AC_ARG_ENABLE(tunnel,
+ [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])],
+ [case "${enableval}" in
+ yes) enable_tunnel=true ;;
+ no) enable_tunnel=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-tunnel) ;;
+ esac],
+ [enable_tunnel=false]
+)
+AM_CONDITIONAL(BUILD_TUNNEL, test x$enable_tunnel = xtrue)
+if test x$enable_tunnel = xtrue; then
+ PKG_CHECK_MODULES(TUNNEL, tunnel >= 0.3.3)
+ 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)])],
+ [case "${enableval}" in
+ yes) enable_msg_storage=true ;;
+ no) enable_msg_storage=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-msg-storage) ;;
+ esac],
+ [enable_msg_storage=auto]
+)
+
+if test x$enable_msg_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"
+ if test "$build_macos" = "yes" -o "$ios_found" = "yes"; then
+ SQLITE3_LIBS+=" -liconv"
+ fi
+ enable_msg_storage=true
+ else
+ if test x$enable_msg_storage = xtrue; then
+ AC_MSG_ERROR([sqlite3, required for message storage, not found])
+ fi
+ enable_msg_storage=false
+ fi
+
+ AC_SUBST(SQLITE3_CFLAGS)
+ AC_SUBST(SQLITE3_LIBS)
+fi
+
+AM_CONDITIONAL(BUILD_MSG_STORAGE, test x$enable_msg_storage = xtrue)
+
+PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.4.0])
+
+SIPSTACK_CFLAGS="$BELLESIP_CFLAGS"
+SIPSTACK_LIBS="$BELLESIP_LIBS"
+
+
+AC_SUBST(SIPSTACK_CFLAGS)
+AC_SUBST(SIPSTACK_LIBS)
+
+dnl check for db2html (docbook) to generate html user manual
+AC_CHECK_PROG(have_sgmltools, sgmltools, yes, no)
+AM_CONDITIONAL(ENABLE_MANUAL, test x$have_sgmltools$build_manual = xyesyes )
+
+dnl for external use of linphone libs
+LINPHONE_CFLAGS="-I${includedir} -I${includedir}/linphone"
+LINPHONE_LIBS="-L${libdir} -llinphone"
+
+AC_SUBST(LINPHONE_CFLAGS)
+AC_SUBST(LINPHONE_LIBS)
+
+AC_DEFINE_UNQUOTED(LINPHONE_VERSION, "$PACKAGE_VERSION", [Linphone\'s version number])
+
+AC_DEFINE_UNQUOTED(LINPHONE_PLUGINS_DIR, "${package_prefix}/lib/liblinphone/plugins" ,[path of liblinphone plugins, not mediastreamer2 plugins])
+LINPHONE_PLUGINS_DIR="${package_prefix}/lib/liblinphone/plugins"
+AC_SUBST(LINPHONE_PLUGINS_DIR)
+
+
+
+AC_ARG_ENABLE(tutorials,
+ [AS_HELP_STRING([--disable-tutorials], [Disable compilation of tutorials])],
+ [case "${enableval}" in
+ yes) tutorials_enabled=true ;;
+ no) tutorials_enabled=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-tutorials) ;;
+ esac],
+ [tutorials_enabled=yes]
+)
+AM_CONDITIONAL(ENABLE_TUTORIALS, test x$tutorials_enabled = xyes)
+
+AC_ARG_ENABLE(tests,
[AS_HELP_STRING([--disable-tests], [Disable compilation of tests])],
[case "${enableval}" in
yes) tests_enabled=true ;;
no) tests_enabled=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;;
esac],
- [tests_enabled=false]
+ [tests_enabled=yes]
)
AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes)
+PKG_CHECK_MODULES(CUNIT, cunit, [found_cunit=yes],[found_cunit=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"
+ ])
+
+ ])
+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 ##################################################
dnl # Check for doxygen
dnl ##################################################
-AC_PATH_PROG(DOXYGEN,doxygen,false)
-AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false)
+AC_ARG_ENABLE(documentation,
+ [AS_HELP_STRING([--enable-documentation], [Documentation generation using doxygen (default=yes)])],
+ [case "${enableval}" in
+ yes) documentation_enabled=yes;;
+ no) documentation_enabled=no;;
+ *) AC_MSG_ERROR("Bad value for --enable-documentation");;
+ esac],
+ [documentation_enabled=yes]
+)
+if test "$documentation_enabled" = "yes" ; then
+ AC_CHECK_PROG(DOXYGEN,doxygen,doxygen,false)
+else
+ DOXYGEN=false
+fi
+AM_CONDITIONAL(HAVE_DOXYGEN, test "$DOXYGEN" != "false")
-AC_CONFIG_FILES([
+AC_CONFIG_FILES([
Makefile
build/Makefile
build/macos/Makefile
build/macos/Info-linphone.plist
+ build/macos/pkg-distribution.xml
m4/Makefile
po/Makefile.in
pixmaps/Makefile
+ include/Makefile
coreapi/Makefile
coreapi/help/Makefile
coreapi/help/Doxyfile
+ tester/Makefile
gtk/Makefile
console/Makefile
share/Makefile
@@ -766,6 +1048,7 @@ AC_CONFIG_FILES([
share/xml/Makefile
share/linphone.pc
share/linphone.desktop
+ share/audio-assistant.desktop
scripts/Makefile
tools/Makefile
linphone.spec
@@ -780,12 +1063,17 @@ printf "* %-30s %s\n" "Video support" $video
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" "zRTP encryption (GPLv3)" $zrtp
+printf "* %-30s %s\n" "Tools" $build_tools
+printf "* %-30s %s\n" "Message storage" $enable_msg_storage
+printf "* %-30s %s\n" "IM encryption" $lime
printf "* %-30s %s\n" "uPnP support" $build_upnp
+printf "* %-30s %s\n" "LDAP support" $enable_ldap
+printf "* %-30s %s\n" "ZLIB support" $found_zlib
+printf "* %-30s %s\n" "Documentation" $documentation_enabled
if test "$enable_tunnel" = "true" ; then
- printf "* Tunnel support\t\ttrue\n"
+ printf "* %-30s %s\n" "Tunnel support" "true"
fi
+
echo "Now type 'make' to compile, and then 'make install' as root to install it."
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
new file mode 100644
index 000000000..328fdbcf2
--- /dev/null
+++ b/console/CMakeLists.txt
@@ -0,0 +1,53 @@
+############################################################################
+# CMakeLists.txt
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+
+set(LINPHONEC_SOURCE_FILES
+ linphonec.c
+ linphonec.h
+ commands.c
+)
+set(LINPHONECSH_SOURCE_FILES
+ shell.c
+)
+
+add_executable(linphonec ${LINPHONEC_SOURCE_FILES})
+target_link_libraries(linphonec linphone)
+
+if(WIN32)
+ add_executable(linphoned WIN32 ${LINPHONEC_SOURCE_FILES})
+ target_link_libraries(linphoned linphone)
+endif()
+
+add_executable(linphonecsh ${LINPHONECSH_SOURCE_FILES})
+target_link_libraries(linphonecsh linphone)
+
+set(INSTALL_TARGETS linphonec linphonecsh)
+if(WIN32)
+ list(APPEND INSTALL_TARGETS linphoned)
+endif()
+
+install(TARGETS ${INSTALL_TARGETS}
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+ 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 82ce998e5..482948329 100644
--- a/console/Makefile.am
+++ b/console/Makefile.am
@@ -3,18 +3,19 @@
AM_CPPFLAGS=\
-I$(top_srcdir) \
-I$(top_srcdir)/coreapi \
- -I$(top_srcdir)/exosip
+ -I$(top_srcdir)/include
COMMON_CFLAGS=\
-DIN_LINPHONE \
- -DENABLE_TRACE \
-D_ORTP_SOURCE \
$(STRICT_OPTIONS) \
+ $(STRICT_OPTIONS_CC) \
+ $(ORTP_CFLAGS) \
+ $(MEDIASTREAMER_CFLAGS) \
$(VIDEO_CFLAGS) \
$(READLINE_CFLAGS) \
- $(OSIP_CFLAGS) \
- $(ORTP_CFLAGS) \
- $(MEDIASTREAMER_CFLAGS)
+ $(SQLITE3_CFLAGS) \
+ $(LIBXML2_CFLAGS)
if BUILD_CONSOLE
@@ -25,10 +26,13 @@ bin_PROGRAMS+=linphoned
endif
linphonec_SOURCES=linphonec.c linphonec.h commands.c
-linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS)
+linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) $(BELLESIP_CFLAGS)
linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \
$(READLINE_LIBS) \
- $(X11_LIBS)
+ $(SQLITE3_LIBS) \
+ $(X11_LIBS) \
+ $(BELLESIP_LIBS) \
+ $(LIBXML2_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 22b09a5a6..f50ed4886 100644
--- a/console/commands.c
+++ b/console/commands.c
@@ -28,7 +28,6 @@
#include
#ifndef _WIN32_WCE
#include
-#include
#endif /*_WIN32_WCE*/
#include
#include
@@ -38,6 +37,7 @@
#ifndef WIN32
#include
+#include
#endif
#define AUDIO 0
@@ -45,7 +45,7 @@
/***************************************************************************
*
- * Forward declarations
+ * Forward declarations
*
***************************************************************************/
@@ -97,6 +97,7 @@ static int lpc_cmd_camera(LinphoneCore *lc, char *args);
static int lpc_cmd_video_window(LinphoneCore *lc, char *args);
static int lpc_cmd_preview_window(LinphoneCore *lc, char *args);
static int lpc_cmd_snapshot(LinphoneCore *lc, char *args);
+static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args);
static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg);
#endif
static int lpc_cmd_states(LinphoneCore *lc, char *args);
@@ -149,6 +150,15 @@ static LPC_COMMAND commands[] = {
"'help '\t: displays specific help for command.\n"
"'help advanced'\t: shows advanced commands.\n"
},
+ { "answer", lpc_cmd_answer, "Answer a call",
+ "'answer' : Answer the current incoming call\n"
+ "'answer ' : Answer the call with given id\n"
+ },
+ { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode",
+ "'autoanswer' \t: show current autoanswer mode\n"
+ "'autoanswer enable'\t: enable autoanswer mode\n"
+ "'autoanswer disable'\t: disable autoanswer mode\n"
+ },
{ "call", lpc_cmd_call, "Call a SIP uri or number",
#ifdef VIDEO_ENABLED
"'call [options]' \t: initiate a call to the specified destination.\n"
@@ -161,83 +171,24 @@ static LPC_COMMAND commands[] = {
},
{ "calls", lpc_cmd_calls, "Show all the current calls with their id and status.",
NULL
- },
+ },
+ { "call-logs", lpc_cmd_call_logs, "Calls history", NULL
+ },
+#ifdef VIDEO_ENABLED
+ { "camera", lpc_cmd_camera, "Send camera output for current call.",
+ "'camera on'\t: allow sending of local camera video to remote end.\n"
+ "'camera off'\t: disable sending of local camera's video to remote end.\n"
+ },
+#endif
{ "chat", lpc_cmd_chat, "Chat with a SIP uri",
"'chat \"message\"' "
": send a chat message \"message\" to the specified destination."
- },
- { "terminate", lpc_cmd_terminate, "Terminate a call",
- "'terminate' : Terminate the current call\n"
- "'terminate ' : Terminate the call with supplied id\n"
- "'terminate ' : Terminate all the current calls\n"
- },
- { "answer", lpc_cmd_answer, "Answer a call",
- "'answer' : Answer the current incoming call\n"
- "'answer ' : Answer the call with given id\n"
- },
- { "pause", lpc_cmd_pause, "pause a call",
- "'pause' : pause the current call\n"},
- { "resume", lpc_cmd_resume, "resume a call",
- "'resume' : resume the unique call\n"
- "'resume ' : hold off the call with given id\n"},
- { "transfer", lpc_cmd_transfer,
- "Transfer a call to a specified destination.",
- "'transfer ' : transfers the current active call to the destination sip-uri\n"
- "'transfer ': transfers the call with 'id' to the destination sip-uri\n"
- "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n"
},
{ "conference", lpc_cmd_conference, "Create and manage an audio conference.",
"'conference add : join the call with id 'call id' into the audio conference."
"'conference rm : remove the call with id 'call id' from the audio conference."
},
- { "mute", lpc_cmd_mute_mic,
- "Mute microphone and suspend voice transmission."},
-#ifdef VIDEO_ENABLED
- { "camera", lpc_cmd_camera, "Send camera output for current call.",
- "'camera on'\t: allow sending of local camera video to remote end.\n"
- "'camera off'\t: disable sending of local camera's video to remote end.\n"},
-#endif
- { "unmute", lpc_cmd_unmute_mic,
- "Unmute microphone and resume voice transmission."},
- { "playbackgain", lpc_cmd_playback_gain,
- "Adjust playback gain."},
- { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL },
-
- { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode",
- "'autoanswer' \t: show current autoanswer mode\n"
- "'autoanswer enable'\t: enable autoanswer mode\n"
- "'autoanswer disable'\t: disable autoanswer mode��\n"},
- { "proxy", lpc_cmd_proxy, "Manage proxies",
- "'proxy list' : list all proxy setups.\n"
- "'proxy add' : add a new proxy setup.\n"
- "'proxy remove ' : remove proxy setup with number index.\n"
- "'proxy use ' : use proxy with number index as default proxy.\n"
- "'proxy unuse' : don't use a default proxy.\n"
- "'proxy show ' : show configuration and status of the proxy numbered by index.\n"
- "'proxy show default' : show configuration and status of the default proxy.\n"
- },
- { "soundcard", lpc_cmd_soundcard, "Manage soundcards",
- "'soundcard list' : list all sound devices.\n"
- "'soundcard show' : show current sound devices configuration.\n"
- "'soundcard use ' : select a sound device.\n"
- "'soundcard use files' : use .wav files instead of soundcard\n"
- },
- { "webcam", lpc_cmd_webcam, "Manage webcams",
- "'webcam list' : list all known devices.\n"
- "'webcam use ' : select a video device.\n"
- },
- { "ipv6", lpc_cmd_ipv6, "Use IPV6",
- "'ipv6 status' : show ipv6 usage status.\n"
- "'ipv6 enable' : enable the use of the ipv6 network.\n"
- "'ipv6 disable' : do not use ipv6 network."
- },
- { "nat", lpc_cmd_nat, "Set nat address",
- "'nat' : show nat settings.\n"
- "'nat ' : set nat address.\n"
- },
- { "stun", lpc_cmd_stun, "Set stun server address",
- "'stun' : show stun settings.\n"
- "'stun ' : set stun server address.\n"
+ { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL
},
{ "firewall", lpc_cmd_firewall, "Set firewall policy",
"'firewall' : show current firewall policy.\n"
@@ -247,7 +198,6 @@ static LPC_COMMAND commands[] = {
"'firewall ice' : use ice.\n"
"'firewall upnp' : use uPnP IGD.\n"
},
- { "call-logs", lpc_cmd_call_logs, "Calls history", NULL },
{ "friend", lpc_cmd_friend, "Manage friends",
"'friend list []' : list friends.\n"
"'friend call ' : call a friend.\n"
@@ -256,18 +206,79 @@ static LPC_COMMAND commands[] = {
" there. Don't use '<' '>' around .\n"
"'friend delete ' : remove friend, 'all' removes all\n"
},
+ { "ipv6", lpc_cmd_ipv6, "Use IPV6",
+ "'ipv6 status' : show ipv6 usage status.\n"
+ "'ipv6 enable' : enable the use of the ipv6 network.\n"
+ "'ipv6 disable' : do not use ipv6 network."
+ },
+ { "mute", lpc_cmd_mute_mic,
+ "Mute microphone and suspend voice transmission."
+ },
+ { "nat", lpc_cmd_nat, "Set nat address",
+ "'nat' : show nat settings.\n"
+ "'nat ' : set nat address.\n"
+ },
+ { "pause", lpc_cmd_pause, "pause a call",
+ "'pause' : pause the current call\n"
+ },
{ "play", lpc_cmd_play, "play a wav file",
"This command has two roles:\n"
"Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n"
"Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n"
"'play ' : play a wav file."
},
+ { "playbackgain", lpc_cmd_playback_gain,
+ "Adjust playback gain."
+ },
+ { "proxy", lpc_cmd_proxy, "Manage proxies",
+ "'proxy list' : list all proxy setups.\n"
+ "'proxy add' : add a new proxy setup.\n"
+ "'proxy remove ' : remove proxy setup with number index.\n"
+ "'proxy use ' : use proxy with number index as default proxy.\n"
+ "'proxy unuse' : don't use a default proxy.\n"
+ "'proxy show ' : show configuration and status of the proxy numbered by index.\n"
+ "'proxy show default' : show configuration and status of the default proxy.\n"
+ },
{ "record", lpc_cmd_record, "record to a wav file",
"This feature is available only in file mode (see 'help soundcard')\n"
"'record ' : record into wav file."
},
- { "quit", lpc_cmd_quit, "Exit linphonec", NULL },
- { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL }
+ { "resume", lpc_cmd_resume, "resume a call",
+ "'resume' : resume the unique call\n"
+ "'resume ' : hold off the call with given id\n"
+ },
+ { "soundcard", lpc_cmd_soundcard, "Manage soundcards",
+ "'soundcard list' : list all sound devices.\n"
+ "'soundcard show' : show current sound devices configuration.\n"
+ "'soundcard use ' : select a sound device.\n"
+ "'soundcard use files' : use .wav files instead of soundcard\n"
+ },
+ { "stun", lpc_cmd_stun, "Set stun server address",
+ "'stun' : show stun settings.\n"
+ "'stun ' : set stun server address.\n"
+ },
+ { "terminate", lpc_cmd_terminate, "Terminate a call",
+ "'terminate' : Terminate the current call\n"
+ "'terminate ' : Terminate the call with supplied id\n"
+ "'terminate ' : Terminate all the current calls\n"
+ },
+ { "transfer", lpc_cmd_transfer,
+ "Transfer a call to a specified destination.",
+ "'transfer ' : transfers the current active call to the destination sip-uri\n"
+ "'transfer ': transfers the call with 'id' to the destination sip-uri\n"
+ "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n"
+ },
+ { "unmute", lpc_cmd_unmute_mic,
+ "Unmute microphone and resume voice transmission."
+ },
+ { "webcam", lpc_cmd_webcam, "Manage webcams",
+ "'webcam list' : list all known devices.\n"
+ "'webcam use ' : select a video device.\n"
+ },
+ { "quit", lpc_cmd_quit, "Exit linphonec", NULL
+ },
+ { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL
+ }
};
@@ -291,7 +302,8 @@ static LPC_COMMAND advanced_commands[] = {
{ "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute,
"Set the rtp_no_xmit_on_audio_mute configuration parameter",
" If set to 1 then rtp transmission will be muted when\n"
- " audio is muted , otherwise rtp is always sent."},
+ " audio is muted , otherwise rtp is always sent."
+ },
#ifdef VIDEO_ENABLED
{ "vwindow", lpc_cmd_video_window, "Control video display window",
"'vwindow show': shows video window\n"
@@ -312,20 +324,25 @@ static LPC_COMMAND advanced_commands[] = {
{ "snapshot", lpc_cmd_snapshot, "Take a snapshot of currently received video stream",
"'snapshot ': take a snapshot and records it in jpeg format into the supplied path\n"
},
- { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call"},
+ { "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"
+},
#endif
{ "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions",
"'states global': shows global state of liblinphone \n"
"'states calls': shows state of calls\n"
"'states proxies': shows state of proxy configurations"
},
- { "register", lpc_cmd_register, "Register in one line to a proxy" , "register "},
+ { "register", lpc_cmd_register, "Register in one line to a proxy" , "register "
+},
{ "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL },
- { "status", lpc_cmd_status, "Print various status information",
+ { "status", lpc_cmd_status, "Print various status information",
"'status register' \t: print status concerning registration\n"
"'status autoanswer'\t: tell whether autoanswer mode is enabled\n"
"'status hook' \t: print hook status\n" },
- { "ports", lpc_cmd_ports, "Network ports configuration",
+ { "ports", lpc_cmd_ports, "Network ports configuration",
"'ports' \t: prints current used ports.\n"
"'ports sip '\t: Sets the sip port.\n" },
{ "param", lpc_cmd_param, "parameter set or read as normally given in .linphonerc",
@@ -365,7 +382,7 @@ static LPC_COMMAND advanced_commands[] = {
/***************************************************************************
*
- * Public interface
+ * Public interface
*
***************************************************************************/
@@ -476,7 +493,7 @@ linphonec_command_generator(const char *text, int state)
/***************************************************************************
*
- * Command handlers
+ * Command handlers
*
***************************************************************************/
@@ -497,7 +514,7 @@ lpc_cmd_help(LinphoneCore *lc, char *arg)
commands[i].help);
i++;
}
-
+
linphonec_out("---------------------------\n");
linphonec_out("Type 'help ' for more details or\n");
linphonec_out(" 'help advanced' to list additional commands.\n");
@@ -511,17 +528,17 @@ lpc_cmd_help(LinphoneCore *lc, char *arg)
i=0;
while (advanced_commands[i].help)
{
- linphonec_out("%10.10s\t%s\n", advanced_commands[i].name,
+ linphonec_out("%20.20s\t%s\n", advanced_commands[i].name,
advanced_commands[i].help);
i++;
}
-
+
linphonec_out("---------------------------\n");
linphonec_out("Type 'help ' for more details.\n");
return 1;
}
-
+
cmd=lpc_find_command(arg);
if ( !cmd )
{
@@ -558,10 +575,12 @@ lpc_cmd_call(LinphoneCore *lc, char *args)
opt2=strstr(args,"--early-media");
if (opt1){
opt1[0]='\0';
+ while(--opt1 > args && opt1[0]==' ') opt1[0]='\0';
linphone_call_params_enable_video (cp,FALSE);
}
if (opt2){
opt2[0]='\0';
+ while(--opt2 > args && opt2[0]==' ') opt2[0]='\0';
linphone_call_params_enable_early_media_sending(cp,TRUE);
}
if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) )
@@ -577,7 +596,7 @@ lpc_cmd_call(LinphoneCore *lc, char *args)
return 1;
}
-static int
+static int
lpc_cmd_calls(LinphoneCore *lc, char *args){
const MSList *calls = linphone_core_get_calls(lc);
if(calls)
@@ -597,6 +616,7 @@ lpc_cmd_chat(LinphoneCore *lc, char *args)
char *arg1 = args;
char *arg2 = NULL;
char *ptr = args;
+ LinphoneChatRoom *cr;
if (!args) return 0;
@@ -613,10 +633,8 @@ lpc_cmd_chat(LinphoneCore *lc, char *args)
/* missing one parameter */
return 0;
}
- LinphoneChatRoom *cr = linphone_core_create_chat_room(lc,arg1);
+ cr = linphone_core_get_chat_room_from_uri(lc,arg1);
linphone_chat_room_send_message(cr,arg2);
- linphone_chat_room_destroy(cr);
-
return 1;
}
@@ -692,7 +710,7 @@ lpc_cmd_terminate(LinphoneCore *lc, char *args)
}
return 1;
}
-
+
if(strcmp(args,"all")==0){
linphonec_out("We are going to stop all the calls.\n");
linphone_core_terminate_all_calls(lc);
@@ -709,7 +727,7 @@ lpc_cmd_terminate(LinphoneCore *lc, char *args)
return 1;
}
return 0;
-
+
}
static int
@@ -852,7 +870,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args)
{
linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall);
}
- else if (strcmp(args,"upnp")==0)
+ else if (strcmp(args,"upnp")==0)
{
linphone_core_set_firewall_policy(lc,LinphonePolicyUseUpnp);
}
@@ -930,7 +948,7 @@ lpc_friend_name(char **args, char **name)
*args = ++end;
} else {
*name = strsep(args, " ");
-
+
if (NULL == *args) { /* Means there was no separator */
fprintf(stderr, "Either name or address is missing\n");
return 0;
@@ -960,7 +978,7 @@ lpc_cmd_friend(LinphoneCore *lc, char *args)
args+=4;
if ( ! *args ) return 0;
friend_num = strtol(args, NULL, 10);
-#ifndef _WIN32_WCE
+#ifndef _WIN32_WCE
if ( errno == ERANGE ) {
linphonec_out("Invalid friend number\n");
return 0;
@@ -978,11 +996,11 @@ lpc_cmd_friend(LinphoneCore *lc, char *args)
if (!strncmp(args, "all", 3))
{
friend_num = -1;
- }
+ }
else
{
friend_num = strtol(args, NULL, 10);
-#ifndef _WIN32_WCE
+#ifndef _WIN32_WCE
if ( errno == ERANGE ) {
linphonec_out("Invalid friend number\n");
return 0;
@@ -1022,7 +1040,7 @@ lpc_cmd_friend(LinphoneCore *lc, char *args)
linphonec_friend_add(lc, name, addr);
#else
LinphoneFriend *new_friend;
- new_friend = linphone_friend_new_with_addr(args);
+ new_friend = linphone_friend_new_with_address(args);
linphone_core_add_friend(lc, new_friend);
#endif
return 1;
@@ -1082,6 +1100,7 @@ lpc_cmd_proxy(LinphoneCore *lc, char *args)
}
else if (strcmp(arg1,"remove")==0)
{
+ if (arg2==NULL) return 0;
linphonec_proxy_remove(lc,atoi(arg2));
}
else if (strcmp(arg1,"use")==0)
@@ -1109,16 +1128,16 @@ lpc_cmd_proxy(LinphoneCore *lc, char *args)
{
if (strstr(arg2,"default"))
{
- proxynum=linphone_core_get_default_proxy(lc, NULL);
- if ( proxynum < 0 ) {
- linphonec_out("No default proxy defined\n");
- return 1;
- }
- linphonec_proxy_show(lc,proxynum);
+ proxynum=linphone_core_get_default_proxy(lc, NULL);
+ if ( proxynum < 0 ) {
+ linphonec_out("No default proxy defined\n");
+ return 1;
+ }
+ linphonec_proxy_show(lc,proxynum);
}
else
{
- linphonec_proxy_show(lc, atoi(arg2));
+ linphonec_proxy_show(lc, atoi(arg2));
}
}
else return 0; /* syntax error */
@@ -1410,7 +1429,7 @@ static int lpc_cmd_pause(LinphoneCore *lc, char *args){
}
static int lpc_cmd_resume(LinphoneCore *lc, char *args){
-
+
if(linphone_core_in_call(lc))
{
linphonec_out("There is already a call in process pause or stop it first");
@@ -1449,7 +1468,7 @@ static int lpc_cmd_resume(LinphoneCore *lc, char *args){
}
}
return 0;
-
+
}
static int lpc_cmd_conference(LinphoneCore *lc, char *args){
@@ -1513,7 +1532,7 @@ linphonec_proxy_add(LinphoneCore *lc)
continue;
}
- cfg=linphone_proxy_config_new();
+ cfg=linphone_core_create_proxy_config(lc);
if (linphone_proxy_config_set_server_addr(cfg,clean)<0)
{
linphonec_out("Invalid sip address (sip:sip.domain.tld).\n");
@@ -1595,7 +1614,7 @@ linphonec_proxy_add(LinphoneCore *lc)
*/
if ( enable_register==TRUE )
{
- long int expires=0;
+ int expires=0;
while (1)
{
char *input=linphonec_readline("Specify register expiration time"
@@ -1607,15 +1626,10 @@ linphonec_proxy_add(LinphoneCore *lc)
return;
}
- expires=strtol(input, (char **)NULL, 10);
- if ( expires == LONG_MIN || expires == LONG_MAX )
- {
- linphonec_out("Invalid value: %s\n", strerror(errno));
- free(input);
- continue;
- }
+ expires=atoi(input);
+ if (expires==0) expires=600;
- linphone_proxy_config_expires(cfg, expires);
+ linphone_proxy_config_set_expires(cfg, expires);
linphonec_out("Expiration: %d seconds\n", linphone_proxy_config_get_expires (cfg));
free(input);
@@ -1658,7 +1672,7 @@ linphonec_proxy_add(LinphoneCore *lc)
}
/*
- * Final confirmation
+ * Final confirmation
*/
while (1)
{
@@ -1741,12 +1755,12 @@ linphonec_proxy_list(LinphoneCore *lc)
const MSList *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++){
if (n==def)
linphonec_out("****** Proxy %i - this is the default one - *******\n",n);
- else
+ else
linphonec_out("****** Proxy %i *******\n",n);
linphonec_proxy_display((LinphoneProxyConfig*)proxies->data);
}
@@ -1788,7 +1802,7 @@ linphonec_friend_display(LinphoneFriend *fr)
{
LinphoneAddress *uri=linphone_address_clone(linphone_friend_get_address(fr));
char *str;
-
+
linphonec_out("name: %s\n", linphone_address_get_display_name(uri));
linphone_address_set_display_name(uri,NULL);
str=linphone_address_as_string(uri);
@@ -1852,7 +1866,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_addr(url);
+ newFriend = linphone_friend_new_with_address(url);
linphone_core_add_friend(lc, newFriend);
return 0;
}
@@ -1873,7 +1887,7 @@ linphonec_friend_delete(LinphoneCore *lc, int num)
}
}
- if (-1 == num)
+ if (-1 == num)
{
unsigned int i;
for (i = 0 ; i < n ; i++)
@@ -1899,26 +1913,26 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){
char passwd[512];
LinphoneProxyConfig *cfg;
const MSList *elem;
-
+
if (!args)
- {
- /* it means that you want to register the default proxy */
- LinphoneProxyConfig *cfg=NULL;
- linphone_core_get_default_proxy(lc,&cfg);
- if (cfg)
- {
- if(!linphone_proxy_config_is_registered(cfg)) {
+ {
+ /* it means that you want to register the default proxy */
+ LinphoneProxyConfig *cfg=NULL;
+ linphone_core_get_default_proxy(lc,&cfg);
+ if (cfg)
+ {
+ if(!linphone_proxy_config_is_registered(cfg)) {
linphone_proxy_config_enable_register(cfg,TRUE);
linphone_proxy_config_done(cfg);
}else{
linphonec_out("default proxy already registered\n");
}
- }else{
- linphonec_out("we do not have a default proxy\n");
- return 0;
- }
- return 1;
- }
+ }else{
+ linphonec_out("we do not have a default proxy\n");
+ return 0;
+ }
+ return 1;
+ }
passwd[0]=proxy[0]=identity[0]='\0';
sscanf(args,"%511s %511s %511s",identity,proxy,passwd);
if (proxy[0]=='\0' || identity[0]=='\0'){
@@ -1929,9 +1943,7 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){
LinphoneAddress *from;
LinphoneAuthInfo *info;
if ((from=linphone_address_new(identity))!=NULL){
- char realm[128];
- snprintf(realm,sizeof(realm)-1,"\"%s\"",linphone_address_get_domain(from));
- info=linphone_auth_info_new(linphone_address_get_username(from),NULL,passwd,NULL,NULL);
+ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,passwd,NULL,NULL,linphone_address_get_username(from));
linphone_core_add_auth_info(lc,info);
linphone_address_destroy(from);
linphone_auth_info_destroy(info);
@@ -1942,7 +1954,7 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){
cfg=(LinphoneProxyConfig*)elem->data;
linphone_proxy_config_edit(cfg);
}
- else cfg=linphone_proxy_config_new();
+ else cfg=linphone_core_create_proxy_config(lc);
linphone_proxy_config_set_identity(cfg,identity);
linphone_proxy_config_set_server_addr(cfg,proxy);
linphone_proxy_config_enable_register(cfg,TRUE);
@@ -1980,7 +1992,7 @@ static int lpc_cmd_duration(LinphoneCore *lc, char *args){
static int lpc_cmd_status(LinphoneCore *lc, char *args)
{
LinphoneProxyConfig *cfg;
-
+
if ( ! args ) return 0;
linphone_core_get_default_proxy(lc,&cfg);
if (strstr(args,"register"))
@@ -2030,7 +2042,7 @@ static int lpc_cmd_status(LinphoneCore *lc, char *args)
if (linphone_call_get_dir(call)==LinphoneCallOutgoing){
linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(),
linphone_core_get_current_call_duration(lc),
- linphone_core_is_mic_muted (lc) ? "yes" : "no",
+ linphone_core_mic_enabled(lc) ? "no" : "yes",
linphone_core_is_rtp_muted(lc) ? "yes" : "no");
}else{
linphonec_out("hook=answered duration=%i %s\n" ,
@@ -2043,7 +2055,7 @@ static int lpc_cmd_status(LinphoneCore *lc, char *args)
default:
break;
}
-
+
}
else return 0;
@@ -2102,15 +2114,25 @@ static int lpc_cmd_speak(LinphoneCore *lc, char *args){
char voice[64];
char *sentence;
char cl[128];
- char *wavfile;
+ char wavfile[128]="/tmp/linphonec-espeak-XXXXXX";
int status;
FILE *file;
-
+
if (!args) return 0;
memset(voice,0,sizeof(voice));
sscanf(args,"%63s",voice);
sentence=args+strlen(voice);
- wavfile=tempnam("/tmp/","linphonec-espeak-");
+
+#ifdef __APPLE__
+ mktemp(wavfile);
+#else
+ if (mkstemp(wavfile)==-1){
+ ms_error("Could not create temporary filename: %s", strerror(errno));
+ linphonec_out("An error occured, please consult logs for details.");
+ return 1;
+ }
+#endif
+
snprintf(cl,sizeof(cl),"espeak -v %s -s 100 -w %s --stdin",voice,wavfile);
file=popen(cl,"w");
if (file==NULL){
@@ -2195,7 +2217,7 @@ static void linphonec_codec_list(int type, LinphoneCore *lc){
for(;node!=NULL;node=ms_list_next(node)){
pt=(PayloadType*)(node->data);
- linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate,
+ linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate,
linphone_core_payload_type_enabled(lc,pt) ? "enabled" : "disabled");
index++;
}
@@ -2269,7 +2291,7 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){
if (arg2 != 0) {
n = sscanf(arg2, "%d %d %d", &delay, &tail_len, &frame_size);
- if (n == 1) {
+ if (n == 1) {
lp_config_set_int(config,"sound","ec_delay",delay);
}
else if (n == 2) {
@@ -2287,11 +2309,11 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){
linphone_core_enable_echo_cancellation(lc,0);
}
else if (strcmp(arg1,"show")==0){
- linphonec_out("echo cancellation is %s; delay %d, tail length %d, frame size %d\n",
+ linphonec_out("echo cancellation is %s; delay %d, tail length %d, frame size %d\n",
linphone_core_echo_cancellation_enabled(lc) ? "on" : "off",
lp_config_get_int(config,"sound","ec_delay",0),
lp_config_get_int(config,"sound","ec_tail_len",0),
- lp_config_get_int(config,"sound","ec_framesize",0));
+ lp_config_get_int(config,"sound","ec_framesize",0));
}
else {
return 0;
@@ -2341,7 +2363,7 @@ static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args)
if(strstr(args,"1"))rtp_xmit_off=TRUE;
if(linphone_core_get_current_call (lc)==NULL)
linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off);
- else
+ else
linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n");
}
rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc);
@@ -2354,12 +2376,12 @@ static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args)
#ifdef VIDEO_ENABLED
static int _lpc_cmd_video_window(LinphoneCore *lc, char *args, bool_t is_preview){
char subcommand[64];
- int a,b;
+ long a,b;
int err;
VideoParams *params=is_preview ? &lpc_preview_params : &lpc_video_params;
if (!args) return 0;
- err=sscanf(args,"%63s %i %i",subcommand,&a,&b);
+ err=sscanf(args,"%63s %ld %ld",subcommand,&a,&b);
if (err>=1){
if (strcmp(subcommand,"pos")==0){
if (err<3) return 0;
@@ -2381,15 +2403,15 @@ static int _lpc_cmd_video_window(LinphoneCore *lc, char *args, bool_t is_preview
if (is_preview) linphone_core_enable_video_preview (lc,FALSE);
}else if (strcmp(subcommand,"id")==0){
if (err == 1){
- linphonec_out("vwindow id: 0x%x\n",is_preview ? linphone_core_get_native_preview_window_id (lc) :
+ linphonec_out("vwindow id: 0x%p\n",is_preview ? linphone_core_get_native_preview_window_id (lc) :
linphone_core_get_native_video_window_id (lc));
return 1;
} else if (err != 2) return 0;
- params->wid=a;
+ params->wid=(void *)a;
if (is_preview)
- linphone_core_set_native_preview_window_id (lc,a);
+ linphone_core_set_native_preview_window_id(lc, (void *)a);
else
- linphone_core_set_native_video_window_id(lc,a);
+ linphone_core_set_native_video_window_id(lc, (void *)a);
}else if (is_preview==TRUE){
if (strcmp(subcommand,"integrated")==0){
linphone_core_use_preview_window (lc,FALSE);
@@ -2428,8 +2450,9 @@ static void lpc_display_call_states(LinphoneCore *lc){
}else{
for(;elem!=NULL;elem=elem->next){
const char *flag;
+ bool_t in_conference;
call=(LinphoneCall*)elem->data;
- bool_t in_conference=linphone_call_params_local_conference_mode(linphone_call_get_current_params(call));
+ in_conference=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
tmp=linphone_call_get_remote_address_as_string (call);
flag=in_conference ? "conferencing" : "";
flag=linphone_call_has_transfer_pending(call) ? "transfer pending" : flag;
@@ -2482,7 +2505,7 @@ static int lpc_cmd_states(LinphoneCore *lc, char *args){
static int lpc_cmd_camera(LinphoneCore *lc, char *args){
LinphoneCall *call=linphone_core_get_current_call(lc);
bool_t activated=FALSE;
-
+
if (linphone_core_video_enabled (lc)==FALSE){
linphonec_out("Video is disabled, re-run linphonec with -V option.");
return 1;
@@ -2510,13 +2533,15 @@ static int lpc_cmd_camera(LinphoneCore *lc, char *args){
const LinphoneCallParams *cp=linphone_call_get_current_params (call);
if (args){
linphone_call_enable_camera(call,activated);
- if ((activated && !linphone_call_params_video_enabled (cp))){
- /*update the call to add the video stream*/
- LinphoneCallParams *ncp=linphone_call_params_copy(cp);
- linphone_call_params_enable_video(ncp,TRUE);
- linphone_core_update_call(lc,call,ncp);
- linphone_call_params_destroy (ncp);
- linphonec_out("Trying to bring up video stream...\n");
+ if (linphone_call_get_state(call)==LinphoneCallStreamsRunning){
+ if ((activated && !linphone_call_params_video_enabled (cp))){
+ /*update the call to add the video stream*/
+ LinphoneCallParams *ncp=linphone_call_params_copy(cp);
+ linphone_call_params_enable_video(ncp,TRUE);
+ linphone_core_update_call(lc,call,ncp);
+ linphone_call_params_destroy (ncp);
+ linphonec_out("Trying to bring up video stream...\n");
+ }
}
}
if (linphone_call_camera_enabled (call))
@@ -2537,6 +2562,17 @@ static int lpc_cmd_snapshot(LinphoneCore *lc, char *args){
return 1;
}
+static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args){
+ LinphoneCall *call;
+ if (!args) return 0;
+ call=linphone_core_get_current_call(lc);
+ if (call!=NULL){
+ linphone_call_take_preview_snapshot(call,args);
+ linphonec_out("Taking video preview snapshot in file %s\n", args);
+ }else linphonec_out("There is no active call.\n");
+ return 1;
+}
+
static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg){
LinphoneCall *call;
call=linphone_core_get_current_call(lc);
diff --git a/console/linphonec.c b/console/linphonec.c
index fca5a9b86..cdf06f474 100644
--- a/console/linphonec.c
+++ b/console/linphonec.c
@@ -25,9 +25,7 @@
****************************************************************************/
#include
#ifndef _WIN32_WCE
-#include
#include
-#include
#include
#include
#include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */
@@ -48,32 +46,25 @@
#endif /*_WIN32_WCE*/
#else
#include
+#include
#include
#include
#include
+#include
#endif
-#if defined(_WIN32_WCE)
-
#if !defined(PATH_MAX)
#define PATH_MAX 256
#endif /*PATH_MAX*/
+#if defined(_WIN32_WCE)
+
#if !defined(strdup)
#define strdup _strdup
#endif /*strdup*/
#endif /*_WIN32_WCE*/
-#ifdef HAVE_GETTEXT
-#include
-#ifndef _
-#define _(String) gettext(String)
-#endif
-#else
-#define _(something) (something)
-#endif
-
#ifndef PACKAGE_DIR
#define PACKAGE_DIR ""
#endif
@@ -117,13 +108,12 @@ static char **linephonec_readline_completion(const char *text,
#endif
/* These are callback for linphone core */
-static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm,
- const char *username);
+static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain);
static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to);
static void linphonec_display_something (LinphoneCore * lc, const char *something);
static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url);
static void linphonec_display_warning (LinphoneCore * lc, const char *something);
-static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event);
+static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state);
static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid);
static void linphonec_new_unknown_subscriber(LinphoneCore *lc,
@@ -149,6 +139,7 @@ static char last_in_history[256];
#endif
//auto answer (-a) option
static bool_t auto_answer=FALSE;
+static bool_t real_early_media_sending=FALSE;
static bool_t answer_call=FALSE;
static bool_t vcap_enabled=FALSE;
static bool_t display_enabled=FALSE;
@@ -161,9 +152,10 @@ static int trace_level = 0;
static char *logfile_name = NULL;
static char configfile_name[PATH_MAX];
static char zrtpsecrets[PATH_MAX];
+static char usr_certificates_path[PATH_MAX];
static const char *factory_configfile_name=NULL;
static char *sip_addr_to_call = NULL; /* for autocall */
-static int window_id = 0; /* 0=standalone window, or window id for embedding video */
+static void *window_id = NULL; /* NULL=standalone window, or window id for embedding video */
#if !defined(_WIN32_WCE)
static ortp_pipe_t client_sock=ORTP_PIPE_INVALID;
#endif /*_WIN32_WCE*/
@@ -256,7 +248,7 @@ linphonec_display_url (LinphoneCore * lc, const char *something, const char *url
* Linphone core callback
*/
static void
-linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username)
+linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain)
{
/* no prompt possible when using pipes or tcp mode*/
if (unix_socket){
@@ -272,7 +264,7 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern
return;
}
- pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm);
+ pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm,domain);
auth_stack.elem[auth_stack.nitems++]=pending_auth;
}
}
@@ -281,13 +273,14 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern
* Linphone core callback
*/
static void
-linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event)
+linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state)
{
- if(!strcmp(event,"refer"))
- {
- linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n",
- from,(long)linphone_call_get_user_pointer (call));
+ char *remote=linphone_call_get_remote_address_as_string(call);
+ if (new_call_state==LinphoneCallConnected){
+ linphonec_out("The distant endpoint %s of call %li has been transfered, you can safely close the call.\n",
+ remote,(long)linphone_call_get_user_pointer (call));
}
+ ms_free(remote);
}
@@ -362,10 +355,17 @@ static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, L
linphone_call_enable_camera (call,linphonec_camera_enabled);
id=(long)linphone_call_get_user_pointer (call);
linphonec_set_caller(from);
+ linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id);
if ( auto_answer) {
answer_call=TRUE;
+ } else if (real_early_media_sending) {
+ LinphoneCallParams* callparams = linphone_core_create_default_call_parameters(lc);
+ linphonec_out("Sending early media using real hardware\n");
+ linphone_call_params_enable_early_media_sending(callparams, TRUE);
+ if (vcap_enabled) linphone_call_params_enable_video(callparams, TRUE);
+ linphone_core_accept_early_media_with_params(lc, call, callparams);
+ linphone_call_params_destroy(callparams);
}
- linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id);
break;
case LinphoneCallOutgoingInit:
linphonec_call_identify(call);
@@ -492,7 +492,6 @@ static void *pipe_thread(void*p){
}
static void start_pipe_reader(void){
- ms_mutex_init(&prompt_mutex,NULL);
pipe_reader_run=TRUE;
ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL);
}
@@ -528,6 +527,7 @@ char *linphonec_readline(char *prompt){
fprintf(stdout,"%s",prompt);
fflush(stdout);
while(1){
+
ms_mutex_lock(&prompt_mutex);
if (have_prompt){
char *ret=strdup(received_prompt);
@@ -538,15 +538,17 @@ char *linphonec_readline(char *prompt){
ms_mutex_unlock(&prompt_mutex);
linphonec_idle_call();
#ifdef WIN32
- Sleep(20);
- /* Following is to get the video window going as it
- should. Maybe should we only have this on when the option -V
- or -D is on? */
- MSG msg;
-
- if (PeekMessage(&msg, NULL, 0, 0,1)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
+ {
+ MSG msg;
+ Sleep(20);
+ /* Following is to get the video window going as it
+ should. Maybe should we only have this on when the option -V
+ or -D is on? */
+
+ if (PeekMessage(&msg, NULL, 0, 0,1)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
}
#else
usleep(20000);
@@ -628,8 +630,8 @@ int
main (int argc, char *argv[]) {
#endif
linphonec_vtable.call_state_changed=linphonec_call_state_changed;
- linphonec_vtable.notify_presence_recv = linphonec_notify_presence_received;
- linphonec_vtable.new_subscription_request = linphonec_new_unknown_subscriber;
+ linphonec_vtable.notify_presence_received = linphonec_notify_presence_received;
+ linphonec_vtable.new_subscription_requested = linphonec_new_unknown_subscriber;
linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth;
linphonec_vtable.display_status = linphonec_display_status;
linphonec_vtable.display_message=linphonec_display_something;
@@ -638,9 +640,9 @@ main (int argc, char *argv[]) {
linphonec_vtable.text_received=linphonec_text_received;
linphonec_vtable.dtmf_received=linphonec_dtmf_received;
linphonec_vtable.refer_received=linphonec_display_refer;
- linphonec_vtable.notify_recv=linphonec_notify_received;
+ linphonec_vtable.transfer_state_changed=linphonec_transfer_state_changed;
linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed;
-
+
if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
linphonec_main_loop (linphonec);
@@ -650,6 +652,12 @@ main (int argc, char *argv[]) {
exit(EXIT_SUCCESS); /* should never reach here */
}
+#ifdef _MSC_VER
+int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
+ return main(__argc, __argv);
+}
+#endif
+
/*
* Initialize linphonec
*/
@@ -663,13 +671,15 @@ linphonec_init(int argc, char **argv)
* Set initial values for global variables
*/
mylogfile = NULL;
-
-
+
+
#ifndef _WIN32
snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
getenv("HOME"));
snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
getenv("HOME"));
+ snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt",
+ getenv("HOME"));
#elif defined(_WIN32_WCE)
strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX);
mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w");
@@ -679,6 +689,8 @@ linphonec_init(int argc, char **argv)
getenv("APPDATA"));
snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
getenv("APPDATA"));
+ snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt",
+ getenv("APPDATA"));
#endif
/* Handle configuration filename changes */
switch (handle_configfile_migration())
@@ -694,17 +706,6 @@ linphonec_init(int argc, char **argv)
break;
}
-#ifdef ENABLE_NLS
- if (NULL == bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR))
- perror ("bindtextdomain failed");
-#ifndef __ARM__
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
- textdomain (GETTEXT_PACKAGE);
-#else
- printf ("NLS disabled.\n");
-#endif
-
linphonec_parse_cmdline(argc, argv);
if (trace_level > 0)
@@ -733,11 +734,15 @@ linphonec_init(int argc, char **argv)
* Initialize linphone core
*/
linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
+
+ linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION);
linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
- linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
- if (display_enabled && window_id != 0)
+ linphone_core_set_user_certificates_path(linphonec,usr_certificates_path);
+ linphone_core_enable_video_capture(linphonec, vcap_enabled);
+ linphone_core_enable_video_display(linphonec, display_enabled);
+ if (display_enabled && (window_id != NULL))
{
- printf ("Setting window_id: 0x%x\n", window_id);
+ printf("Setting window_id: 0x%p\n", window_id);
linphone_core_set_native_video_window_id(linphonec,window_id);
}
@@ -773,7 +778,7 @@ linphonec_finish(int exit_status)
{
// Do not allow concurrent destroying to prevent glibc errors
static bool_t terminating=FALSE;
- if (terminating) return;
+ if (terminating) return;
terminating=TRUE;
linphonec_out("Terminating...\n");
@@ -792,6 +797,7 @@ linphonec_finish(int exit_status)
if (mylogfile != NULL && mylogfile != stdout)
{
fclose (mylogfile);
+ mylogfile=stdout;
}
printf("\n");
exit(exit_status);
@@ -818,12 +824,13 @@ linphonec_prompt_for_auth_final(LinphoneCore *lc)
#ifdef HAVE_READLINE
rl_hook_func_t *old_event_hook;
#endif
+ LinphoneAuthInfo *pending_auth;
if (reentrancy!=0) return 0;
-
+
reentrancy++;
-
- LinphoneAuthInfo *pending_auth=auth_stack.elem[auth_stack.nitems-1];
+
+ pending_auth=auth_stack.elem[auth_stack.nitems-1];
snprintf(auth_prompt, 256, "Password for %s on %s: ",
pending_auth->username, pending_auth->realm);
@@ -905,12 +912,14 @@ print_usage (int exit_status)
" -l logfile specify the log file for your SIP phone\n"
" -s sipaddress specify the sip call to do at startup\n"
" -a enable auto answering for incoming calls\n"
+" --real-early-media enable sending early media using real audio/video (beware of privacy issue)\n"
" -V enable video features globally (disabled by default)\n"
" -C enable video capture only (disabled by default)\n"
" -D enable video display only (disabled by default)\n"
" -S show general state messages (disabled by default)\n"
" --wid windowid force embedding of video window into provided windowid (disabled by default)\n"
-" -v or --version display version and exits.\n");
+" -v or --version display version and exits.\n"
+);
exit(exit_status);
}
@@ -957,28 +966,29 @@ static void x11_apply_video_params(VideoParams *params, Window window){
static void lpc_apply_video_params(){
- static unsigned long old_wid=0,old_pwid=0;
- unsigned long wid=linphone_core_get_native_video_window_id (linphonec);
- unsigned long pwid=linphone_core_get_native_preview_window_id (linphonec);
+ static void *old_wid=NULL;
+ static void *old_pwid=NULL;
+ void *wid=linphone_core_get_native_video_window_id(linphonec);
+ void *pwid=linphone_core_get_native_preview_window_id(linphonec);
- if (wid!=0 && (lpc_video_params.refresh || old_wid!=wid)){
+ if (wid!=NULL && (lpc_video_params.refresh || old_wid!=wid)){
lpc_video_params.refresh=FALSE;
#ifdef HAVE_X11_XLIB_H
if (lpc_video_params.wid==0){ // do not manage window if embedded
- x11_apply_video_params(&lpc_video_params,wid);
+ x11_apply_video_params(&lpc_video_params,(Window)wid);
} else {
linphone_core_show_video(linphonec, lpc_video_params.show);
}
#endif
}
old_wid=wid;
- if (pwid!=0 && (lpc_preview_params.refresh || old_pwid!=pwid)){
+ if (pwid!=NULL && (lpc_preview_params.refresh || old_pwid!=pwid)){
lpc_preview_params.refresh=FALSE;
#ifdef HAVE_X11_XLIB_H
- /*printf("wid=%lu pwid=%lu\n",wid,pwid);*/
- if (lpc_preview_params.wid==0){ // do not manage window if embedded
+ /*printf("wid=%p pwid=%p\n",wid,pwid);*/
+ if (lpc_preview_params.wid==NULL){ // do not manage window if embedded
printf("Refreshing\n");
- x11_apply_video_params(&lpc_preview_params,pwid);
+ x11_apply_video_params(&lpc_preview_params,(Window)pwid);
}
#endif
}
@@ -1074,7 +1084,7 @@ linphonec_initialize_readline()
rl_attempted_completion_function = linephonec_readline_completion;
/* printf("Readline initialized.\n"); */
- setlinebuf(stdout);
+ setlinebuf(stdout);
return 0;
}
@@ -1149,7 +1159,6 @@ linphonec_main_loop (LinphoneCore * opm)
add_history(iptr);
}
#endif
-
linphonec_parse_command_line(linphonec, iptr);
linphonec_command_finished();
free(input);
@@ -1193,15 +1202,21 @@ linphonec_parse_cmdline(int argc, char **argv)
else if (strncmp ("-c", argv[arg_num], 2) == 0)
{
if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
+#ifdef _MSC_VER
+ if (strcmp(argv[arg_num], "NUL") != 0) {
+#endif
#if !defined(_WIN32_WCE)
- if (access(argv[arg_num],F_OK)!=0 )
- {
- fprintf (stderr,
- "Cannot open config file %s.\n",
- argv[arg_num]);
- exit(EXIT_FAILURE);
- }
+ if (access(argv[arg_num], F_OK) != 0)
+ {
+ fprintf(stderr,
+ "Cannot open config file %s.\n",
+ argv[arg_num]);
+ exit(EXIT_FAILURE);
+ }
#endif /*_WIN32_WCE*/
+#ifdef _MSC_VER
+ }
+#endif
snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]);
}
else if (strncmp ("-b", argv[arg_num], 2) == 0)
@@ -1228,6 +1243,10 @@ linphonec_parse_cmdline(int argc, char **argv)
{
auto_answer = TRUE;
}
+ else if (strncmp ("--real-early-media", argv[arg_num], strlen("--real-early-media")) == 0)
+ {
+ real_early_media_sending = TRUE;
+ }
else if (strncmp ("-C", argv[arg_num], 2) == 0)
{
vcap_enabled = TRUE;
@@ -1266,7 +1285,7 @@ linphonec_parse_cmdline(int argc, char **argv)
arg_num++;
if (arg_num < argc) {
char *tmp;
- window_id = strtol( argv[arg_num], &tmp, 0 );
+ window_id = (void *)strtol( argv[arg_num], &tmp, 0 );
lpc_video_params.wid = window_id;
}
}
@@ -1305,11 +1324,7 @@ handle_configfile_migration()
char *old_cfg_gui;
char *old_cfg_cli;
char *new_cfg;
-#if !defined(_WIN32_WCE)
const char *home = getenv("HOME");
-#else
- const char *home = ".";
-#endif /*_WIN32_WCE*/
new_cfg = ms_strdup_printf("%s/.linphonerc", home);
/*
@@ -1400,6 +1415,7 @@ copy_file(const char *from, const char *to)
snprintf(message, 255, "Can't open %s for writing: %s\n",
to, strerror(errno));
fprintf(stderr, "%s", message);
+ fclose(in);
return 0;
}
@@ -1408,6 +1424,8 @@ copy_file(const char *from, const char *to)
{
if ( ! fwrite(buf, 1, n, out) )
{
+ fclose(in);
+ fclose(out);
return 0;
}
}
diff --git a/console/linphonec.h b/console/linphonec.h
index 3265d42ac..23c0ee5a0 100644
--- a/console/linphonec.h
+++ b/console/linphonec.h
@@ -99,7 +99,7 @@ typedef struct {
typedef struct {
int x,y,w,h;
- unsigned long wid;
+ void *wid;
bool_t show;
bool_t refresh;
} VideoParams;
diff --git a/console/shell.c b/console/shell.c
index e61a6e514..7fbccb4c3 100644
--- a/console/shell.c
+++ b/console/shell.c
@@ -22,12 +22,13 @@
#include
#include
-
+#include
+#include
#ifdef WIN32
+#include
#include
#include
-#include
#include
#include
#else
@@ -47,6 +48,7 @@
#define STATUS_AUTOANSWER (1<<3)
#define STATUS_IN_CONNECTED (1<<4) /* incoming call accepted */
#define STATUS_OUT_CONNECTED (1<<5) /*outgoing call accepted */
+#define STATUS_IN_COMING (1<<6) /*incoming call pending */
static int make_status_value(const char *status_string){
@@ -69,6 +71,9 @@ static int make_status_value(const char *status_string){
if (strstr(status_string,"hook=answered")){
ret|=STATUS_IN_CONNECTED;
}
+ if (strstr(status_string,"Incoming call from ")){
+ ret|=STATUS_IN_COMING;
+ }
return ret;
}
@@ -125,11 +130,36 @@ static void print_usage(void){
exit(-1);
}
+#ifdef WIN32
+static char *argv_to_line(int argc, char *argv[]) {
+ int i;
+ int line_length;
+ char *line;
+
+ assert( argc>=0 );
+
+ if(argc == 0) return NULL;
+
+ line_length = strlen(argv[0]);
+ for(i=1; i $(GITVERSION_FILE_TMP) ; \
- elif test "$(GITREVISION)" != "" ; then \
- $(ECHO) -n "#define LIBLINPHONE_GIT_VERSION \"$(LINPHONE_VERSION)_$(GITREVISION)\"" > $(GITVERSION_FILE_TMP) ; \
- else \
- $(ECHO) -n "" > $(GITVERSION_FILE_TMP) ; \
+ if test -n "$(GITLOG)" ; then \
+ if test "$(GITDESCRIBE)" != "" ; then \
+ if [ "$(GIT_TAG)" != "$(PACKAGE_VERSION)" ]; then \
+ $(ECHO) "*** PACKAGE_VERSION and git tag differ. Please put them identical."; \
+ exit 1; \
+ fi ; \
+ printf "#define LIBLINPHONE_GIT_VERSION \"$(GITDESCRIBE)\"\n" > $(builddir)/$(GITVERSION_FILE_TMP) ; \
+ elif test "$(GITREVISION)" != "" ; then \
+ printf "#define LIBLINPHONE_GIT_VERSION \"$(LINPHONE_VERSION)_$(GITREVISION)\"\n" > $(builddir)/$(GITVERSION_FILE_TMP) ; \
+ else \
+ printf "" > $(builddir)/$(GITVERSION_FILE_TMP) ; \
+ fi ; \
+ if ! test -f $(builddir)/$(GITVERSION_FILE) ; then \
+ cp -f $(builddir)/$(GITVERSION_FILE_TMP) $(builddir)/$(GITVERSION_FILE) ; \
+ fi ; \
+ if test "`cat $(builddir)/$(GITVERSION_FILE_TMP)`" != "`cat $(builddir)/$(GITVERSION_FILE)`" ; then \
+ cp -f $(builddir)/$(GITVERSION_FILE_TMP) $(builddir)/$(GITVERSION_FILE) ; \
+ fi ; \
+ rm -f $(builddir)/$(GITVERSION_FILE_TMP) ; \
fi
- if test ! -f $(srcdir)/$(GITVERSION_FILE) ; then \
- cp -f $(GITVERSION_FILE_TMP) $(srcdir)/$(GITVERSION_FILE) ; \
- fi
- if test "`cat $(GITVERSION_FILE_TMP)`" != "`cat $(srcdir)/$(GITVERSION_FILE)`" ; then \
- cp -f $(GITVERSION_FILE_TMP) $(srcdir)/$(GITVERSION_FILE) ; \
- fi
- rm -f $(GITVERSION_FILE_TMP) ;
$(GITVERSION_FILE): make_gitversion_h
diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc
index 4828bf114..128707a12 100644
--- a/coreapi/TunnelManager.cc
+++ b/coreapi/TunnelManager.cc
@@ -1,7 +1,7 @@
/*
* C Implementation: tunnel
*
- * Description:
+ * Description:
*
*
* Author: Simon Morlat , (C) 2009
@@ -16,92 +16,22 @@
#include "ortp/rtpsession.h"
#include "linphonecore.h"
#include "linphonecore_utils.h"
-#include "eXosip2/eXosip_transport_hook.h"
-#include "tunnel/udp_mirror.hh"
+#include "private.h"
#ifdef ANDROID
#include
#endif
+belledonnecomm::TunnelManager *bcTunnel(const LinphoneTunnel *tunnel);
using namespace belledonnecomm;
using namespace ::std;
-Mutex TunnelManager::sMutex;
-
-int TunnelManager::eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata){
- TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
-
- sMutex.lock();
- if (lTunnelMgr->mSipSocket==NULL){
- sMutex.unlock();
- return len;
- }
- lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen);
- sMutex.unlock();
- //ignore the error in all cases, retransmissions might be successful.
- return len;
-}
-
-int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen,void* userdata){
- TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
- int err;
- sMutex.lock();
- if (lTunnelMgr->mSipSocket==NULL){
- sMutex.unlock();
- return 0;//let ignore the error
- }
- err=lTunnelMgr->mSipSocket->recvfrom(buf,len,from,*fromlen);
- sMutex.unlock();
- return err;
-}
-
-int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata){
- struct timeval begin,cur;
- TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
- if (s1 && tv!=0 && tv->tv_sec){
- /*this is the select from udp.c, the one that is interesting to us*/
- NativeSocket udp_fd=(NativeSocket)eXosip_get_udp_socket();
- NativeSocket controlfd=(NativeSocket)eXosip_get_control_fd();
-
- FD_ZERO(s1);
- gettimeofday(&begin,NULL);
- do{
- struct timeval abit;
-
- abit.tv_sec=0;
- abit.tv_usec=20000;
- sMutex.lock();
- if (lTunnelMgr->mSipSocket!=NULL){
- if (lTunnelMgr->mSipSocket->hasData()) {
- sMutex.unlock();
- /* we make exosip believe that it has udp data to read*/
- FD_SET(udp_fd,s1);
- return 1;
- }
- }
- sMutex.unlock();
- gettimeofday(&cur,NULL);
- if (cur.tv_sec-begin.tv_sec>tv->tv_sec) {
- FD_SET(controlfd,s1);
- FD_SET(udp_fd,s1);
- return 0;
- }
- FD_ZERO(s1);
- FD_SET(controlfd,s1);
- if (select(max_fds,s1,s2,s3,&abit)==1) {
- return 1;
- }
- }while(1);
-
- }else{
- /*select called from other places, only the control fd is present */
- return select(max_fds,s1,s2,s3,tv);
- }
-}
-
-
void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) {
+ if (ip == NULL) {
+ ip = "";
+ ms_warning("Adding tunnel server with empty ip, it will not work!");
+ }
addServer(ip,port);
mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip,udpMirrorPort),delay));
}
@@ -119,7 +49,6 @@ void TunnelManager::cleanServers() {
mServerAddrs.clear();
UdpMirrorClientList::iterator it;
- mAutoDetectStarted=false;
for (it = mUdpMirrorClients.begin(); it != mUdpMirrorClients.end();) {
UdpMirrorClient& s=*it++;
s.stop();
@@ -133,11 +62,6 @@ void TunnelManager::reconnect(){
mTunnelClient->reconnect();
}
-void TunnelManager::setCallback(StateCallback cb, void *userdata) {
- mCallback=cb;
- mCallbackData=userdata;
-}
-
static void sCloseRtpTransport(RtpTransport *t, void *userData){
TunnelSocket *s=(TunnelSocket*)userData;
TunnelManager *manager=(TunnelManager*)s->getUserPointer();
@@ -145,13 +69,16 @@ static void sCloseRtpTransport(RtpTransport *t, void *userData){
}
void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){
mTunnelClient->closeSocket(s);
- ms_free(t);
}
static RtpTransport *sCreateRtpTransport(void* userData, int port){
return ((TunnelManager *) userData)->createRtpTransport(port);
}
+void sDestroyRtpTransport(RtpTransport *t){
+ ms_free(t);
+}
+
RtpTransport *TunnelManager::createRtpTransport(int port){
TunnelSocket *socket=mTunnelClient->createSocket(port);
socket->setUserPointer(this);
@@ -160,32 +87,27 @@ RtpTransport *TunnelManager::createRtpTransport(int port){
t->t_recvfrom=customRecvfrom;
t->t_sendto=customSendto;
t->t_close=sCloseRtpTransport;
+ t->t_destroy=sDestroyRtpTransport;
t->data=socket;
return t;
}
-void TunnelManager::start() {
- if (!mTunnelClient) {
- mTunnelClient = new TunnelClient();
- mTunnelClient->setCallback((StateCallback)tunnelCallback,this);
- 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());
+void TunnelManager::startClient() {
+ ms_message("TunnelManager: Starting tunnel client");
+ mTunnelClient = new TunnelClient(TRUE);
+ mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this);
+ 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();
-
- if (mSipSocket == NULL) mSipSocket =mTunnelClient->createSocket(5060);
+ sal_set_tunnel(mCore->sal, mTunnelClient);
}
-bool TunnelManager::isStarted() {
- return mTunnelClient != 0 && mTunnelClient->isStarted();
-}
-
-bool TunnelManager::isReady() const {
- return mTunnelClient && mTunnelClient->isReady();
+bool TunnelManager::isConnected() const {
+ return mTunnelClient != NULL && mTunnelClient->isReady();
}
int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen){
@@ -197,25 +119,23 @@ 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);
+ //to make ice happy
+ inet_aton(((TunnelManager*)((TunnelSocket*)t->data)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr);
if (err>0) return err;
return 0;
}
-
-TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
-,mCore(lc)
-,mSipSocket(NULL)
-,mCallback(NULL)
-,mEnabled(false)
-,mTunnelClient(NULL)
-,mAutoDetectStarted(false)
-,mHttpProxyPort(0){
-
- mExosipTransport.data=this;
- mExosipTransport.recvfrom=eXosipRecvfrom;
- mExosipTransport.sendto=eXosipSendto;
- mExosipTransport.select=eXosipSelect;
+TunnelManager::TunnelManager(LinphoneCore* lc) :
+ mCore(lc),
+ mMode(LinphoneTunnelModeDisable),
+ mState(disabled),
+ mTunnelizeSipPackets(true),
+ mTunnelClient(NULL),
+ mHttpProxyPort(0),
+ mVTable(NULL)
+{
linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this);
mTransportFactories.audio_rtcp_func=sCreateRtpTransport;
mTransportFactories.audio_rtcp_func_data=this;
@@ -225,108 +145,103 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
mTransportFactories.video_rtcp_func_data=this;
mTransportFactories.video_rtp_func=sCreateRtpTransport;
mTransportFactories.video_rtp_func_data=this;
+ mVTable = linphone_core_v_table_new();
+ mVTable->network_reachable = networkReachableCb;
+ linphone_core_add_listener(mCore, mVTable);
+ linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr);
}
TunnelManager::~TunnelManager(){
- stopClient();
+ for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) {
+ udpMirror->stop();
+ }
+ if(mTunnelClient) delete mTunnelClient;
+ linphone_core_remove_listener(mCore, mVTable);
+ linphone_core_v_table_destroy(mVTable);
}
-void TunnelManager::stopClient(){
- eXosip_transport_hook_register(NULL);
- if (mSipSocket != NULL){
- sMutex.lock();
- mTunnelClient->closeSocket(mSipSocket);
- mSipSocket = NULL;
- sMutex.unlock();
+void TunnelManager::doRegistration(){
+ LinphoneProxyConfig* lProxy;
+ linphone_core_get_default_proxy(mCore, &lProxy);
+ if (lProxy) {
+ ms_message("TunnelManager: New registration");
+ lProxy->commit = TRUE;
}
- if (mTunnelClient){
- delete mTunnelClient;
- mTunnelClient=NULL;
+}
+
+void TunnelManager::doUnregistration() {
+ LinphoneProxyConfig *lProxy;
+ linphone_core_get_default_proxy(mCore, &lProxy);
+ if(lProxy) {
+ _linphone_proxy_config_unregister(lProxy);
}
}
void TunnelManager::processTunnelEvent(const Event &ev){
- LinphoneProxyConfig* lProxy;
- linphone_core_get_default_proxy(mCore, &lProxy);
-
- if (mEnabled && mTunnelClient->isReady()){
- ms_message("Tunnel is up, registering now");
- linphone_core_set_firewall_policy(mCore,LinphonePolicyNoFirewall);
- linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories);
- eXosip_transport_hook_register(&mExosipTransport);
- //force transport to udp
- LCSipTransports lTransport;
-
- lTransport.udp_port=(0xDFFF&random())+1024;
- lTransport.tcp_port=0;
- lTransport.tls_port=0;
- lTransport.dtls_port=0;
-
- linphone_core_set_sip_transports(mCore, &lTransport);
- //register
- if (lProxy) {
- linphone_proxy_config_done(lProxy);
- }
- }else if (mEnabled && !mTunnelClient->isReady()){
- /* we got disconnected from the tunnel */
- if (lProxy && linphone_proxy_config_is_registered(lProxy)) {
- /*forces de-registration so that we register again when the tunnel is up*/
- linphone_proxy_config_edit(lProxy);
- linphone_core_iterate(mCore);
+ 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();
+ }
+
}
+ } else {
+ ms_error("TunnelManager: tunnel has been disconnected");
}
}
-void TunnelManager::waitUnRegistration(){
- LinphoneProxyConfig* lProxy;
- linphone_core_get_default_proxy(mCore, &lProxy);
- if (lProxy && linphone_proxy_config_get_state(lProxy)==LinphoneRegistrationOk) {
- int i=0;
- linphone_proxy_config_edit(lProxy);
- //make sure unregister is sent and authenticated
- do{
- linphone_core_iterate(mCore);
- ms_usleep(20000);
- if (i>100){
- ms_message("tunnel: timeout for unregistration expired, giving up");
- break;
- }
- i++;
- }while(linphone_proxy_config_get_state(lProxy)!=LinphoneRegistrationCleared);
- }
-}
-
-void TunnelManager::enable(bool isEnable) {
- ms_message("Turning tunnel [%s]",(isEnable?"on":"off"));
- if (isEnable && !mEnabled){
- mEnabled=true;
- //1 save transport and firewall policy
- linphone_core_get_sip_transports(mCore, &mRegularTransport);
- mPreviousFirewallPolicy=linphone_core_get_firewall_policy(mCore);
- //2 unregister
- waitUnRegistration();
- //3 insert tunnel
- start();
- }else if (!isEnable && mEnabled){
- //1 unregister
- waitUnRegistration();
-
- mEnabled=false;
- stopClient();
-
- linphone_core_set_rtp_transport_factories(mCore,NULL);
-
- eXosip_transport_hook_register(NULL);
- //Restore transport and firewall policy
- linphone_core_set_sip_transports(mCore, &mRegularTransport);
- linphone_core_set_firewall_policy(mCore, mPreviousFirewallPolicy);
- //register
- LinphoneProxyConfig* lProxy;
- linphone_core_get_default_proxy(mCore, &lProxy);
- if (lProxy) {
- linphone_proxy_config_done(lProxy);
+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) {
+ case LinphoneTunnelModeEnable:
+ if(mState == disabled) {
+ startClient();
+ mState = connecting;
+ mMode = mode;
+ } else {
+ ms_error("TunnelManager: could not change mode. Bad state");
}
-
+ 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");
+ }
+ break;
+ case LinphoneTunnelModeAuto:
+ if(mState == disabled || mState == ready) {
+ if(startAutoDetection()) {
+ mState = autodetecting;
+ mMode = mode;
+ }
+ } else {
+ ms_error("TunnelManager: could not change mode. Bad state");
+ }
+ break;
+ default:
+ ms_error("TunnelManager::setMode(): invalid mode (%d)", mode);
}
}
@@ -359,8 +274,12 @@ void TunnelManager::sOnIterate(TunnelManager *zis){
}
#ifdef ANDROID
-extern void linphone_android_log_handler(int prio, const char *fmt, va_list args);
+extern void linphone_android_log_handler(int prio, char *str);
static void linphone_android_tunnel_log_handler(int lev, const char *fmt, va_list args) {
+ char str[4096];
+ vsnprintf(str, sizeof(str) - 1, fmt, args);
+ str[sizeof(str) - 1] = '\0';
+
int prio;
switch(lev){
case TUNNEL_DEBUG: prio = ANDROID_LOG_DEBUG; break;
@@ -370,7 +289,7 @@ static void linphone_android_tunnel_log_handler(int lev, const char *fmt, va_lis
case TUNNEL_ERROR: prio = ANDROID_LOG_ERROR; break;
default: prio = ANDROID_LOG_DEFAULT; break;
}
- linphone_android_log_handler(prio, fmt, args);
+ linphone_android_log_handler(prio, str);
}
#endif /*ANDROID*/
@@ -383,7 +302,7 @@ void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) {
#ifdef ANDROID
else SetLogHandler(linphone_android_tunnel_log_handler);
#else
- else SetLogHandler(default_log_handler);
+ else SetLogHandler(tunnel_default_log_handler);
#endif
if (isEnabled) {
@@ -392,30 +311,39 @@ void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) {
SetLogLevel(TUNNEL_ERROR|TUNNEL_WARN);
}
}
-
-bool TunnelManager::isEnabled() {
- return mEnabled;
+
+LinphoneTunnelMode TunnelManager::getMode() const {
+ return mMode;
}
void TunnelManager::processUdpMirrorEvent(const Event &ev){
+ if(mState != autodetecting) return;
if (ev.mData.mHaveUdp) {
- LOGI("Tunnel is not required, disabling");
- enable(false);
- mAutoDetectStarted = false;
+ ms_message("TunnelManager: UDP mirror test succeed");
+ if(mTunnelClient) {
+ if(mTunnelizeSipPackets) doUnregistration();
+ delete mTunnelClient;
+ mTunnelClient = NULL;
+ if(mTunnelizeSipPackets) doRegistration();
+ }
+ mState = disabled;
} else {
+ ms_message("TunnelManager: UDP mirror test failed");
mCurrentUdpMirrorClient++;
if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) {
- // enable tunnel but also try backup server
- LOGI("Tunnel is required, enabling; Trying backup udp mirror");
-
+ ms_message("TunnelManager: trying another UDP mirror");
UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient;
lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this);
} else {
- LOGI("Tunnel is required, enabling; no backup udp mirror available");
- mAutoDetectStarted = false;
+ ms_message("TunnelManager: all UDP mirror tests failed");
+ if(mTunnelClient==NULL) {
+ startClient();
+ mState = connecting;
+ } else {
+ mState = ready;
+ }
}
- enable(true);
}
}
@@ -433,21 +361,37 @@ void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) {
thiz->postEvent(ev);
}
-void TunnelManager::autoDetect() {
- // first check if udp mirrors was provisionned
+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;
+ }
+ linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr);
+}
+
+bool TunnelManager::startAutoDetection() {
if (mUdpMirrorClients.empty()) {
- LOGE("No UDP mirror server configured aborting auto detection");
- return;
+ ms_error("TunnelManager: No UDP mirror server configured aborting auto detection");
+ return false;
}
- if (mAutoDetectStarted) {
- LOGE("auto detection already in progress, restarting");
- (*mCurrentUdpMirrorClient).stop();
- }
- mAutoDetectStarted=true;
- mCurrentUdpMirrorClient =mUdpMirrorClients.begin();
+ ms_message("TunnelManager: Starting auto-detection");
+ mCurrentUdpMirrorClient = mUdpMirrorClients.begin();
UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient;
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;
}
void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd) {
@@ -456,6 +400,14 @@ void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd
if (mTunnelClient) mTunnelClient->setHttpProxyAuthInfo(username,passwd);
}
+void TunnelManager::tunnelizeSipPackets(bool enable){
+ mTunnelizeSipPackets = enable;
+}
+
+bool TunnelManager::tunnelizeSipPacketsEnabled() const {
+ return mTunnelizeSipPackets;
+}
+
void TunnelManager::setHttpProxy(const char *host,int port, const char *username, const char *passwd){
mHttpUserName=username?username:"";
mHttpPasswd=passwd?passwd:"";
@@ -464,6 +416,6 @@ void TunnelManager::setHttpProxy(const char *host,int port, const char *username
if (mTunnelClient) mTunnelClient->setHttpProxy(host, port, username, passwd);
}
-LinphoneCore *TunnelManager::getLinphoneCore(){
+LinphoneCore *TunnelManager::getLinphoneCore() const{
return mCore;
}
diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh
index d4c1458fc..f8002ee5d 100644
--- a/coreapi/TunnelManager.hh
+++ b/coreapi/TunnelManager.hh
@@ -1,7 +1,7 @@
/*
* C Implementation: tunnel
*
- * Description:
+ * Description:
*
*
*
@@ -12,32 +12,35 @@
#define __TUNNEL_CLIENT_MANAGER_H__
#include
#include
-#include "tunnel/client.hh"
+#include
+#include
#include "linphonecore.h"
+#include "linphone_tunnel.h"
+#ifndef USE_BELLESIP
extern "C" {
- #include "eXosip2/eXosip_transport_hook.h"
+ #include
}
+#endif
+
namespace belledonnecomm {
-class TunnelClient;
-class UdpMirrorClient;
/**
- * @addtogroup tunnel_client
+ * @addtogroup tunnel_client
* @{
**/
/**
- * The TunnelManager class extends the LinphoneCore functionnality in order to provide an easy to use API to
+ * The TunnelManager class extends the LinphoneCore functionnality in order to provide an easy to use API to
* - provision tunnel servers ip addresses and ports
* - start/stop the tunneling service
- * - be informed of of connection and disconnection events to the tunnel server
+ * - be informed of connection and disconnection events to the tunnel server
* - perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets.
- *
+ *
* It takes in charge automatically the SIP registration procedure when connecting or disconnecting to a tunnel server.
* No other action on LinphoneCore is required to enable full operation in tunnel mode.
**/
- class TunnelManager : public TunnelClientController{
-
+ class TunnelManager {
+
public:
/**
* Add a tunnel server. At least one should be provided to be able to connect.
@@ -53,25 +56,13 @@ class UdpMirrorClient;
* @param ip tunnel server ip address
* @param port tunnel server tls port, recommended value is 443
* @param udpMirrorPort remote port on the tunnel server side used to test udp reachability
- * @param delay udp packet round trip delay in ms considered as acceptable. recommanded value is 1000 ms.
+ * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms.
*/
void addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay);
/**
* Removes all tunnel server address previously entered with addServer()
- **/
- void cleanServers();
- /**
- * Register a state callback to be notified whenever the tunnel client is connected or disconnected to the tunnel server.
- * @param cb application callback function to use for notifying of connection/disconnection events.
- * @param userdata An opaque pointer passed to the callback, used optionally by the application to retrieve a context.
- **/
- void setCallback(StateCallback cb, void *userdata);
- /**
- * Start connecting to a tunnel server.
- * At this step, nothing is tunneled yet. The enable() method must be used to state whether SIP and RTP traffic
- * need to be tunneled or not.
**/
- void start();
+ void cleanServers();
/**
* Forces reconnection to the tunnel server.
* This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket
@@ -80,22 +71,15 @@ class UdpMirrorClient;
**/
void reconnect();
/**
- * Sets whether tunneling of SIP and RTP is required.
- * @param isEnabled If true enter in tunneled mode, if false exits from tunneled mode.
- * The TunnelManager takes care of refreshing SIP registration when switching on or off the tunneled mode.
- *
- **/
- void enable(bool isEnabled);
- /**
- * In auto detect mode, the tunnel manager try to establish a real time rtp cummunication with the tunnel server on specified port.
- * In case of success, the tunnel is automatically turned off. Otherwise, if no udp commmunication is feasible, tunnel mode is turned on.
- * Call this method each time to run the auto detection algorithm
+ * @brief setMode
+ * @param mode
*/
- void autoDetect();
+ void setMode(LinphoneTunnelMode mode);
/**
- * Returns a boolean indicating whether tunneled operation is enabled.
- **/
- bool isEnabled();
+ * @brief Return the tunnel mode
+ * @return #LinphoneTunnelMode
+ */
+ LinphoneTunnelMode getMode() const;
/**
* Enables debug logs of the Tunnel subsystem.
**/
@@ -112,24 +96,61 @@ class UdpMirrorClient;
* @param passwd The password.
**/
void setHttpProxyAuthInfo(const char* username,const char* passwd);
- ~TunnelManager();
+ void setHttpProxy(const char *host,int port, const char *username, const char *passwd);
+ /**
+ * Indicate to the tunnel manager whether SIP packets must pass
+ * through the tunnel. That featurte is automatically enabled at
+ * the creation of the TunnelManager instance.
+ * @param enable If set to TRUE, SIP packets will pass through the tunnel.
+ * If set to FALSE, SIP packets will pass by the configured proxies.
+ */
+ void tunnelizeSipPackets(bool enable);
+ /**
+ * @brief Check whether the tunnel manager is set to tunnelize SIP packets
+ * @return True, SIP packets pass through the tunnel
+ */
+ bool tunnelizeSipPacketsEnabled() const;
+ /**
+ * @brief Constructor
+ * @param lc The LinphoneCore instance of which the TunnelManager will be associated to.
+ */
TunnelManager(LinphoneCore* lc);
/**
- * Destroy the given RtpTransport.
+ * @brief Destructor
*/
- void closeRtpTransport(RtpTransport *t, TunnelSocket *s);
-
+ ~TunnelManager();
/**
- * Create an RtpTransport.
+ * @brief Create an RtpTransport
+ * @param port
+ * @return
*/
RtpTransport *createRtpTransport(int port);
-
/**
- * Get associated Linphone Core.
+ * @brief Destroy the given RtpTransport
+ * @param t
+ * @param s
*/
- LinphoneCore *getLinphoneCore();
- virtual void setHttpProxy(const char *host,int port, const char *username, const char *passwd);
+ void closeRtpTransport(RtpTransport *t, TunnelSocket *s);
+ /**
+ * @brief Get associated Linphone Core
+ * @return pointer on the associated LinphoneCore
+ */
+ LinphoneCore *getLinphoneCore() const;
+ /**
+ * @brief Check wehter the tunnel is connected
+ * @return True whether the tunnel is connected
+ */
+ bool isConnected() const;
+
+ bool isActivated() const;
private:
+ enum State {
+ disabled,
+ connecting,
+ ready,
+ autodetecting
+ };
+
enum EventType{
UdpMirrorClientEvent,
TunnelEvent,
@@ -142,9 +163,6 @@ class UdpMirrorClient;
}mData;
};
typedef std::list UdpMirrorClientList;
- virtual bool isStarted();
- virtual bool isReady() const;
- void onIterate();
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);
@@ -153,32 +171,36 @@ class UdpMirrorClient;
static void tunnelCallback(bool connected, TunnelManager *zis);
static void sOnIterate(TunnelManager *zis);
static void sUdpMirrorClientCallback(bool result, void* data);
- void waitUnRegistration();
+ static void networkReachableCb(LinphoneCore *lc, bool_t reachable);
+
+ private:
+ void onIterate();
+ void doRegistration();
+ void doUnregistration();
+ void startClient();
+ bool startAutoDetection();
void processTunnelEvent(const Event &ev);
void processUdpMirrorEvent(const Event &ev);
void postEvent(const Event &ev);
+
+ private:
LinphoneCore* mCore;
- LCSipTransports mRegularTransport;
- TunnelSocket *mSipSocket;
- eXosip_transport_hooks_t mExosipTransport;
- StateCallback mCallback;
- void * mCallbackData;
- bool mEnabled;
- std::queue mEvq;
- std::list mServerAddrs;
- UdpMirrorClientList mUdpMirrorClients;
- UdpMirrorClientList::iterator mCurrentUdpMirrorClient;
+ LinphoneTunnelMode mMode;
+ State mState;
+ bool mTunnelizeSipPackets;
TunnelClient* mTunnelClient;
- void stopClient();
- Mutex mMutex;
- static Mutex sMutex;
- bool mAutoDetectStarted;
- LinphoneRtpTransportFactories mTransportFactories;
std::string mHttpUserName;
std::string mHttpPasswd;
std::string mHttpProxyHost;
int mHttpProxyPort;
- LinphoneFirewallPolicy mPreviousFirewallPolicy;
+ LinphoneCoreVTable *mVTable;
+ std::list mServerAddrs;
+ UdpMirrorClientList mUdpMirrorClients;
+ UdpMirrorClientList::iterator mCurrentUdpMirrorClient;
+ LinphoneRtpTransportFactories mTransportFactories;
+ Mutex mMutex;
+ std::queue mEvq;
+ char mLocalAddr[64];
};
/**
diff --git a/coreapi/account_creator.c b/coreapi/account_creator.c
new file mode 100644
index 000000000..23cdffb28
--- /dev/null
+++ b/coreapi/account_creator.c
@@ -0,0 +1,325 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "linphonecore.h"
+#include "private.h"
+
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorCbs);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorCbs, belle_sip_object_t,
+ NULL, // destroy
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
+
+static LinphoneAccountCreatorCbs * linphone_account_creator_cbs_new(void) {
+ return belle_sip_object_new(LinphoneAccountCreatorCbs);
+}
+
+LinphoneAccountCreatorCbs * linphone_account_creator_cbs_ref(LinphoneAccountCreatorCbs *cbs) {
+ belle_sip_object_ref(cbs);
+ return cbs;
+}
+
+void linphone_account_creator_cbs_unref(LinphoneAccountCreatorCbs *cbs) {
+ belle_sip_object_unref(cbs);
+}
+
+void *linphone_account_creator_cbs_get_user_data(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->user_data;
+}
+
+void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud) {
+ cbs->user_data = ud;
+}
+
+LinphoneAccountCreatorCbsExistenceTestedCb linphone_account_creator_cbs_get_existence_tested(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->existence_tested;
+}
+
+void linphone_account_creator_cbs_set_existence_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsExistenceTestedCb cb) {
+ cbs->existence_tested = cb;
+}
+
+LinphoneAccountCreatorCbsValidationTestedCb linphone_account_creator_cbs_get_validation_tested(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->validation_tested;
+}
+
+void linphone_account_creator_cbs_set_validation_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidationTestedCb cb) {
+ cbs->validation_tested = cb;
+}
+
+LinphoneAccountCreatorCbsValidatedCb linphone_account_creator_cbs_get_validated(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->validated;
+}
+
+void linphone_account_creator_cbs_set_validated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidatedCb cb) {
+ cbs->validated = cb;
+}
+
+
+static void _linphone_account_creator_destroy(LinphoneAccountCreator *creator) {
+ linphone_xml_rpc_session_unref(creator->xmlrpc_session);
+ linphone_account_creator_cbs_unref(creator->callbacks);
+ if (creator->username) ms_free(creator->username);
+ if (creator->password) ms_free(creator->password);
+ 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);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreator);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreator, belle_sip_object_t,
+ (belle_sip_object_destroy_t)_linphone_account_creator_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
+
+
+LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) {
+ LinphoneAccountCreator *creator;
+ creator = belle_sip_object_new(LinphoneAccountCreator);
+ creator->callbacks = linphone_account_creator_cbs_new();
+ creator->core = core;
+ creator->xmlrpc_session = linphone_xml_rpc_session_new(core, xmlrpc_url);
+ return creator;
+}
+
+LinphoneAccountCreator * linphone_account_creator_ref(LinphoneAccountCreator *creator) {
+ belle_sip_object_ref(creator);
+ return creator;
+}
+
+void linphone_account_creator_unref(LinphoneAccountCreator *creator) {
+ belle_sip_object_unref(creator);
+}
+
+void *linphone_account_creator_get_user_data(const LinphoneAccountCreator *creator) {
+ return creator->user_data;
+}
+
+void linphone_account_creator_set_user_data(LinphoneAccountCreator *creator, void *ud) {
+ creator->user_data = ud;
+}
+
+void linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username) {
+ set_string(&creator->username, username);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+ 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);
+ 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);
+ // If account created on sip.linphone.org, we configure linphone to use TLS by default
+ if (linphone_core_sip_transport_supported(creator->core, LinphoneTransportTls)) {
+ LinphoneAddress *addr = linphone_address_new(linphone_proxy_config_get_server_addr(cfg));
+ char *tmp;
+ linphone_address_set_transport(addr, LinphoneTransportTls);
+ tmp = linphone_address_as_string(addr);
+ linphone_proxy_config_set_server_addr(cfg, tmp);
+ linphone_proxy_config_set_route(cfg, tmp);
+ ms_free(tmp);
+ linphone_address_destroy(addr);
+ }
+ linphone_core_set_stun_server(creator->core, "stun.linphone.org");
+ 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));
+ linphone_core_add_auth_info(creator->core, info);
+ linphone_address_destroy(identity);
+
+ if (linphone_core_add_proxy_config(creator->core, cfg) != -1) {
+ linphone_core_set_default_proxy(creator->core, cfg);
+ return cfg;
+ }
+
+ linphone_core_remove_auth_info(creator->core, info);
+ linphone_proxy_config_unref(cfg);
+ return NULL;
+}
diff --git a/coreapi/account_creator.h b/coreapi/account_creator.h
new file mode 100644
index 000000000..f2231984a
--- /dev/null
+++ b/coreapi/account_creator.h
@@ -0,0 +1,320 @@
+/*
+account_creator.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef LINPHONE_ACCOUNT_CREATOR_H_
+#define LINPHONE_ACCOUNT_CREATOR_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @addtogroup misc
+ * @{
+ */
+
+/**
+* Enum describing the status of a LinphoneAccountCreator operation.
+**/
+typedef enum _LinphoneAccountCreatorStatus {
+ LinphoneAccountCreatorOk,
+ LinphoneAccountCreatorFailed
+} LinphoneAccountCreatorStatus;
+
+/**
+ * The LinphoneAccountCreator object used to create an account on a server via XML-RPC.
+**/
+typedef struct _LinphoneAccountCreator LinphoneAccountCreator;
+
+/**
+ * An object to handle the callbacks for handling the LinphoneAccountCreator operations.
+**/
+typedef struct _LinphoneAccountCreatorCbs LinphoneAccountCreatorCbs;
+
+/**
+ * Callback used to notify the end of a LinphoneAccountCreator test existence operation.
+ * @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);
+
+/**
+ * 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
+ * @return The new LinphoneAccountCreator object
+**/
+LINPHONE_PUBLIC LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url);
+
+/**
+ * Acquire a reference to the LinphoneAccountCreator.
+ * @param[in] creator LinphoneAccountCreator object.
+ * @return The same LinphoneAccountCreator object.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreator * linphone_account_creator_ref(LinphoneAccountCreator *creator);
+
+/**
+ * Release reference to the LinphoneAccountCreator.
+ * @param[in] creator LinphoneAccountCreator object.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_unref(LinphoneAccountCreator *creator);
+
+/**
+ * Retrieve the user pointer associated with the LinphoneAccountCreator.
+ * @param[in] creator LinphoneAccountCreator object.
+ * @return The user pointer associated with the LinphoneAccountCreator.
+**/
+LINPHONE_PUBLIC void *linphone_account_creator_get_user_data(const LinphoneAccountCreator *creator);
+
+/**
+ * Assign a user pointer to the LinphoneAccountCreator.
+ * @param[in] creator LinphoneAccountCreator object.
+ * @param[in] ud The user pointer to associate with the LinphoneAccountCreator.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_user_data(LinphoneAccountCreator *creator, void *ud);
+
+/**
+ * Set the username.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] username The username to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username);
+
+/**
+ * Get the username.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The username of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_username(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the password.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] password The password to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password);
+
+/**
+ * Get the password.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The password of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_password(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the domain.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] domain The domain to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain);
+
+/**
+ * Get the domain.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The domain of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_domain(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the route.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] route The route to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route);
+
+/**
+ * Get the route.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The route of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_route(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the email.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] display_name The display name to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name);
+
+/**
+ * Get the email.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The display name of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_display_name(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the email.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] email The email to set
+**/
+LINPHONE_PUBLIC void linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email);
+
+/**
+ * Get the email.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The email of the 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
+ * @return The LinphoneAccountCreatorCbs object associated with the LinphoneAccountCreator.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbs * linphone_account_creator_get_callbacks(const LinphoneAccountCreator *creator);
+
+/**
+ * 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
+**/
+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);
+
+/**
+ * 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
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_validate(LinphoneAccountCreator *creator);
+
+/**
+ * Configure an account (create a proxy config and authentication info for it).
+ * @param[in] creator LinphoneAccountCreator object
+ * @return A LinphoneProxyConfig object if successful, NULL otherwise
+**/
+LINPHONE_PUBLIC LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCreator *creator);
+
+
+/**
+ * Acquire a reference to a LinphoneAccountCreatorCbs object.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The same LinphoneAccountCreatorCbs object.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbs * linphone_account_creator_cbs_ref(LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Release a reference to a LinphoneAccountCreatorCbs object.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_unref(LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Retrieve the user pointer associated with a LinphoneAccountCreatorCbs object.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The user pointer associated with the LinphoneAccountCreatorCbs object.
+**/
+LINPHONE_PUBLIC void *linphone_account_creator_cbs_get_user_data(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Assign a user pointer to a LinphoneAccountCreatorCbs object.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] ud The user pointer to associate with the LinphoneAccountCreatorCbs object.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * Get the validated callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current validated callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsValidatedCb linphone_account_creator_cbs_get_validated(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the validated callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The validated callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_validated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidatedCb cb);
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINPHONE_ACCOUNT_CREATOR_H_ */
diff --git a/coreapi/address.c b/coreapi/address.c
index ad07819bc..1299a5351 100644
--- a/coreapi/address.c
+++ b/coreapi/address.c
@@ -32,7 +32,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**/
LinphoneAddress * linphone_address_new(const char *addr){
SalAddress *saddr=sal_address_new(addr);
- if (saddr==NULL) ms_error("Cannot create LinphoneAddress, bad uri [%s]",addr);
+ if (saddr==NULL)
+ ms_error("Cannot create LinphoneAddress, bad uri [%s]",addr);
return saddr;
}
@@ -43,6 +44,20 @@ LinphoneAddress * linphone_address_clone(const LinphoneAddress *addr){
return sal_address_clone(addr);
}
+/**
+ * Increment reference count of LinphoneAddress object.
+**/
+LinphoneAddress * linphone_address_ref(LinphoneAddress *addr){
+ return sal_address_ref(addr);
+}
+
+/**
+ * Decrement reference count of LinphoneAddress object. When dropped to zero, memory is freed.
+**/
+void linphone_address_unref(LinphoneAddress *addr){
+ sal_address_unref(addr);
+}
+
/**
* Returns the address scheme, normally "sip".
**/
@@ -92,18 +107,26 @@ void linphone_address_set_domain(LinphoneAddress *uri, const char *host){
sal_address_set_domain(uri,host);
}
-/**
- * Sets the port number.
-**/
-void linphone_address_set_port(LinphoneAddress *uri, const char *port){
- sal_address_set_port(uri,port);
-}
/**
* Sets the port number.
**/
-void linphone_address_set_port_int(LinphoneAddress *uri, int port){
- sal_address_set_port_int(uri,port);
+void linphone_address_set_port(LinphoneAddress *uri, int port){
+ sal_address_set_port(uri,port);
+}
+
+/**
+ * Set a transport.
+**/
+void linphone_address_set_transport(LinphoneAddress *uri, LinphoneTransportType tp){
+ sal_address_set_transport(uri,(SalTransport)tp);
+}
+
+/**
+ * Get the transport.
+**/
+LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri){
+ return (LinphoneTransportType)sal_address_get_transport(uri);
}
/**
@@ -129,6 +152,36 @@ char *linphone_address_as_string_uri_only(const LinphoneAddress *u){
return sal_address_as_string_uri_only(u);
}
+/**
+ * Returns true if address refers to a secure location (sips)
+ * @deprecated use linphone_address_get_secure()
+**/
+bool_t linphone_address_is_secure(const LinphoneAddress *uri){
+ return sal_address_is_secure(uri);
+}
+
+/**
+ * Returns true if address refers to a secure location (sips)
+**/
+bool_t linphone_address_get_secure(const LinphoneAddress *uri){
+ return sal_address_is_secure(uri);
+}
+
+/**
+ * Make the address refer to a secure location (sips scheme)
+ * @param 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);
+}
+
+/**
+ * returns true if address is a routable sip address
+ */
+bool_t linphone_address_is_sip(const LinphoneAddress *uri){
+ return sal_address_is_sip(uri);
+}
+
static bool_t strings_equals(const char *s1, const char *s2){
if (s1==NULL && s2==NULL) return TRUE;
if (s1!=NULL && s2!=NULL && strcmp(s1,s2)==0) return TRUE;
@@ -137,7 +190,10 @@ static bool_t strings_equals(const char *s1, const char *s2){
/**
* Compare two LinphoneAddress ignoring tags and headers, basically just domain, username, and port.
- * Returns TRUE if they are equal.
+ * @param[in] a1 LinphoneAddress object
+ * @param[in] a2 LinphoneAddress object
+ * @return Boolean value telling if the LinphoneAddress objects are equal.
+ * @see linphone_address_equal()
**/
bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2){
const char *u1,*u2;
@@ -145,26 +201,86 @@ bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddr
int p1,p2;
u1=linphone_address_get_username(a1);
u2=linphone_address_get_username(a2);
- p1=linphone_address_get_port_int(a1);
- p2=linphone_address_get_port_int(a2);
+ p1=linphone_address_get_port(a1);
+ p2=linphone_address_get_port(a2);
h1=linphone_address_get_domain(a1);
h2=linphone_address_get_domain(a2);
return strings_equals(u1,u2) && strings_equals(h1,h2) && p1==p2;
}
/**
- * Destroys a LinphoneAddress object.
+ * Compare two LinphoneAddress taking the tags and headers into account.
+ * @param[in] a1 LinphoneAddress object
+ * @param[in] a2 LinphoneAddress object
+ * @return Boolean value telling if the LinphoneAddress objects are equal.
+ * @see linphone_address_weak_equal()
+ */
+bool_t linphone_address_equal(const LinphoneAddress *a1, const LinphoneAddress *a2) {
+ char *s1;
+ char *s2;
+ bool_t res;
+ if ((a1 == NULL) && (a2 == NULL)) return TRUE;
+ if ((a1 == NULL) || (a2 == NULL)) return FALSE;
+ s1 = linphone_address_as_string(a1);
+ s2 = linphone_address_as_string(a2);
+ res = (strcmp(s1, s2) == 0) ? TRUE : FALSE;
+ ms_free(s1);
+ ms_free(s2);
+ return res;
+}
+
+/**
+ * Destroys a LinphoneAddress object (actually calls linphone_address_unref()).
+ * @deprecated Use linphone_address_unref() instead
**/
void linphone_address_destroy(LinphoneAddress *u){
- sal_address_destroy(u);
+ sal_address_unref(u);
}
-int linphone_address_get_port_int(const LinphoneAddress *u) {
- return sal_address_get_port_int(u);
-}
+/**
+ * Get port number as an integer value.
+ */
-const char* linphone_address_get_port(const LinphoneAddress *u) {
+/**
+ * Get port number, 0 if not present.
+ */
+int linphone_address_get_port(const LinphoneAddress *u) {
return sal_address_get_port(u);
}
+/**
+ * Set the password encoded in the address.
+ * It is used for basic authentication (not recommended).
+ * @param addr the LinphoneAddress
+ * @param passwd the password to set.
+**/
+void linphone_address_set_password(LinphoneAddress *addr, const char *passwd){
+ sal_address_set_password(addr,passwd);
+}
+
+/**
+ * Get the password encoded in the address.
+ * It is used for basic authentication (not recommended).
+ * @param addr the address
+ * @return the password, if any, NULL otherwise.
+**/
+const char *linphone_address_get_password(const LinphoneAddress *addr){
+ return sal_address_get_password(addr);
+}
+
+/**
+ * Set a header into the address.
+ * Headers appear in the URI with '?', such as .
+ * @param addr the address
+ * @param header_name the header name
+ * @param header_value the header value
+**/
+void linphone_address_set_header(LinphoneAddress *addr, const char *header_name, const char *header_value){
+ sal_address_set_header(addr,header_name,header_value);
+}
+
+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 4b5d10fa8..f0f5eec2e 100644
--- a/coreapi/authentication.c
+++ b/coreapi/authentication.c
@@ -21,7 +21,7 @@
* 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 "lpconfig.h"
@@ -31,47 +31,32 @@
* @{
**/
-/**
- * Create a LinphoneAuthInfo object with supplied information.
- *
- * The object can be created empty, that is with all arguments set to NULL.
- * Username, userid, password and realm can be set later using specific methods.
-**/
-LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *userid,
- const char *passwd, const char *ha1,const char *realm)
-{
+LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain){
LinphoneAuthInfo *obj=ms_new0(LinphoneAuthInfo,1);
if (username!=NULL && (strlen(username)>0) ) obj->username=ms_strdup(username);
if (userid!=NULL && (strlen(userid)>0)) obj->userid=ms_strdup(userid);
if (passwd!=NULL && (strlen(passwd)>0)) obj->passwd=ms_strdup(passwd);
if (ha1!=NULL && (strlen(ha1)>0)) obj->ha1=ms_strdup(ha1);
if (realm!=NULL && (strlen(realm)>0)) obj->realm=ms_strdup(realm);
- obj->works=FALSE;
+ if (domain!=NULL && (strlen(domain)>0)) obj->domain=ms_strdup(domain);
return obj;
}
-static LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo *ai){
+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);
- obj->works=FALSE;
- obj->usecount=0;
+ if (ai->domain) obj->domain=ms_strdup(ai->domain);
return obj;
}
-/**
- * Returns username.
-**/
const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i){
return i->username;
}
-/**
- * Returns password.
-**/
const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i){
return i->passwd;
}
@@ -80,9 +65,18 @@ const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i){
return i->userid;
}
-/**
- * Sets the password.
-**/
+const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *i){
+ return i->realm;
+}
+
+const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *i){
+ return i->domain;
+}
+
+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) {
ms_free(info->passwd);
@@ -91,9 +85,6 @@ void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd){
if (passwd!=NULL && (strlen(passwd)>0)) info->passwd=ms_strdup(passwd);
}
-/**
- * Sets the username.
-**/
void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username){
if (info->username){
ms_free(info->username);
@@ -102,9 +93,6 @@ void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *usernam
if (username && strlen(username)>0) info->username=ms_strdup(username);
}
-/**
- * Sets userid.
-**/
void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid){
if (info->userid){
ms_free(info->userid);
@@ -113,6 +101,30 @@ void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *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){
+ ms_free(info->realm);
+ info->realm=NULL;
+ }
+ if (realm && strlen(realm)>0) info->realm=ms_strdup(realm);
+}
+
+void linphone_auth_info_set_domain(LinphoneAuthInfo *info, const char *domain){
+ if (info->domain){
+ ms_free(info->domain);
+ info->domain=NULL;
+ }
+ if (domain && strlen(domain)>0) info->domain=ms_strdup(domain);
+}
+
+void linphone_auth_info_set_ha1(LinphoneAuthInfo *info, const char *ha1){
+ if (info->ha1){
+ ms_free(info->ha1);
+ info->ha1=NULL;
+ }
+ if (ha1 && strlen(ha1)>0) info->ha1=ms_strdup(ha1);
+}
+
/**
* Destroys a LinphoneAuthInfo object.
**/
@@ -122,6 +134,7 @@ void linphone_auth_info_destroy(LinphoneAuthInfo *obj){
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);
ms_free(obj);
}
@@ -130,50 +143,53 @@ void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, in
char key[50];
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) {
+ /*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);
+ }
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->passwd!=NULL){
- lp_config_set_string(config,key,"passwd",obj->passwd);
- }
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->realm!=NULL){
lp_config_set_string(config,key,"realm",obj->realm);
}
+ if (obj->domain!=NULL){
+ lp_config_set_string(config,key,"domain",obj->domain);
+ }
}
LinphoneAuthInfo *linphone_auth_info_new_from_config_file(LpConfig * config, int pos)
{
char key[50];
- const char *username,*userid,*passwd,*ha1,*realm;
-
+ const char *username,*userid,*passwd,*ha1,*realm,*domain;
+ LinphoneAuthInfo *ret;
+
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);
- return linphone_auth_info_new(username,userid,passwd,ha1,realm);
-}
-
-static bool_t key_match(const char *tmp1, const char *tmp2){
- if (tmp1==NULL && tmp2==NULL) return TRUE;
- if (tmp1!=NULL && tmp2!=NULL && strcmp(tmp1,tmp2)==0) return TRUE;
- return FALSE;
-
+ domain=lp_config_get_string(config,key,"domain",NULL);
+ ret=linphone_auth_info_new(username,userid,passwd,ha1,realm,domain);
+ return ret;
}
static char * remove_quotes(char * input){
@@ -184,7 +200,7 @@ static char * remove_quotes(char * input){
return input;
}
-static int realm_match(const char *realm1, const char *realm2){
+static bool_t realm_match(const char *realm1, const char *realm2){
if (realm1==NULL && realm2==NULL) return TRUE;
if (realm1!=NULL && realm2!=NULL){
if (strcmp(realm1,realm2)==0) return TRUE;
@@ -202,41 +218,63 @@ static int realm_match(const char *realm1, const char *realm2){
return FALSE;
}
-/**
- * Retrieves a LinphoneAuthInfo previously entered into the LinphoneCore.
-**/
-const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username)
-{
+static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain){
MSList *elem;
- LinphoneAuthInfo *ret=NULL,*candidate=NULL;
- for (elem=lc->auth_info;elem!=NULL;elem=elem->next){
- LinphoneAuthInfo *pinfo=(LinphoneAuthInfo*)elem->data;
- if (realm==NULL){
- /*return the authinfo for any realm provided that there is only one for that username*/
- if (key_match(pinfo->username,username)){
- if (ret!=NULL){
- ms_warning("There are several auth info for username '%s'",username);
- return NULL;
+ const LinphoneAuthInfo *ret=NULL;
+
+ for (elem=lc->auth_info;elem!=NULL;elem=elem->next) {
+ LinphoneAuthInfo *pinfo = (LinphoneAuthInfo*)elem->data;
+ if (username && pinfo->username && strcmp(username,pinfo->username)==0) {
+ if (realm && domain){
+ if (pinfo->realm && realm_match(realm,pinfo->realm)
+ && pinfo->domain && strcmp(domain,pinfo->domain)==0) {
+ return pinfo;
}
- ret=pinfo;
- }
- }else{
- /*return the exact authinfo, or an authinfo for which realm was not supplied yet*/
- if (pinfo->realm!=NULL){
- if (realm_match(pinfo->realm,realm)
- && key_match(pinfo->username,username))
+ } else if (realm) {
+ if (pinfo->realm && realm_match(realm,pinfo->realm)) {
+ if (ret!=NULL) {
+ ms_warning("Non unique realm found for %s",username);
+ return NULL;
+ }
ret=pinfo;
- }else{
- if (key_match(pinfo->username,username))
- candidate=pinfo;
+ }
+ } else if (domain && pinfo->domain && strcmp(domain,pinfo->domain)==0 && pinfo->ha1==NULL) {
+ return pinfo;
+ } else if (!domain && pinfo->ha1==NULL) {
+ return pinfo;
}
}
}
- if (ret==NULL && candidate!=NULL)
- ret=candidate;
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 *ai=NULL;
+ if (realm){
+ ai=find_auth_info(lc,username,realm,NULL);
+ if (ai==NULL && domain){
+ ai=find_auth_info(lc,username,realm,domain);
+ }
+ }
+ if (ai == NULL && domain != NULL) {
+ ai=find_auth_info(lc,username,NULL,domain);
+ }
+ if (ai==NULL){
+ ai=find_auth_info(lc,username,NULL,NULL);
+ }
+ /*if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", ai->username, ai->realm);*/
+ return ai;
+}
+
static void write_auth_infos(LinphoneCore *lc){
MSList *elem;
int i;
@@ -249,43 +287,73 @@ static void write_auth_infos(LinphoneCore *lc){
linphone_auth_info_write_config(lc->config,NULL,i); /* mark the end */
}
+LinphoneAuthInfo * linphone_core_create_auth_info(LinphoneCore *lc, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain) {
+ return linphone_auth_info_new(username, userid, passwd, ha1, realm, domain);
+}
+
/**
* Adds authentication information to the LinphoneCore.
- *
- * This information will be used during all SIP transacations that require authentication.
+ *
+ * This information will be used during all SIP transactions that require authentication.
**/
-void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info)
-{
+void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){
LinphoneAuthInfo *ai;
MSList *elem;
MSList *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;
+ }
/* find if we are attempting to modify an existing auth info */
- ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username);
- if (ai!=NULL){
+ 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);
linphone_auth_info_destroy(ai);
+ updating=TRUE;
}
lc->auth_info=ms_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){
- const char *username,*realm;
SalOp *op=(SalOp*)elem->data;
LinphoneAuthInfo *ai;
- sal_op_get_auth_requested(op,&realm,&username);
- ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
+ 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);
if (ai){
SalAuthInfo sai;
+ MSList* proxy;
sai.username=ai->username;
sai.userid=ai->userid;
sai.realm=ai->realm;
sai.password=ai->passwd;
+ sai.ha1=ai->ha1;
+ /*proxy case*/
+ for (proxy=(MSList*)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;
+ }
+ }
sal_op_authenticate(op,&sai);
- ai->usecount++;
+ restarted_op_count++;
}
}
+ if (l){
+ ms_message("linphone_core_add_auth_info(): restarted [%i] operation(s) after %s auth info for\n"
+ "\tusername: [%s]\n"
+ "\trealm [%s]\n"
+ "\tdomain [%s]\n",
+ restarted_op_count,
+ updating ? "updating" : "adding",
+ info->username ? info->username : "",
+ info->realm ? info->realm : "",
+ info->domain ? info->domain : "");
+ }
ms_list_free(l);
- write_auth_infos(lc);
+ if(lc->sip_conf.save_auth_info) write_auth_infos(lc);
}
@@ -301,17 +369,18 @@ void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *inf
**/
void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){
LinphoneAuthInfo *r;
- r=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username);
+ 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);
- /*printf("len=%i newlen=%i\n",len,newlen);*/
linphone_auth_info_destroy(r);
- write_auth_infos(lc);
+ if(lc->sip_conf.save_auth_info) write_auth_infos(lc);
}
}
/**
* Returns an unmodifiable list of currently entered LinphoneAuthInfo.
+ * @param[in] lc The LinphoneCore object
+ * @return \mslist{LinphoneAuthInfo}
**/
const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc){
return lc->auth_info;
diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c
new file mode 100644
index 000000000..2546e7850
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_address_impl.c
@@ -0,0 +1,238 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+/**/
+/* Address manipulation API*/
+SalAddress * sal_address_new(const char *uri){
+ belle_sip_header_address_t* result;
+ if (uri) {
+ result=belle_sip_header_address_parse (uri);
+ /*may return NULL*/
+ } else {
+ result = belle_sip_header_address_new();
+ belle_sip_header_address_set_uri(result,belle_sip_uri_new());
+ }
+ if (result) belle_sip_object_ref(result);
+ return (SalAddress *)result;
+}
+
+SalAddress * sal_address_clone(const SalAddress *addr){
+ return (SalAddress *) belle_sip_object_ref(belle_sip_object_clone(BELLE_SIP_OBJECT(addr)));
+}
+
+const char *sal_address_get_scheme(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ belle_generic_uri_t* generic_uri = belle_sip_header_address_get_absolute_uri(header_addr);
+ if (uri) {
+ if (belle_sip_uri_is_secure(uri)) return "sips";
+ else return "sip";
+ } else if (generic_uri)
+ return belle_generic_uri_get_scheme(generic_uri);
+ else
+ return NULL;
+}
+
+void sal_address_set_secure(SalAddress *addr, bool_t enabled){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ if (uri) belle_sip_uri_set_secure(uri,enabled);
+}
+
+bool_t sal_address_is_secure(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ if (uri) return belle_sip_uri_is_secure(uri);
+ return FALSE;
+}
+
+const char *sal_address_get_display_name(const SalAddress* addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ return belle_sip_header_address_get_displayname(header_addr);
+
+}
+const char *sal_address_get_display_name_unquoted(const SalAddress *addr){
+ return sal_address_get_display_name(addr);
+}
+#define SAL_ADDRESS_GET(addr,param) \
+{belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\
+belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\
+if (uri) {\
+ return belle_sip_uri_get_##param(uri);\
+} else\
+ return NULL;}
+
+#define SAL_ADDRESS_SET(addr,param,value) {\
+belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\
+belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\
+belle_sip_uri_set_##param(uri,value);}
+
+const char *sal_address_get_username(const SalAddress *addr){
+ SAL_ADDRESS_GET(addr,user)
+}
+const char *sal_address_get_domain(const SalAddress *addr){
+ SAL_ADDRESS_GET(addr,host)
+}
+int sal_address_get_port(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ if (uri) {
+ return belle_sip_uri_get_port(uri);
+ } else
+ return -1;
+}
+SalTransport sal_address_get_transport(const SalAddress* addr){
+ const char *transport=sal_address_get_transport_name(addr);
+ if (transport)
+ return sal_transport_parse(transport);
+ else
+ return SalTransportUDP;
+};
+
+const char* sal_address_get_transport_name(const SalAddress* addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ if (uri) {
+ return belle_sip_uri_get_transport_param(uri);
+ }
+ return NULL;
+}
+
+void sal_address_set_display_name(SalAddress *addr, const char *display_name){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_header_address_set_displayname(header_addr,display_name);
+}
+
+void sal_address_set_username(SalAddress *addr, const char *username){
+ SAL_ADDRESS_SET(addr,user,username);
+}
+
+void sal_address_set_password(SalAddress *addr, const char *passwd){
+ SAL_ADDRESS_SET(addr,user_password,passwd);
+}
+
+const char* sal_address_get_password(const SalAddress *addr){
+ SAL_ADDRESS_GET(addr,user_password);
+}
+
+void sal_address_set_domain(SalAddress *addr, const char *host){
+ SAL_ADDRESS_SET(addr,host,host);
+}
+
+void sal_address_set_port(SalAddress *addr, int port){
+ SAL_ADDRESS_SET(addr,port,port);
+}
+
+void sal_address_clean(SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri=belle_sip_header_address_get_uri(header_addr);
+ if (uri) {
+ belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(uri));
+ belle_sip_uri_headers_clean(uri);
+ }
+ belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(header_addr));
+ return ;
+}
+
+char *sal_address_as_string(const SalAddress *addr){
+ char tmp[1024]={0};
+ size_t off=0;
+ belle_sip_object_marshal((belle_sip_object_t*)addr,tmp,sizeof(tmp),&off);
+ return ms_strdup(tmp);
+}
+
+bool_t sal_address_is_sip(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ return belle_sip_header_address_get_uri(header_addr) != NULL;
+}
+
+char *sal_address_as_string_uri_only(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* sip_uri = belle_sip_header_address_get_uri(header_addr);
+ belle_generic_uri_t* absolute_uri = belle_sip_header_address_get_absolute_uri(header_addr);
+ char tmp[1024]={0};
+ size_t off=0;
+ belle_sip_object_t* uri;
+
+ if (sip_uri) {
+ uri=(belle_sip_object_t*)sip_uri;
+ } else if (absolute_uri) {
+ uri=(belle_sip_object_t*)absolute_uri;
+ } else {
+ ms_error("Cannot generate string for addr [%p] with null uri",addr);
+ return NULL;
+ }
+ belle_sip_object_marshal(uri,tmp,sizeof(tmp),&off);
+ return ms_strdup(tmp);
+}
+
+void sal_address_set_param(SalAddress *addr,const char* name,const char* value){
+ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr);
+ belle_sip_parameters_set_parameter(parameters,name,value);
+ return ;
+}
+
+
+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_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);
+}
+
+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);
+}
+
+void sal_address_set_transport(SalAddress* addr,SalTransport transport){
+ if (!sal_address_is_secure(addr)){
+ SAL_ADDRESS_SET(addr,transport_param,sal_transport_to_string(transport));
+ }
+}
+
+void sal_address_set_transport_name(SalAddress* addr,const char *transport){
+ SAL_ADDRESS_SET(addr,transport_param,transport);
+}
+
+SalAddress *sal_address_ref(SalAddress *addr){
+ return (SalAddress*)belle_sip_object_ref(BELLE_SIP_HEADER_ADDRESS(addr));
+}
+
+void sal_address_unref(SalAddress *addr){
+ belle_sip_object_unref(BELLE_SIP_HEADER_ADDRESS(addr));
+}
+
+bool_t sal_address_is_ipv6(const SalAddress *addr){
+ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
+ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
+ if (uri){
+ const char *host=belle_sip_uri_get_host(uri);
+ if (host && strchr(host,':')!=NULL)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void sal_address_destroy(SalAddress *addr){
+ sal_address_unref(addr);
+}
+
diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c
new file mode 100644
index 000000000..6e3a6cba9
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_impl.c
@@ -0,0 +1,1184 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "sal_impl.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+typedef struct belle_sip_certificates_chain_t _SalCertificatesChain;
+typedef struct belle_sip_signing_key_t _SalSigningKey;
+
+/*
+rfc3323
+4.2 Expressing Privacy Preferences
+When a Privacy header is constructed, it MUST consist of either the
+ value 'none', or one or more of the values 'user', 'header' and
+ 'session' (each of which MUST appear at most once) which MAY in turn
+ be followed by the 'critical' indicator.
+ */
+void sal_op_set_privacy_from_message(SalOp* op,belle_sip_message_t* msg) {
+ belle_sip_header_privacy_t* privacy = belle_sip_message_get_header_by_type(msg,belle_sip_header_privacy_t);
+ if (!privacy) {
+ sal_op_set_privacy(op,SalPrivacyNone);
+ } else {
+ belle_sip_list_t* privacy_list=belle_sip_header_privacy_get_privacy(privacy);
+ sal_op_set_privacy(op,0);
+ for (;privacy_list!=NULL;privacy_list=privacy_list->next) {
+ char* privacy_value=(char*)privacy_list->data;
+ if(strcmp(sal_privacy_to_string(SalPrivacyCritical),privacy_value) == 0)
+ sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyCritical);
+ if(strcmp(sal_privacy_to_string(SalPrivacyHeader),privacy_value) == 0)
+ sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyHeader);
+ if(strcmp(sal_privacy_to_string(SalPrivacyId),privacy_value) == 0)
+ sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyId);
+ if(strcmp(sal_privacy_to_string(SalPrivacyNone),privacy_value) == 0) {
+ sal_op_set_privacy(op,SalPrivacyNone);
+ break;
+ }
+ if(strcmp(sal_privacy_to_string(SalPrivacySession),privacy_value) == 0)
+ sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacySession);
+ if(strcmp(sal_privacy_to_string(SalPrivacyUser),privacy_value) == 0)
+ sal_op_set_privacy(op,sal_op_get_privacy(op)|SalPrivacyUser);
+ }
+ }
+}
+static void set_tls_properties(Sal *ctx);
+
+void _belle_sip_log(belle_sip_log_level lev, const char *fmt, va_list args) {
+ int ortp_level;
+ switch(lev) {
+ case BELLE_SIP_LOG_FATAL:
+ ortp_level=ORTP_FATAL;
+ break;
+ case BELLE_SIP_LOG_ERROR:
+ ortp_level=ORTP_ERROR;
+ break;
+ case BELLE_SIP_LOG_WARNING:
+ ortp_level=ORTP_WARNING;
+ break;
+ case BELLE_SIP_LOG_MESSAGE:
+ ortp_level=ORTP_MESSAGE;
+ break;
+ case BELLE_SIP_LOG_DEBUG:
+ default:
+ ortp_level=ORTP_DEBUG;
+ break;
+ }
+ if (ortp_log_level_enabled(ortp_level)){
+ ortp_logv(ortp_level,fmt,args);
+ }
+}
+
+void sal_enable_log(){
+ sal_set_log_level(ORTP_MESSAGE);
+}
+
+void sal_disable_log() {
+ sal_set_log_level(ORTP_ERROR);
+}
+
+void sal_set_log_level(OrtpLogLevel level) {
+ belle_sip_log_level belle_sip_level;
+ if ((level&ORTP_FATAL) != 0) {
+ belle_sip_level = BELLE_SIP_LOG_FATAL;
+ } else if ((level&ORTP_ERROR) != 0) {
+ belle_sip_level = BELLE_SIP_LOG_ERROR;
+ } else if ((level&ORTP_WARNING) != 0) {
+ belle_sip_level = BELLE_SIP_LOG_WARNING;
+ } else if ((level&ORTP_MESSAGE) != 0) {
+ belle_sip_level = BELLE_SIP_LOG_MESSAGE;
+ } else if (((level&ORTP_DEBUG) != 0) || ((level&ORTP_TRACE) != 0)) {
+ belle_sip_level = BELLE_SIP_LOG_DEBUG;
+ } else {
+ //well, this should never occurs but...
+ belle_sip_level = BELLE_SIP_LOG_MESSAGE;
+ }
+ belle_sip_set_log_level(belle_sip_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);
+ op->has_auth_pending=TRUE;
+ }
+}
+
+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);
+ }
+ }
+}
+
+void sal_process_authentication(SalOp *op) {
+ belle_sip_request_t* initial_request=belle_sip_transaction_get_request((belle_sip_transaction_t*)op->pending_auth_transaction);
+ belle_sip_request_t* new_request;
+ bool_t is_within_dialog=FALSE;
+ belle_sip_list_t* auth_list=NULL;
+ belle_sip_auth_event_t* auth_event;
+ belle_sip_response_t *response=belle_sip_transaction_get_response((belle_sip_transaction_t*)op->pending_auth_transaction);
+ belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(initial_request,belle_sip_header_from_t);
+ belle_sip_uri_t *from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from);
+
+ if (strcasecmp(belle_sip_uri_get_host(from_uri),"anonymous.invalid")==0){
+ /*prefer using the from from the SalOp*/
+ from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)sal_op_get_from_address(op));
+ }
+
+ if (op->dialog && belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_CONFIRMED) {
+ new_request = belle_sip_dialog_create_request_from(op->dialog,initial_request);
+ if (!new_request)
+ new_request = belle_sip_dialog_create_queued_request_from(op->dialog,initial_request);
+ is_within_dialog=TRUE;
+ } else {
+ new_request=initial_request;
+ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_AUTHORIZATION);
+ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_PROXY_AUTHORIZATION);
+ }
+ if (new_request==NULL) {
+ ms_error("sal_process_authentication() op=[%p] cannot obtain new request from dialog.",op);
+ return;
+ }
+
+ if (belle_sip_provider_add_authorization(op->base.root->prov,new_request,response,from_uri,&auth_list,op->base.realm)) {
+ if (is_within_dialog) {
+ sal_op_send_request(op,new_request);
+ } else {
+ sal_op_resend_request(op,new_request);
+ }
+ sal_remove_pending_auth(op->base.root,op);
+ }else {
+ belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(response,belle_sip_header_from_t);
+ char *tmp=belle_sip_object_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)));
+ ms_message("No auth info found for [%s]",tmp);
+ belle_sip_free(tmp);
+ sal_add_pending_auth(op->base.root,op);
+
+ if (is_within_dialog) {
+ belle_sip_object_unref(new_request);
+ }
+ }
+ /*always store auth info, for case of wrong credential*/
+ if (op->auth_info) {
+ sal_auth_info_delete(op->auth_info);
+ op->auth_info=NULL;
+ }
+ if (auth_list){
+ auth_event=(belle_sip_auth_event_t*)(auth_list->data);
+ op->auth_info=sal_auth_info_create(auth_event);
+ belle_sip_list_free_with_data(auth_list,(void (*)(void*))belle_sip_auth_event_destroy);
+ }
+}
+
+static void process_dialog_terminated(void *sal, const belle_sip_dialog_terminated_event_t *event){
+ belle_sip_dialog_t* dialog = belle_sip_dialog_terminated_event_get_dialog(event);
+ SalOp* op = belle_sip_dialog_get_application_data(dialog);
+ if (op && op->callbacks && op->callbacks->process_dialog_terminated) {
+ op->callbacks->process_dialog_terminated(op,event);
+ } else {
+ ms_error("sal process_dialog_terminated no op found for this dialog [%p], ignoring",dialog);
+ }
+}
+
+static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
+ belle_sip_client_transaction_t*client_transaction;
+ SalOp* op;
+ 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(belle_sip_io_error_event_get_source(event));
+ op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction));
+ /*also reset auth count on IO error*/
+ op->auth_requests=0;
+ if (op->callbacks && op->callbacks->process_io_error) {
+ op->callbacks->process_io_error(op,event);
+ }
+ } else {
+ /*ms_error("sal process_io_error not implemented yet for non transaction");*/
+ /*nop, because already handle at transaction layer*/
+ }
+}
+
+static void process_request_event(void *ud, const belle_sip_request_event_t *event) {
+ Sal *sal=(Sal*)ud;
+ SalOp* op=NULL;
+ belle_sip_request_t* req = belle_sip_request_event_get_request(event);
+ belle_sip_dialog_t* dialog=belle_sip_request_event_get_dialog(event);
+ belle_sip_header_address_t* origin_address;
+ belle_sip_header_address_t* address=NULL;
+ belle_sip_header_from_t* from_header;
+ belle_sip_header_to_t* to;
+ belle_sip_response_t* resp;
+ belle_sip_header_t *evh;
+ const char *method=belle_sip_request_get_method(req);
+ belle_sip_header_contact_t* remote_contact = belle_sip_message_get_header_by_type(req, belle_sip_header_contact_t);
+
+ from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
+
+ if (dialog) {
+ op=(SalOp*)belle_sip_dialog_get_application_data(dialog);
+ 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;
+ }
+
+ if (!op->base.from_address) {
+ if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)))
+ 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)));
+ else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header))))
+ address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
+ ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
+ else
+ ms_error("Cannot not find from uri from request [%p]",req);
+ sal_op_set_from_address(op,(SalAddress*)address);
+ belle_sip_object_unref(address);
+ }
+
+ if( remote_contact ){
+ __sal_op_set_remote_contact(op, belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact)));
+ }
+
+ if (!op->base.to_address) {
+ to=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_to_t);
+ if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)))
+ address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to))
+ ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)));
+ else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to))))
+ address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to))
+ ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to)));
+ else
+ ms_error("Cannot not find to uri from request [%p]",req);
+
+ sal_op_set_to_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));
+ __sal_op_set_network_origin_address(op,(SalAddress*)origin_address);
+ belle_sip_object_unref(origin_address);
+ }
+ if (!op->base.remote_ua) {
+ sal_op_set_remote_ua(op,BELLE_SIP_MESSAGE(req));
+ }
+
+ if (!op->base.call_id) {
+ op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_call_id_t))));
+ }
+ /*It is worth noting that proxies can (and
+ will) remove this header field*/
+ sal_op_set_privacy_from_message(op,(belle_sip_message_t*)req);
+
+ sal_op_assign_recv_headers(op,(belle_sip_message_t*)req);
+ if (op->callbacks && op->callbacks->process_request_event) {
+ op->callbacks->process_request_event(op,event);
+ } else {
+ ms_error("sal process_request_event not implemented yet");
+ }
+
+}
+
+static void process_response_event(void *user_ctx, const belle_sip_response_event_t *event){
+ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
+ belle_sip_response_t* response = belle_sip_response_event_get_response(event);
+ int response_code = belle_sip_response_get_status_code(response);
+
+ if (!client_transaction) {
+ ms_warning("Discarding stateless response [%i]",response_code);
+ return;
+ } else {
+ SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction));
+ belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+ belle_sip_header_contact_t* remote_contact = belle_sip_message_get_header_by_type(response, belle_sip_header_contact_t);
+
+ if (op->state == SalOpStateTerminated) {
+ belle_sip_message("Op is terminated, nothing to do with this [%i]",response_code);
+ return;
+ }
+ /*do it all the time, since we can receive provisional responses from a different instance than the final one*/
+ sal_op_set_remote_ua(op,BELLE_SIP_MESSAGE(response));
+
+ if(remote_contact) {
+ __sal_op_set_remote_contact(op, belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact)));
+ }
+
+ if (!op->base.call_id) {
+ op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(response), belle_sip_header_call_id_t))));
+ }
+
+ sal_op_assign_recv_headers(op,(belle_sip_message_t*)response);
+
+ if (op->callbacks && op->callbacks->process_response_event) {
+ /*handle authorization*/
+ switch (response_code) {
+ case 200:
+ break;
+ case 401:
+ case 407:
+ if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) {
+ /*only bye are completed*/
+ belle_sip_message("Op is in state terminating, nothing else to do ");
+ } else {
+ if (op->pending_auth_transaction){
+ belle_sip_object_unref(op->pending_auth_transaction);
+ op->pending_auth_transaction=NULL;
+ }
+ if (++op->auth_requests > 2) {
+ ms_warning("Auth info cannot be found for op [%s/%s] after 2 attempts, giving up",sal_op_get_from(op)
+ ,sal_op_get_to(op));
+ op->base.root->callbacks.auth_failure(op,op->auth_info);
+ sal_remove_pending_auth(op->base.root,op);
+ } else {
+ op->pending_auth_transaction=(belle_sip_client_transaction_t*)belle_sip_object_ref(client_transaction);
+ sal_process_authentication(op);
+ return;
+ }
+ }
+ break;
+ case 403:
+ if (op->auth_info) op->base.root->callbacks.auth_failure(op,op->auth_info);
+ break;
+ }
+ if (response_code >= 180 && response_code !=401 && response_code !=407 && response_code !=403) {
+ /*not an auth request*/
+ op->auth_requests=0;
+ }
+ op->callbacks->process_response_event(op,event);
+ } else {
+ ms_error("Unhandled event response [%p]",event);
+ }
+ }
+}
+
+static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
+ belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event);
+ SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction));
+ if (op && op->callbacks && op->callbacks->process_timeout) {
+ op->callbacks->process_timeout(op,event);
+ } else {
+ ms_error("Unhandled event timeout [%p]",event);
+ }
+}
+
+static void process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
+ belle_sip_client_transaction_t* client_transaction = belle_sip_transaction_terminated_event_get_client_transaction(event);
+ belle_sip_server_transaction_t* server_transaction = belle_sip_transaction_terminated_event_get_server_transaction(event);
+ belle_sip_transaction_t* trans;
+ SalOp* op;
+
+ if(client_transaction)
+ trans=BELLE_SIP_TRANSACTION(client_transaction);
+ else
+ trans=BELLE_SIP_TRANSACTION(server_transaction);
+
+ op = (SalOp*)belle_sip_transaction_get_application_data(trans);
+ if (op && op->callbacks && op->callbacks->process_transaction_terminated) {
+ op->callbacks->process_transaction_terminated(op,event);
+ } else {
+ ms_message("Unhandled transaction terminated [%p]",trans);
+ }
+ if (op) sal_op_unref(op); /*because every transaction ref op*/
+}
+
+
+static void process_auth_requested(void *sal, belle_sip_auth_event_t *event) {
+ SalAuthInfo* auth_info = sal_auth_info_create(event);
+ ((Sal*)sal)->callbacks.auth_requested(sal,auth_info);
+ belle_sip_auth_event_set_passwd(event,(const char*)auth_info->password);
+ belle_sip_auth_event_set_ha1(event,(const char*)auth_info->ha1);
+ belle_sip_auth_event_set_userid(event,(const char*)auth_info->userid);
+ belle_sip_auth_event_set_signing_key(event,(belle_sip_signing_key_t *)auth_info->key);
+ belle_sip_auth_event_set_client_certificates_chain(event,(belle_sip_certificates_chain_t* )auth_info->certificates);
+ sal_auth_info_delete(auth_info);
+}
+
+Sal * sal_init(){
+ belle_sip_listener_callbacks_t listener_callbacks;
+ Sal * sal=ms_new0(Sal,1);
+
+ /*belle_sip_object_enable_marshal_check(TRUE);*/
+ sal->auto_contacts=TRUE;
+
+ /*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);
+
+ 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);
+#endif
+ sal_append_stack_string_to_user_agent(sal);
+ belle_sip_object_ref(sal->user_agent);
+
+ sal->prov = belle_sip_stack_create_provider(sal->stack,NULL);
+ sal_nat_helper_enable(sal,TRUE);
+ memset(&listener_callbacks,0,sizeof(listener_callbacks));
+ listener_callbacks.process_dialog_terminated=process_dialog_terminated;
+ listener_callbacks.process_io_error=process_io_error;
+ listener_callbacks.process_request_event=process_request_event;
+ listener_callbacks.process_response_event=process_response_event;
+ listener_callbacks.process_timeout=process_timeout;
+ listener_callbacks.process_transaction_terminated=process_transaction_terminated;
+ listener_callbacks.process_auth_requested=process_auth_requested;
+ sal->listener=belle_sip_listener_create_from_callbacks(&listener_callbacks,sal);
+ belle_sip_provider_add_sip_listener(sal->prov,sal->listener);
+ sal->tls_verify=TRUE;
+ sal->tls_verify_cn=TRUE;
+ sal->refresher_retry_after=60000; /*default value in ms*/
+ sal->enable_sip_update=TRUE;
+ sal->pending_trans_checking=TRUE;
+ return sal;
+}
+
+void sal_set_user_pointer(Sal *sal, void *user_data){
+ sal->up=user_data;
+}
+
+void *sal_get_user_pointer(const Sal *sal){
+ return sal->up;
+}
+
+static void unimplemented_stub(){
+ ms_warning("Unimplemented SAL callback");
+}
+
+void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
+ memcpy(&ctx->callbacks,cbs,sizeof(*cbs));
+ if (ctx->callbacks.call_received==NULL)
+ ctx->callbacks.call_received=(SalOnCallReceived)unimplemented_stub;
+ if (ctx->callbacks.call_ringing==NULL)
+ ctx->callbacks.call_ringing=(SalOnCallRinging)unimplemented_stub;
+ if (ctx->callbacks.call_accepted==NULL)
+ ctx->callbacks.call_accepted=(SalOnCallAccepted)unimplemented_stub;
+ if (ctx->callbacks.call_failure==NULL)
+ ctx->callbacks.call_failure=(SalOnCallFailure)unimplemented_stub;
+ if (ctx->callbacks.call_terminated==NULL)
+ ctx->callbacks.call_terminated=(SalOnCallTerminated)unimplemented_stub;
+ if (ctx->callbacks.call_released==NULL)
+ ctx->callbacks.call_released=(SalOnCallReleased)unimplemented_stub;
+ if (ctx->callbacks.call_updating==NULL)
+ ctx->callbacks.call_updating=(SalOnCallUpdating)unimplemented_stub;
+ if (ctx->callbacks.auth_failure==NULL)
+ ctx->callbacks.auth_failure=(SalOnAuthFailure)unimplemented_stub;
+ if (ctx->callbacks.register_success==NULL)
+ ctx->callbacks.register_success=(SalOnRegisterSuccess)unimplemented_stub;
+ if (ctx->callbacks.register_failure==NULL)
+ ctx->callbacks.register_failure=(SalOnRegisterFailure)unimplemented_stub;
+ if (ctx->callbacks.dtmf_received==NULL)
+ ctx->callbacks.dtmf_received=(SalOnDtmfReceived)unimplemented_stub;
+ if (ctx->callbacks.notify==NULL)
+ 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.parse_presence_requested==NULL)
+ ctx->callbacks.parse_presence_requested=(SalOnParsePresenceRequested)unimplemented_stub;
+ if (ctx->callbacks.convert_presence_to_xml_requested==NULL)
+ ctx->callbacks.convert_presence_to_xml_requested=(SalOnConvertPresenceToXMLRequested)unimplemented_stub;
+ if (ctx->callbacks.notify_presence==NULL)
+ ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub;
+ if (ctx->callbacks.subscribe_presence_received==NULL)
+ ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub;
+ if (ctx->callbacks.text_received==NULL)
+ ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub;
+ if (ctx->callbacks.is_composing_received==NULL)
+ ctx->callbacks.is_composing_received=(SalOnIsComposingReceived)unimplemented_stub;
+ if (ctx->callbacks.ping_reply==NULL)
+ ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
+ if (ctx->callbacks.auth_requested==NULL)
+ ctx->callbacks.auth_requested=(SalOnAuthRequested)unimplemented_stub;
+ if (ctx->callbacks.info_received==NULL)
+ ctx->callbacks.info_received=(SalOnInfoReceived)unimplemented_stub;
+ if (ctx->callbacks.on_publish_response==NULL)
+ ctx->callbacks.on_publish_response=(SalOnPublishResponse)unimplemented_stub;
+ if (ctx->callbacks.on_expire==NULL)
+ ctx->callbacks.on_expire=(SalOnExpire)unimplemented_stub;
+}
+
+
+
+void sal_uninit(Sal* sal){
+ belle_sip_object_unref(sal->user_agent);
+ belle_sip_object_unref(sal->prov);
+ 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);
+ if (sal->uuid) ms_free(sal->uuid);
+ if (sal->root_ca) ms_free(sal->root_ca);
+ ms_free(sal);
+};
+
+int sal_transport_available(Sal *sal, SalTransport t){
+ switch(t){
+ case SalTransportUDP:
+ case SalTransportTCP:
+ return TRUE;
+ case SalTransportTLS:
+ return belle_sip_stack_tls_available(sal->stack);
+ case SalTransportDTLS:
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static int sal_add_listen_port(Sal *ctx, SalAddress* addr, bool_t is_tunneled){
+ int result;
+ belle_sip_listening_point_t* lp;
+ if (is_tunneled){
+#ifdef TUNNEL_ENABLED
+ if (sal_address_get_transport(addr)!=SalTransportUDP){
+ ms_error("Tunneled mode is only available for UDP kind of transports.");
+ return -1;
+ }
+ lp = belle_sip_tunnel_listening_point_new(ctx->stack, ctx->tunnel_client);
+ if (!lp){
+ ms_error("Could not create tunnel listening point.");
+ return -1;
+ }
+#else
+ ms_error("No tunnel support in library.");
+ return -1;
+#endif
+ }else{
+ lp = belle_sip_stack_create_listening_point(ctx->stack,
+ sal_address_get_domain(addr),
+ sal_address_get_port(addr),
+ sal_transport_to_string(sal_address_get_transport(addr)));
+ }
+ 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);
+ } else {
+ return -1;
+ }
+ return result;
+}
+
+int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_tunneled) {
+ SalAddress* sal_addr = sal_address_new(NULL);
+ int result;
+ sal_address_set_domain(sal_addr,addr);
+ sal_address_set_port(sal_addr,port);
+ sal_address_set_transport(sal_addr,tr);
+ result = sal_add_listen_port(ctx, sal_addr, is_tunneled);
+ sal_address_destroy(sal_addr);
+ return result;
+}
+
+static void remove_listening_point(belle_sip_listening_point_t* lp,belle_sip_provider_t* prov) {
+ belle_sip_provider_remove_listening_point(prov,lp);
+}
+
+int sal_get_listening_port(Sal *ctx, SalTransport tr){
+ const char *tpn=sal_transport_to_string(tr);
+ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(ctx->prov, tpn);
+ if (lp){
+ return belle_sip_listening_point_get_port(lp);
+ }
+ return 0;
+}
+
+int sal_unlisten_ports(Sal *ctx){
+ const belle_sip_list_t * lps = belle_sip_provider_get_listening_points(ctx->prov);
+ belle_sip_list_t * tmp_list = belle_sip_list_copy(lps);
+ belle_sip_list_for_each2 (tmp_list,(void (*)(void*,void*))remove_listening_point,ctx->prov);
+ belle_sip_list_free(tmp_list);
+ ms_message("sal_unlisten_ports done");
+ return 0;
+}
+
+ortp_socket_t sal_get_socket(Sal *ctx){
+ ms_warning("sal_get_socket is deprecated");
+ return -1;
+}
+
+void sal_set_user_agent(Sal *ctx, const char *user_agent){
+ belle_sip_header_user_agent_set_products(ctx->user_agent,NULL);
+ belle_sip_header_user_agent_add_product(ctx->user_agent,user_agent);
+ return ;
+}
+
+const char* sal_get_user_agent(Sal *ctx){
+ static char user_agent[255];
+ belle_sip_header_user_agent_get_products_as_string(ctx->user_agent, user_agent, 254);
+ return user_agent;
+}
+
+void sal_append_stack_string_to_user_agent(Sal *ctx) {
+ char stack_string[64];
+ snprintf(stack_string, sizeof(stack_string) - 1, "(belle-sip/%s)", belle_sip_version_to_string());
+ belle_sip_header_user_agent_add_product(ctx->user_agent, stack_string);
+}
+
+/*keepalive period in ms*/
+void sal_set_keepalive_period(Sal *ctx,unsigned int value){
+ const belle_sip_list_t* iterator;
+ belle_sip_listening_point_t* lp;
+ ctx->keep_alive=value;
+ for (iterator=belle_sip_provider_get_listening_points(ctx->prov);iterator!=NULL;iterator=iterator->next) {
+ lp=(belle_sip_listening_point_t*)iterator->data;
+ if (ctx->use_tcp_tls_keep_alive || strcasecmp(belle_sip_listening_point_get_transport(lp),"udp")==0) {
+ belle_sip_listening_point_set_keep_alive(lp,ctx->keep_alive);
+ }
+ }
+}
+int sal_set_tunnel(Sal *ctx, void *tunnelclient) {
+#ifdef TUNNEL_ENABLED
+ ctx->tunnel_client=tunnelclient;
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/**
+ * returns keepalive period in ms
+ * 0 desactiaved
+ * */
+unsigned int sal_get_keepalive_period(Sal *ctx){
+ return ctx->keep_alive;
+}
+void sal_use_session_timers(Sal *ctx, int expires){
+ ctx->session_expires=expires;
+ return ;
+}
+
+void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){
+ ctx->one_matching_codec=one_matching_codec;
+}
+
+void sal_use_rport(Sal *ctx, bool_t use_rports){
+ belle_sip_provider_enable_rport(ctx->prov,use_rports);
+ ms_message("Sal use rport [%s]",use_rports?"enabled":"disabled");
+ return ;
+}
+
+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);
+ }
+}
+
+void sal_set_root_ca(Sal* ctx, const char* rootCa){
+ if (ctx->root_ca){
+ ms_free(ctx->root_ca);
+ ctx->root_ca=NULL;
+ }
+ if (rootCa)
+ ctx->root_ca=ms_strdup(rootCa);
+ set_tls_properties(ctx);
+ return ;
+}
+
+void sal_verify_server_certificates(Sal *ctx, bool_t verify){
+ ctx->tls_verify=verify;
+ set_tls_properties(ctx);
+ return ;
+}
+
+void sal_verify_server_cn(Sal *ctx, bool_t verify){
+ ctx->tls_verify_cn=verify;
+ set_tls_properties(ctx);
+ return ;
+}
+
+void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled) {
+ ctx->use_tcp_tls_keep_alive=enabled;
+}
+
+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);
+}
+
+#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);
+ ms_error("sal_get_default_local_ip() is deprecated.");
+}
+
+const char *sal_get_root_ca(Sal* ctx) {
+ return ctx->root_ca;
+}
+
+int sal_reset_transports(Sal *ctx){
+ ms_message("Reseting transports");
+ belle_sip_provider_clean_channels(ctx->prov);
+ return 0;
+}
+
+void sal_set_dscp(Sal *ctx, int dscp){
+ belle_sip_stack_set_default_dscp(ctx->stack,dscp);
+}
+
+void sal_set_send_error(Sal *sal,int value) {
+ belle_sip_stack_set_send_error(sal->stack,value);
+}
+void sal_set_recv_error(Sal *sal,int value) {
+ belle_sip_provider_set_recv_error(sal->prov,value);
+}
+void sal_nat_helper_enable(Sal *sal,bool_t enable) {
+ sal->nat_helper_enabled=enable;
+ belle_sip_provider_enable_nat_helper(sal->prov,enable);
+ ms_message("Sal nat helper [%s]",enable?"enabled":"disabled");
+}
+bool_t sal_nat_helper_enabled(Sal *sal) {
+ return sal->nat_helper_enabled;
+}
+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);
+}
+
+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_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_set_dns_user_hosts_file(Sal *sal, const char *hosts_file) {
+ belle_sip_stack_set_dns_user_hosts_file(sal->stack, hosts_file);
+}
+
+const char * sal_get_dns_user_hosts_file(const Sal *sal) {
+ return belle_sip_stack_get_dns_user_hosts_file(sal->stack);
+}
+
+SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) {
+ SalAuthInfo* auth_info = sal_auth_info_new();
+ auth_info->realm = ms_strdup(belle_sip_auth_event_get_realm(event));
+ auth_info->username = ms_strdup(belle_sip_auth_event_get_username(event));
+ auth_info->domain = ms_strdup(belle_sip_auth_event_get_domain(event));
+ auth_info->mode = (SalAuthMode)belle_sip_auth_event_get_mode(event);
+ return auth_info;
+}
+
+SalAuthMode sal_auth_info_get_mode(const SalAuthInfo* auth_info) { return auth_info->mode; }
+SalSigningKey *sal_auth_info_get_signing_key(const SalAuthInfo* auth_info) { return auth_info->key; }
+SalCertificatesChain *sal_auth_info_get_certificates_chain(const SalAuthInfo* auth_info) { return auth_info->certificates; }
+void sal_auth_info_set_mode(SalAuthInfo* auth_info, SalAuthMode mode) { auth_info->mode = mode; }
+void sal_certificates_chain_delete(SalCertificatesChain *chain) {
+ belle_sip_object_unref((belle_sip_object_t *)chain);
+}
+void sal_signing_key_delete(SalSigningKey *key) {
+ belle_sip_object_unref((belle_sip_object_t *)key);
+}
+
+const char* sal_op_type_to_string(const SalOpType type) {
+ switch(type) {
+ case SalOpRegister: return "SalOpRegister";
+ case SalOpCall: return "SalOpCall";
+ case SalOpMessage: return "SalOpMessage";
+ case SalOpPresence: return "SalOpPresence";
+ default:
+ return "SalOpUnknown";
+ }
+}
+
+void sal_use_dates(Sal *ctx, bool_t enabled){
+ ctx->use_dates=enabled;
+}
+
+int sal_auth_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]) {
+ return belle_sip_auth_helper_compute_ha1(userid, realm, password, ha1);
+}
+
+
+SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){
+ belle_sip_message_t *msg=(belle_sip_message_t*)ch;
+ belle_sip_header_t *h;
+
+ if (msg==NULL){
+ msg=(belle_sip_message_t*)belle_sip_request_new();
+ belle_sip_object_ref(msg);
+ }
+ h=belle_sip_header_create(name,value);
+ if (h==NULL){
+ belle_sip_error("Fail to parse custom header.");
+ return (SalCustomHeader*)msg;
+ }
+ belle_sip_message_add_header(msg,h);
+ return (SalCustomHeader*)msg;
+}
+
+const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name){
+ if (ch){
+ belle_sip_header_t *h=belle_sip_message_get_header((belle_sip_message_t*)ch,name);
+
+ if (h){
+ return belle_sip_header_get_unparsed_value(h);
+ }
+ }
+ return NULL;
+}
+
+void sal_custom_header_free(SalCustomHeader *ch){
+ if (ch==NULL) return;
+ belle_sip_object_unref((belle_sip_message_t*)ch);
+}
+
+SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch){
+ if (ch==NULL) return NULL;
+ return (SalCustomHeader*)belle_sip_object_ref((belle_sip_message_t*)ch);
+}
+
+const SalCustomHeader *sal_op_get_recv_custom_header(SalOp *op){
+ SalOpBase *b=(SalOpBase *)op;
+ return b->recv_custom_headers;
+}
+
+void sal_set_uuid(Sal *sal, const char *uuid){
+ if (sal->uuid){
+ ms_free(sal->uuid);
+ sal->uuid=NULL;
+ }
+ if (uuid)
+ sal->uuid=ms_strdup(uuid);
+}
+
+typedef struct {
+ unsigned int time_low;
+ unsigned short time_mid;
+ unsigned short time_hi_and_version;
+ unsigned char clock_seq_hi_and_reserved;
+ unsigned char clock_seq_low;
+ unsigned char node[6];
+} sal_uuid_t;
+
+
+int sal_create_uuid(Sal*ctx, char *uuid, size_t len){
+ sal_uuid_t uuid_struct;
+ int i;
+ int written;
+
+ if (len==0) return -1;
+ /*create an UUID as described in RFC4122, 4.4 */
+ belle_sip_random_bytes((unsigned char*)&uuid_struct, sizeof(sal_uuid_t));
+ uuid_struct.clock_seq_hi_and_reserved&=~(1<<6);
+ uuid_struct.clock_seq_hi_and_reserved|=1<<7;
+ uuid_struct.time_hi_and_version&=~(0xf<<12);
+ uuid_struct.time_hi_and_version|=4<<12;
+
+ 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){
+ 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;
+}
+
+static void make_supported_header(Sal *sal){
+ MSList *it;
+ char *alltags=NULL;
+ size_t buflen=64;
+ size_t written=0;
+
+ if (sal->supported){
+ belle_sip_object_unref(sal->supported);
+ sal->supported=NULL;
+ }
+ for(it=sal->supported_tags;it!=NULL;it=it->next){
+ 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);
+ }
+ if (alltags){
+ sal->supported=belle_sip_header_create("Supported",alltags);
+ if (sal->supported){
+ belle_sip_object_ref(sal->supported);
+ }
+ ms_free(alltags);
+ }
+}
+
+void sal_set_supported_tags(Sal *ctx, const char* tags){
+ ctx->supported_tags=ms_list_free_with_data(ctx->supported_tags,ms_free);
+ if (tags){
+ char *iter;
+ char *buffer=ms_strdup(tags);
+ char *tag;
+ char *context=NULL;
+ iter=buffer;
+ while((tag=strtok_r(iter,", ",&context))!=NULL){
+ iter=NULL;
+ ctx->supported_tags=ms_list_append(ctx->supported_tags,ms_strdup(tag));
+ }
+ ms_free(buffer);
+ }
+ make_supported_header(ctx);
+}
+
+const char *sal_get_supported_tags(Sal *ctx){
+ if (ctx->supported){
+ return belle_sip_header_get_unparsed_value(ctx->supported);
+ }
+ return NULL;
+}
+
+void sal_add_supported_tag(Sal *ctx, const char* tag){
+ MSList *elem=ms_list_find_custom(ctx->supported_tags,(MSCompareFunc)strcasecmp,tag);
+ if (!elem){
+ ctx->supported_tags=ms_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);
+ if (elem){
+ ms_free(elem->data);
+ ctx->supported_tags=ms_list_remove_link(ctx->supported_tags,elem);
+ make_supported_header(ctx);
+ }
+}
+
+
+
+belle_sip_response_t* sal_create_response_from_request ( Sal* sal, belle_sip_request_t* req, int code ) {
+ belle_sip_response_t *resp=belle_sip_response_create_from_request(req,code);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(sal->user_agent));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),sal->supported);
+ return resp;
+}
+
+void sal_set_refresher_retry_after(Sal *sal,int value) {
+ sal->refresher_retry_after=value;
+}
+
+int sal_get_refresher_retry_after(const Sal *sal) {
+ return sal->refresher_retry_after;
+}
+
+void sal_enable_auto_contacts(Sal *ctx, bool_t enabled){
+ ctx->auto_contacts=enabled;
+}
+
+void sal_enable_test_features(Sal*ctx, bool_t enabled){
+ ctx->enable_test_features=enabled;
+}
+
+void sal_use_no_initial_route(Sal *ctx, bool_t enabled){
+ ctx->no_initial_route=enabled;
+}
+
+SalResolverContext * sal_resolve_a(Sal* sal, const char *name, int port, int family, SalResolverCallback cb, void *data){
+ 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);
+}
+*/
+
+void sal_enable_unconditional_answer(Sal *sal,int value) {
+ belle_sip_provider_enable_unconditional_answer(sal->prov,value);
+}
+
+/** Parse a file containing either a certificate chain order in PEM format or a single DER cert
+ * @param auth_info structure where to store the result of parsing
+ * @param path path to certificate chain file
+ * @param format either PEM or DER
+ */
+void sal_certificates_chain_parse_file(SalAuthInfo* auth_info, const char* path, SalCertificateRawFormat format) {
+ auth_info->certificates = (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);
+}
+
+/**
+ * Parse a file containing either a private or public rsa key
+ * @param auth_info structure where to store the result of parsing
+ * @param passwd password (optionnal)
+ */
+void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const char *passwd) {
+ auth_info->key = (SalSigningKey *) belle_sip_signing_key_parse_file(path, passwd);
+ if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key);
+}
+
+/**
+ * Parse a directory to get a certificate with the given subject common name
+ *
+ */
+void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) {
+ belle_sip_certificates_chain_t *certificate = NULL;
+ belle_sip_signing_key_t *key = NULL;
+ *certificate_pem = NULL;
+ *key_pem = NULL;
+ if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) {
+ *certificate_pem = belle_sip_certificates_chain_get_pem(certificate);
+ *key_pem = belle_sip_signing_key_get_pem(key);
+ ms_message("Retrieve certificate with CN=%s successful\n", subject);
+ } else {
+ if (generate_certificate == TRUE) {
+ if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) {
+ *certificate_pem = belle_sip_certificates_chain_get_pem(certificate);
+ *key_pem = belle_sip_signing_key_get_pem(key);
+ ms_message("Generate self-signed certificate with CN=%s successful\n", subject);
+ }
+ }
+ }
+ /* generate the fingerprint as described in RFC4572 if needed */
+ if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) {
+ if (*fingerprint != NULL) {
+ ms_free(*fingerprint);
+ }
+ *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate);
+ }
+
+ /* free key and certificate */
+ if ( certificate != NULL ) {
+ belle_sip_object_unref(certificate);
+ }
+ if ( key != NULL ) {
+ belle_sip_object_unref(key);
+ }
+}
+
+unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){
+ return belle_sip_random_bytes(ret,size);
+}
+
+char *sal_get_random_token(int size){
+ return belle_sip_random_token(ms_malloc(size),size);
+}
+
+unsigned int sal_get_random(void){
+ unsigned int ret=0;
+ belle_sip_random_bytes((unsigned char*)&ret,4);
+ return ret;
+}
+
+belle_sip_source_t * sal_create_timer(Sal *sal, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms, const char* timer_name) {
+ belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack);
+ return belle_sip_main_loop_create_timeout(ml, func, data, timeout_value_ms, timer_name);
+}
+
+void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer) {
+ belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack);
+ belle_sip_main_loop_remove_source(ml, timer);
+}
+
+unsigned long sal_begin_background_task(const char *name, void (*max_time_reached)(void *), void *data){
+ return belle_sip_begin_background_task(name, max_time_reached, data);
+}
+
+void sal_end_background_task(unsigned long id){
+ belle_sip_end_background_task(id);
+}
+
+
+void sal_enable_sip_update_method(Sal *ctx,bool_t value) {
+ ctx->enable_sip_update=value;
+}
+
+void sal_default_set_sdp_handling(Sal *sal, SalOpSDPHandling sdp_handling_method) {
+ if (sdp_handling_method != SalOpSDPNormal ) ms_message("Enabling special SDP handling for all new SalOp in Sal[%p]!", sal);
+ sal->default_sdp_handling = sdp_handling_method;
+}
+
+bool_t sal_pending_trans_checking_enabled(const Sal *sal) {
+ return sal->pending_trans_checking;
+}
+
+int sal_enable_pending_trans_checking(Sal *sal, bool_t value) {
+ sal->pending_trans_checking = value;
+ return 0;
+}
diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h
new file mode 100644
index 000000000..b75be9156
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_impl.h
@@ -0,0 +1,175 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SAL_IMPL_H_
+#define SAL_IMPL_H_
+
+#include "sal/sal.h"
+#include "belle-sip/belle-sip.h"
+#include "belle-sip/belle-sdp.h"
+
+struct Sal{
+ SalCallbacks callbacks;
+ MSList *pending_auths;/*MSList of SalOp */
+ belle_sip_stack_t* stack;
+ belle_sip_provider_t *prov;
+ belle_sip_header_user_agent_t* user_agent;
+ belle_sip_listener_t *listener;
+ void *tunnel_client;
+ void *up; /*user pointer*/
+ int session_expires;
+ unsigned int keep_alive;
+ char *root_ca;
+ char *uuid;
+ int refresher_retry_after; /*retry after value for refresher*/
+ MSList *supported_tags;/*list of char * */
+ belle_sip_header_t *supported;
+ bool_t one_matching_codec;
+ bool_t use_tcp_tls_keep_alive;
+ bool_t nat_helper_enabled;
+ bool_t tls_verify;
+ bool_t tls_verify_cn;
+ bool_t use_dates;
+ bool_t auto_contacts;
+ bool_t enable_test_features;
+ bool_t no_initial_route;
+ bool_t enable_sip_update; /*true by default*/
+ SalOpSDPHandling default_sdp_handling;
+ bool_t pending_trans_checking; /*testing purpose*/
+};
+
+typedef enum SalOpState {
+ SalOpStateEarly=0
+ ,SalOpStateActive
+ ,SalOpStateTerminating /*this state is used to wait until a proceeding state, so we can send the cancel*/
+ ,SalOpStateTerminated
+}SalOpState;
+
+const char* sal_op_state_to_string(SalOpState value);
+
+typedef enum SalOpDir {
+ SalOpDirIncoming=0
+ ,SalOpDirOutgoing
+}SalOpDir;
+typedef enum SalOpType {
+ SalOpUnknown,
+ SalOpRegister,
+ SalOpCall,
+ SalOpMessage,
+ SalOpPresence,
+ SalOpPublish,
+ SalOpSubscribe
+}SalOpType;
+
+const char* sal_op_type_to_string(SalOpType type);
+
+struct SalOp{
+ SalOpBase base;
+ const belle_sip_listener_callbacks_t *callbacks;
+ SalErrorInfo error_info;
+ belle_sip_client_transaction_t *pending_auth_transaction;
+ belle_sip_server_transaction_t* pending_server_trans;
+ belle_sip_server_transaction_t* pending_update_server_trans;
+ belle_sip_client_transaction_t* pending_client_trans;
+ SalAuthInfo* auth_info;
+ belle_sip_dialog_t* dialog;
+ belle_sip_header_replaces_t *replaces;
+ belle_sip_header_referred_by_t *referred_by;
+ SalMediaDescription *result;
+ belle_sdp_session_description_t *sdp_answer;
+ SalOpState state;
+ SalOpDir dir;
+ belle_sip_refresher_t* refresher;
+ int ref;
+ SalOpType type;
+ SalPrivacyMask privacy;
+ belle_sip_header_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;
+ bool_t auto_answer_asked;
+ bool_t sdp_offering;
+ bool_t call_released;
+ bool_t manual_refresher;
+ bool_t has_auth_pending;
+ bool_t supports_session_timers;
+};
+
+
+belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescription *sal);
+int sdp_to_media_description(belle_sdp_session_description_t *sdp, SalMediaDescription *desc);
+belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method);
+
+
+void sal_op_call_fill_cbs(SalOp*op);
+void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog);
+
+/*return reffed op*/
+SalOp* sal_op_ref(SalOp* op);
+/*return null, destroy op if ref count =0*/
+void* sal_op_unref(SalOp* op);
+void sal_op_release_impl(SalOp *op);
+
+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);
+void sal_op_resend_request(SalOp* op, belle_sip_request_t* request);
+int sal_op_send_and_create_refresher(SalOp* op,belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener );
+belle_sip_response_t *sal_op_create_response_from_request(SalOp *op, belle_sip_request_t *req, int code);
+
+/*
+ * return true if both from and to uri are sips
+ * */
+bool_t sal_op_is_secure(const SalOp* op);
+
+void sal_process_authentication(SalOp *op);
+belle_sip_header_contact_t* sal_op_create_contact(SalOp *op) ;
+
+bool_t _sal_compute_sal_errors(belle_sip_response_t* response, SalReason* sal_reason, char* reason, size_t reason_size);
+SalReason _sal_reason_from_sip_code(int code);
+
+void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response);
+/*presence*/
+void sal_op_presence_fill_cbs(SalOp*op);
+/*messaging*/
+void sal_op_message_fill_cbs(SalOp*op);
+void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event);
+void sal_op_subscribe_fill_cbs(SalOp*op);
+
+/*call transfer*/
+void sal_op_process_refer(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *tr);
+void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *tr);
+/*create SalAuthInfo by copying username and realm from suth event*/
+SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) ;
+void sal_add_pending_auth(Sal *sal, SalOp *op);
+void sal_remove_pending_auth(Sal *sal, SalOp *op);
+void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence);
+
+belle_sip_response_t *sal_create_response_from_request(Sal *sal, belle_sip_request_t *req, int code);
+
+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);
+
+SalReason sal_reason_to_sip_code(SalReason r);
+
+void _sal_op_add_custom_headers(SalOp *op, 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
new file mode 100644
index 000000000..5474540ea
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_call.c
@@ -0,0 +1,1042 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+#include "offeranswer.h"
+
+static int extract_sdp(SalOp* op,belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error);
+
+/*used for calls terminated before creation of a dialog*/
+static void call_set_released(SalOp* op){
+ if (!op->call_released){
+ op->state=SalOpStateTerminated;
+ op->base.root->callbacks.call_released(op);
+ op->call_released=TRUE;
+ /*be aware that the following line may destroy the op*/
+ set_or_update_dialog(op,NULL);
+ }
+}
+
+static void call_set_error(SalOp* op,belle_sip_response_t* response){
+ sal_op_set_error_info_from_response(op,response);
+ op->base.root->callbacks.call_failure(op);
+}
+static void set_addr_to_0000(char value[]) {
+ if (ms_is_ipv6(value)) {
+ strcpy(value,"::0");
+ } else {
+ strcpy(value,"0.0.0.0");
+ }
+ return;
+}
+static void sdp_process(SalOp *h){
+ ms_message("Doing SDP offer/answer process of type %s",h->sdp_offering ? "outgoing" : "incoming");
+ if (h->result){
+ sal_media_description_unref(h->result);
+ h->result = NULL;
+ }
+
+ /* if SDP was invalid */
+ if (h->base.remote_media == NULL) return;
+
+ h->result=sal_media_description_new();
+ if (h->sdp_offering){
+ offer_answer_initiate_outgoing(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);
+ /*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);
+ }
+ }
+ h->sdp_answer=(belle_sdp_session_description_t *)belle_sip_object_ref(media_description_to_sdp(h->result));
+ /*once we have generated the SDP answer, we modify the result description for processing by the upper layer.
+ It should contains media parameters constraint from the remote offer, not our response*/
+ strcpy(h->result->addr,h->base.remote_media->addr);
+ h->result->bandwidth=h->base.remote_media->bandwidth;
+
+ for(i=0;iresult->nb_streams;++i){
+ /*copy back parameters from remote description that we need in our result description*/
+ if (h->result->streams[i].rtp_port!=0){ /*if stream was accepted*/
+ strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr);
+ h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime;
+ h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth;
+ h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port;
+ 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)) {
+ h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0];
+ }
+ }
+ }
+ }
+}
+
+static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) {
+ belle_sip_header_content_type_t* content_type ;
+ belle_sip_header_content_length_t* content_length;
+ belle_sip_error_code error = BELLE_SIP_BUFFER_OVERFLOW;
+ size_t length = 0;
+
+ if (session_desc) {
+ size_t bufLen = 2048;
+ size_t hardlimit = 16*1024; /* 16k SDP limit seems reasonable */
+ char* buff = belle_sip_malloc(bufLen);
+ content_type = belle_sip_header_content_type_create("application","sdp");
+
+ /* try to marshal the description. This could go higher than 2k so we iterate */
+ while( error != BELLE_SIP_OK && bufLen <= hardlimit && buff != NULL){
+ error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length);
+ if( error != BELLE_SIP_OK ){
+ bufLen *= 2;
+ length = 0;
+ buff = belle_sip_realloc(buff,bufLen);
+ }
+ }
+ /* give up if hard limit reached */
+ if (error != BELLE_SIP_OK || buff == NULL) {
+ ms_error("Buffer too small (%d) or not enough memory, giving up SDP", (int)bufLen);
+ return -1;
+ }
+
+ content_length = belle_sip_header_content_length_create(length);
+ belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type));
+ belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length));
+ belle_sip_message_assign_body(msg,buff,length);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+static int set_sdp_from_desc(belle_sip_message_t *msg, const SalMediaDescription *desc){
+ int err;
+ belle_sdp_session_description_t *sdp=media_description_to_sdp(desc);
+ err=set_sdp(msg,sdp);
+ belle_sip_object_unref(sdp);
+ return err;
+
+}
+static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
+ SalOp* op=(SalOp*)user_ctx;
+
+ if (op->state==SalOpStateTerminated) return;
+
+ if (!op->dialog) {
+ /*call terminated very early*/
+ sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO error",NULL);
+ op->base.root->callbacks.call_failure(op);
+ call_set_released(op);
+ } else {
+ /*dialog will terminated shortly, nothing to do*/
+ }
+}
+static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
+ SalOp* op=(SalOp*)ctx;
+
+ if (op->dialog && op->dialog==belle_sip_dialog_terminated_event_get_dialog(event)) {
+ /*belle_sip_transaction_t* trans=belle_sip_dialog_get_last_transaction(op->dialog);*/
+ 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_CONFIRMED:
+ if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) {
+ /*this is probably a normal termination from a BYE*/
+ op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
+ op->state=SalOpStateTerminating;
+ }
+ break;
+ default:
+ break;
+ }
+ belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack)
+ ,(belle_sip_callback_t) call_set_released
+ , op);
+ } else {
+ ms_error("dialog unknown for op ");
+ }
+}
+
+static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
+ belle_sdp_session_description_t* sdp;
+ SalReason reason;
+ if (op->base.remote_media){
+ sal_media_description_unref(op->base.remote_media);
+ op->base.remote_media=NULL;
+ }
+ if (extract_sdp(op,BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) {
+ if (sdp){
+ op->base.remote_media=sal_media_description_new();
+ sdp_to_media_description(sdp,op->base.remote_media);
+ }/*if no sdp in response, what can we do ?*/
+ }
+ /* process sdp in any case to reset result media description*/
+ if (op->base.local_media) sdp_process(op);
+}
+
+static void cancelling_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);
+ op->state=SalOpStateTerminating;
+}
+
+static int vfu_retry (void *user_data, unsigned int events) {
+ SalOp *op=(SalOp *)user_data;
+ sal_call_send_vfu_request(op);
+ sal_op_unref(op);
+ return BELLE_SIP_STOP;
+}
+
+static void call_process_response(void *op_base, const belle_sip_response_event_t *event){
+ SalOp* op = (SalOp*)op_base;
+ belle_sip_request_t* ack;
+ belle_sip_dialog_state_t dialog_state;
+ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
+ belle_sip_request_t* req;
+ belle_sip_response_t* response=belle_sip_response_event_get_response(event);
+ int code = belle_sip_response_get_status_code(response);
+ belle_sip_header_content_type_t *header_content_type=NULL;
+ belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event);
+ const char *method;
+
+ if (!client_transaction) {
+ ms_warning("Discarding stateless response [%i] on op [%p]",code,op);
+ return;
+ }
+ req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+ set_or_update_dialog(op,dialog);
+ dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL;
+ method=belle_sip_request_get_method(req);
+ ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state));
+ /*to make sure no cb will destroy op*/
+ sal_op_ref(op);
+ switch(dialog_state) {
+ case BELLE_SIP_DIALOG_NULL:
+ case BELLE_SIP_DIALOG_EARLY: {
+ if (strcmp("INVITE",method)==0 ) {
+ if (op->state == SalOpStateTerminating) {
+ /*check if CANCEL was sent before*/
+ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) {
+ /*it wasn't sent */
+ if (code<200) {
+ cancelling_invite(op);
+ }else{
+ /* no need to send the INVITE because the UAS rejected the INVITE*/
+ if (op->dialog==NULL) call_set_released(op);
+ }
+ } else {
+ /*it was sent already, so just expect the 487 or any error response to send the call_released() notification*/
+ if (code>=300){
+ if (op->dialog==NULL) call_set_released(op);
+ }
+ }
+ } else if (code >= 180 && code<200) {
+ belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response");
+ if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){
+ handle_sdp_from_response(op,response);
+ op->base.root->callbacks.call_ringing(op);
+ }
+ 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);
+ if (op->dialog==NULL) call_set_released(op);
+ }
+ } else if (code >=200
+ && code<300
+ && strcmp("UPDATE",belle_sip_request_get_method(req))==0) {
+ handle_sdp_from_response(op,response);
+ op->base.root->callbacks.call_accepted(op);
+ }
+ }
+ break;
+ case BELLE_SIP_DIALOG_CONFIRMED: {
+ switch (op->state) {
+ case SalOpStateEarly:/*invite case*/
+ case SalOpStateActive: /*re-invite, INFO, UPDATE case*/
+ if (strcmp("INVITE",method)==0){
+ 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) {
+ ms_error("This call has been already terminated.");
+ return ;
+ }
+ if (op->sdp_answer){
+ set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer);
+ belle_sip_object_unref(op->sdp_answer);
+ op->sdp_answer=NULL;
+ }
+ 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);
+ }
+ }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);
+ 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);
+ }else {
+ /*ignoring*/
+ }
+ }else if (strcmp("UPDATE",method)==0){
+ op->base.root->callbacks.call_accepted(op); /*INVITE*/
+ }
+ break;
+ case SalOpStateTerminating:
+ sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE"));
+ break;
+ case SalOpStateTerminated:
+ default:
+ ms_error("Call op [%p] receives unexpected answer [%i] while in state [%s].",op,code, sal_op_state_to_string(op->state));
+ }
+ }
+ break;
+ case BELLE_SIP_DIALOG_TERMINATED: {
+ if (strcmp("INVITE",method)==0 && code >= 300){
+ call_set_error(op,response);
+ }
+ }
+ break;
+ default: {
+ ms_error("call op [%p] receive answer [%i] not implemented",op,code);
+ }
+ break;
+ }
+ sal_op_unref(op);
+}
+
+static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
+ SalOp* op=(SalOp*)user_ctx;
+
+ if (op->state==SalOpStateTerminated) return;
+
+ if (!op->dialog) {
+ /*call terminated very early*/
+ sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
+ op->base.root->callbacks.call_failure(op);
+ call_set_released(op);
+ } else {
+ /*dialog will terminated shortly, nothing to do*/
+ }
+}
+
+static void call_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
+ SalOp* op = (SalOp*)user_ctx;
+ belle_sip_client_transaction_t *client_transaction=belle_sip_transaction_terminated_event_get_client_transaction(event);
+ 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;
+ bool_t release_call=FALSE;
+
+ if (client_transaction) {
+ req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+ resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(client_transaction));
+ } else {
+ 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
+ && 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))
+ && op->dialog==NULL) {
+ release_call=TRUE;
+ }
+ if (server_transaction){
+ if (op->pending_server_trans==server_transaction){
+ belle_sip_object_unref(op->pending_server_trans);
+ op->pending_server_trans=NULL;
+ }
+ if (op->pending_update_server_trans==server_transaction){
+ belle_sip_object_unref(op->pending_update_server_trans);
+ op->pending_update_server_trans=NULL;
+ }
+ }
+ if (release_call) call_set_released(op);
+}
+
+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->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);
+}
+
+static void unsupported_method(belle_sip_server_transaction_t* server_transaction,belle_sip_request_t* request) {
+ belle_sip_response_t* resp;
+ resp=belle_sip_response_create_from_request(request,501);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ return;
+}
+
+/*
+ * Extract the sdp from a sip message.
+ * If there is no body in the message, the session_desc is set to null, 0 is returned.
+ * If body was present is not a SDP or parsing of SDP failed, -1 is returned and SalReason is set appropriately.
+ *
+**/
+static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error) {
+ const char *body;
+ belle_sip_header_content_type_t* content_type;
+
+ if (op&&op->sdp_handling == SalOpSDPSimulateError){
+ ms_error("Simulating SDP parsing error for op %p", op);
+ *session_desc=NULL;
+ *error=SalReasonNotAcceptable;
+ return -1;
+ } else if( op && op->sdp_handling == SalOpSDPSimulateRemove){
+ ms_error("Simulating no SDP for op %p", op);
+ *session_desc = NULL;
+ return 0;
+ }
+
+ body = belle_sip_message_get_body(message);
+ if(body == NULL) {
+ *session_desc = NULL;
+ return 0;
+ }
+
+ content_type = belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t);
+ if (content_type){
+ if (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("sdp",belle_sip_header_content_type_get_subtype(content_type))==0) {
+ *session_desc=belle_sdp_session_description_parse(body);
+ if (*session_desc==NULL) {
+ ms_error("Failed to parse SDP message.");
+ *error=SalReasonNotAcceptable;
+ return -1;
+ }
+ }else{
+ *error=SalReasonUnsupportedContent;
+ return -1;
+ }
+ }else *session_desc=NULL;
+ return 0;
+}
+
+static int is_media_description_acceptable(SalMediaDescription *md){
+ if (md->nb_streams==0){
+ ms_warning("Media description does not define any stream.");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+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;
+ op->base.remote_media=sal_media_description_new();
+ sdp_to_media_description(sdp,op->base.remote_media);
+ /*make some sanity check about the SDP received*/
+ if (!is_media_description_acceptable(op->base.remote_media)){
+ err=-1;
+ reason=SalReasonNotAcceptable;
+ }
+ belle_sip_object_unref(sdp);
+ }else op->sdp_offering=TRUE; /*INVITE without SDP*/
+ }else err=-1;
+
+ if (err==-1){
+ sal_call_decline(op,reason,NULL);
+ }
+ return err;
+}
+
+static void sal_op_reset_descriptions(SalOp *op) {
+ if (op->base.remote_media){
+ sal_media_description_unref(op->base.remote_media);
+ op->base.remote_media=NULL;
+ }
+ if (op->result){
+ sal_media_description_unref(op->result);
+ op->result=NULL;
+ }
+}
+static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
+ SalOp* op = (SalOp*)op_base;
+ belle_sip_server_transaction_t* server_transaction=NULL;
+ belle_sdp_session_description_t* sdp;
+ belle_sip_request_t* req = belle_sip_request_event_get_request(event);
+ belle_sip_dialog_state_t dialog_state;
+ belle_sip_response_t* resp;
+ belle_sip_header_t* call_info;
+ const char *method=belle_sip_request_get_method(req);
+ bool_t is_update=FALSE;
+
+ if (strcmp("ACK",method)!=0){ /*ACK does'nt create srv 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));
+ }
+
+ if (strcmp("INVITE",method)==0) {
+ if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
+ /*updating pending invite transaction*/
+ op->pending_server_trans=server_transaction;
+ belle_sip_object_ref(op->pending_server_trans);
+ }
+
+ if (strcmp("UPDATE",method)==0) {
+ if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans);
+ /*updating pending update transaction*/
+ op->pending_update_server_trans=server_transaction;
+ belle_sip_object_ref(op->pending_update_server_trans);
+ }
+
+ if (!op->dialog) {
+ set_or_update_dialog(op,belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(op->pending_server_trans)));
+ ms_message("new incoming call from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
+ }
+ dialog_state=belle_sip_dialog_get_state(op->dialog);
+ switch(dialog_state) {
+ case BELLE_SIP_DIALOG_NULL: {
+ if (strcmp("INVITE",method)==0) {
+ if (!op->replaces && (op->replaces=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_replaces_t))) {
+ belle_sip_object_ref(op->replaces);
+ } else if(op->replaces) {
+ ms_warning("replace header already set");
+ }
+
+ if (process_sdp_for_invite(op,req) == 0) {
+ if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) {
+ if( strstr(belle_sip_header_get_unparsed_value(call_info),"answer-after=") != NULL) {
+ op->auto_answer_asked=TRUE;
+ ms_message("The caller asked to automatically answer the call(Emergency?)\n");
+ }
+ }
+ op->base.root->callbacks.call_received(op);
+ }
+ break;
+ } /* else same behavior as for EARLY state*/
+ }
+ 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*/
+ belle_sip_server_transaction_send_response(server_transaction
+ ,sal_op_create_response_from_request(op,req,200));
+ /*terminate invite transaction*/
+ call_terminated(op
+ ,op->pending_server_trans
+ ,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),487);
+
+
+ } else {
+ /*call leg does not exist*/
+ belle_sip_server_transaction_send_response(server_transaction
+ ,sal_op_create_response_from_request(op,req,481));
+ }
+ } else if (strcmp("PRACK",method)==0) {
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ } else if (strcmp("UPDATE",method)==0) {
+ sal_op_reset_descriptions(op);
+ if (process_sdp_for_invite(op,req)==0)
+ op->base.root->callbacks.call_updating(op,TRUE);
+ } else {
+ belle_sip_error("Unexpected method [%s] for dialog state BELLE_SIP_DIALOG_EARLY",belle_sip_request_get_method(req));
+ unsupported_method(server_transaction,req);
+ }
+ break;
+ }
+ case BELLE_SIP_DIALOG_CONFIRMED:
+ /*great ACK received*/
+ if (strcmp("ACK",method)==0) {
+ if (!op->pending_client_trans ||
+ !belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state((belle_sip_transaction_t*)op->pending_client_trans))){
+ if (op->sdp_offering){
+ SalReason reason;
+ if (extract_sdp(op,BELLE_SIP_MESSAGE(req),&sdp,&reason)==0){
+ if (sdp){
+ if (op->base.remote_media)
+ sal_media_description_unref(op->base.remote_media);
+ op->base.remote_media=sal_media_description_new();
+ sdp_to_media_description(sdp,op->base.remote_media);
+ sdp_process(op);
+ belle_sip_object_unref(sdp);
+ }else{
+ ms_warning("SDP expected in ACK but not found.");
+ }
+ }
+ }
+ op->base.root->callbacks.call_ack(op);
+ }else{
+ ms_message("Ignored received ack since a new client transaction has been started since.");
+ }
+ } else if(strcmp("BYE",method)==0) {
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
+ op->state=SalOpStateTerminating;
+ /*call end not notified by dialog deletion because transaction can end before dialog*/
+ } else if(strcmp("INVITE",method)==0 || (is_update=(strcmp("UPDATE",method)==0)) ) {
+ if (is_update && !belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) {
+ /*session timer case*/
+ /*session expire should be handled. to be done when real session timer (rfc4028) will be implemented*/
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ belle_sip_object_unref(op->pending_update_server_trans);
+ op->pending_update_server_trans=NULL;
+ } else {
+ /*re-invite*/
+ sal_op_reset_descriptions(op);
+ if (process_sdp_for_invite(op,req)==0)
+ op->base.root->callbacks.call_updating(op,is_update);
+ }
+ } else if (strcmp("INFO",method)==0){
+ if (belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))
+ && strstr(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)),"picture_fast_update")) {
+ /*vfu request*/
+ ms_message("Receiving VFU request on op [%p]",op);
+ if (op->base.root->callbacks.vfu_request){
+ op->base.root->callbacks.vfu_request(op);
+
+ }
+ }else{
+ SalBody salbody;
+ if (sal_op_get_body(op,(belle_sip_message_t*)req,&salbody)) {
+ if (sal_body_has_type(&salbody,"application","dtmf-relay")){
+ char tmp[10];
+ if (sal_lines_get_value(salbody.data, "Signal",tmp, sizeof(tmp))){
+ op->base.root->callbacks.dtmf_received(op,tmp[0]);
+ }
+ }else
+ op->base.root->callbacks.info_received(op,&salbody);
+ } else {
+ op->base.root->callbacks.info_received(op,NULL);
+ }
+ }
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ }else if (strcmp("REFER",method)==0) {
+ sal_op_process_refer(op,event,server_transaction);
+ } else if (strcmp("NOTIFY",method)==0) {
+ sal_op_call_process_notify(op,event,server_transaction);
+ } else if (strcmp("OPTIONS",method)==0) {
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ } else if (strcmp("CANCEL",method)==0) {
+ /*call leg does not exist because 200ok already sent*/
+ belle_sip_server_transaction_send_response(server_transaction,sal_op_create_response_from_request(op,req,481));
+ } else if (strcmp("MESSAGE",method)==0){
+ sal_process_incoming_message(op,event);
+ }else{
+ ms_error("unexpected method [%s] for dialog [%p]",belle_sip_request_get_method(req),op->dialog);
+ unsupported_method(server_transaction,req);
+ }
+ break;
+ default:
+ ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
+ break;
+ }
+
+ if (server_transaction) belle_sip_object_unref(server_transaction);
+
+}
+
+
+/*Call API*/
+int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){
+ if (desc)
+ sal_media_description_ref(desc);
+ if (op->base.local_media)
+ sal_media_description_unref(op->base.local_media);
+ op->base.local_media=desc;
+
+ if (op->base.remote_media){
+ /*case of an incoming call where we modify the local capabilities between the time
+ * the call is ringing and it is accepted (for example if you want to accept without video*/
+ /*reset the sdp answer so that it is computed again*/
+ if (op->sdp_answer){
+ belle_sip_object_unref(op->sdp_answer);
+ op->sdp_answer=NULL;
+ }
+ }
+ return 0;
+}
+
+static belle_sip_header_allow_t *create_allow(bool_t enable_update){
+ belle_sip_header_allow_t* header_allow;
+ char allow [256];
+ snprintf(allow,sizeof(allow),"INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO%s",(enable_update?", UPDATE":""));
+ header_allow = belle_sip_header_allow_create(allow);
+ return header_allow;
+}
+
+static void sal_op_fill_invite(SalOp *op, belle_sip_request_t* invite) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(create_allow(op->base.root->enable_sip_update)));
+
+ if (op->base.root->session_expires!=0){
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "600;refresher=uas"));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Supported", "timer"));
+ }
+ if (op->base.local_media){
+ op->sdp_offering=TRUE;
+ set_sdp_from_desc(BELLE_SIP_MESSAGE(invite),op->base.local_media);
+ }else op->sdp_offering=FALSE;
+ return;
+}
+
+int sal_call(SalOp *op, const char *from, const char *to){
+ belle_sip_request_t* invite;
+ op->dir=SalOpDirOutgoing;
+
+ sal_op_set_from(op,from);
+ sal_op_set_to(op,to);
+
+ ms_message("[%s] calling [%s] on op [%p]", from, to, op);
+ invite=sal_op_build_request(op,"INVITE");
+
+ if( invite == NULL ){
+ /* can happen if the op has an invalid address */
+ return -1;
+ }
+
+ sal_op_fill_invite(op,invite);
+
+ sal_op_call_fill_cbs(op);
+ if (op->replaces){
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->replaces));
+ }
+ if (op->referred_by)
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->referred_by));
+
+ return sal_op_send_request(op,invite);
+}
+
+static belle_sip_listener_callbacks_t call_op_callbacks={0};
+
+void sal_op_call_fill_cbs(SalOp*op) {
+ if (call_op_callbacks.process_response_event==NULL){
+ call_op_callbacks.process_io_error=call_process_io_error;
+ call_op_callbacks.process_response_event=call_process_response;
+ call_op_callbacks.process_timeout=call_process_timeout;
+ call_op_callbacks.process_transaction_terminated=call_process_transaction_terminated;
+ call_op_callbacks.process_request_event=process_request_event;
+ call_op_callbacks.process_dialog_terminated=process_dialog_terminated;
+ }
+ op->callbacks=&call_op_callbacks;
+ op->type=SalOpCall;
+}
+
+static void handle_offer_answer_response(SalOp* op, belle_sip_response_t* response) {
+ if (op->base.local_media){
+ /*this is the case where we received an invite without SDP*/
+ if (op->sdp_offering) {
+ set_sdp_from_desc(BELLE_SIP_MESSAGE(response),op->base.local_media);
+ }else{
+
+ if ( op->sdp_answer==NULL )
+ {
+ if( op->sdp_handling == SalOpSDPSimulateRemove ){
+ ms_warning("Simulating SDP removal in answer for op %p", op);
+ } else {
+ sdp_process(op);
+ }
+ }
+
+ if (op->sdp_answer){
+ set_sdp(BELLE_SIP_MESSAGE(response),op->sdp_answer);
+ belle_sip_object_unref(op->sdp_answer);
+ op->sdp_answer=NULL;
+ }
+ }
+ }else{
+ ms_error("You are accepting a call but not defined any media capabilities !");
+ }
+}
+
+int sal_call_notify_ringing(SalOp *op, bool_t early_media){
+ int status_code =early_media?183:180;
+ belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans));
+ belle_sip_response_t* ringing_response = sal_op_create_response_from_request(op,req,status_code);
+ belle_sip_header_t *require;
+ const char *tags=NULL;
+
+ if (early_media){
+ handle_offer_answer_response(op,ringing_response);
+ }
+ require=belle_sip_message_get_header((belle_sip_message_t*)req,"Require");
+ if (require) tags=belle_sip_header_get_unparsed_value(require);
+ /* if client requires 100rel, then add necessary stuff*/
+ if (tags && strstr(tags,"100rel")!=0) {
+ belle_sip_message_add_header((belle_sip_message_t*)ringing_response,belle_sip_header_create("Require","100rel"));
+ belle_sip_message_add_header((belle_sip_message_t*)ringing_response,belle_sip_header_create("RSeq","1"));
+ }
+
+#ifndef SAL_OP_CALL_FORCE_CONTACT_IN_RINGING
+ if (tags && strstr(tags,"100rel")!=0)
+#endif
+ {
+ belle_sip_header_address_t* contact= (belle_sip_header_address_t*)sal_op_get_contact_address(op);
+ belle_sip_header_contact_t* contact_header;
+ if (contact && (contact_header=belle_sip_header_contact_create(contact))) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(ringing_response),BELLE_SIP_HEADER(contact_header));
+ }
+ }
+ belle_sip_server_transaction_send_response(op->pending_server_trans,ringing_response);
+ return 0;
+}
+
+
+/*accept an incoming call or, during a call accept a reINVITE*/
+int sal_call_accept(SalOp*h){
+ belle_sip_response_t *response;
+ belle_sip_header_contact_t* contact_header;
+ belle_sip_server_transaction_t* transaction;
+
+ /*first check if an UPDATE transaction need to be accepted*/
+ if (h->pending_update_server_trans) {
+ transaction=h->pending_update_server_trans;
+ } else if (h->pending_server_trans) {
+ /*so it must be an invite/re-invite*/
+ transaction=h->pending_server_trans;
+ } else {
+ ms_error("No transaction to accept for op [%p]",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;
+ }
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(create_allow(h->base.root->enable_sip_update)));
+ if (h->base.root->session_expires!=0){
+/* if (h->supports_session_timers) {*/
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create("Supported", "timer"));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create( "Session-expires", "600;refresher=uac"));
+ /*}*/
+ }
+
+ if ((contact_header=sal_op_create_contact(h))) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact_header));
+ }
+
+ _sal_op_add_custom_headers(h, BELLE_SIP_MESSAGE(response));
+
+ handle_offer_answer_response(h,response);
+
+ belle_sip_server_transaction_send_response(transaction,response);
+ if (h->pending_update_server_trans) {
+ belle_sip_object_unref(h->pending_update_server_trans);
+ h->pending_update_server_trans=NULL;
+ }
+ return 0;
+}
+
+int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*optional*/){
+ belle_sip_response_t* response;
+ belle_sip_header_contact_t* contact=NULL;
+ int status=sal_reason_to_sip_code(reason);
+ belle_sip_transaction_t *trans;
+
+ if (reason==SalReasonRedirect){
+ if (redirection!=NULL) {
+ if (strstr(redirection,"sip:")!=0) status=302;
+ else status=380;
+ contact= belle_sip_header_contact_new();
+ belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact),belle_sip_uri_parse(redirection));
+ } else {
+ ms_error("Cannot redirect to null");
+ }
+ }
+ trans=(belle_sip_transaction_t*)op->pending_server_trans;
+ if (!trans) trans=(belle_sip_transaction_t*)op->pending_update_server_trans;
+ if (!trans){
+ ms_error("sal_call_decline(): no pending transaction to decline.");
+ return -1;
+ }
+ response = sal_op_create_response_from_request(op,belle_sip_transaction_get_request(trans),status);
+ if (contact) belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact));
+ belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(trans),response);
+ return 0;
+}
+
+int sal_call_update(SalOp *op, const char *subject, bool_t no_user_consent){
+ belle_sip_request_t *update;
+ belle_sip_dialog_state_t state=belle_sip_dialog_get_state(op->dialog);
+ belle_sip_dialog_enable_pending_trans_checking(op->dialog,op->base.root->pending_trans_checking);
+
+ /*check for dialog state*/
+ if ( state == BELLE_SIP_DIALOG_CONFIRMED) {
+ if (no_user_consent)
+ update=belle_sip_dialog_create_request(op->dialog,"UPDATE");
+ else
+ update=belle_sip_dialog_create_request(op->dialog,"INVITE");
+ } else if (state == BELLE_SIP_DIALOG_EARLY) {
+ update=belle_sip_dialog_create_request(op->dialog,"UPDATE");
+ } else {
+ ms_error("Cannot update op [%p] with dialog [%p] in state [%s]",op, op->dialog,belle_sip_dialog_state_to_string(state));
+ return -1;
+ }
+ if (update){
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(update),belle_sip_header_create( "Subject", subject));
+ sal_op_fill_invite(op, update);
+ return sal_op_send_request(op,update);
+ }
+ /*it failed why ?*/
+ if (belle_sip_dialog_request_pending(op->dialog))
+ sal_error_info_set(&op->error_info,SalReasonRequestPending,491,NULL,NULL);
+ else
+ sal_error_info_set(&op->error_info,SalReasonUnknown,500,NULL,NULL);
+ return -1;
+}
+
+SalMediaDescription * sal_call_get_remote_media_description(SalOp *h){
+ return h->base.remote_media;;
+}
+
+SalMediaDescription * sal_call_get_final_media_description(SalOp *h){
+ if (h->base.local_media && h->base.remote_media && !h->result){
+ sdp_process(h);
+ }
+ return h->result;
+}
+
+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;
+ char dtmf_body[128]={0};
+
+ snprintf(dtmf_body, sizeof(dtmf_body)-1, "Signal=%c\r\nDuration=250\r\n", dtmf);
+ bodylen=strlen(dtmf_body);
+ belle_sip_message_set_body((belle_sip_message_t*)req,dtmf_body,bodylen);
+ belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)belle_sip_header_content_length_create(bodylen));
+ belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)belle_sip_header_content_type_create("application", "dtmf-relay"));
+ sal_op_send_request(h,req);
+ }else ms_error("sal_call_send_dtmf(): could not build request");
+ }else ms_error("sal_call_send_dtmf(): no dialog");
+ return 0;
+}
+
+int sal_call_terminate(SalOp *op){
+ belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
+ if (op->state==SalOpStateTerminating || op->state==SalOpStateTerminated) {
+ ms_error("Cannot terminate op [%p] in state [%s]",op,sal_op_state_to_string(op->state));
+ return -1;
+ }
+ switch(dialog_state) {
+ case BELLE_SIP_DIALOG_CONFIRMED: {
+ sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE"));
+ op->state=SalOpStateTerminating;
+ break;
+ }
+ case BELLE_SIP_DIALOG_NULL: {
+ if (op->dir == SalOpDirIncoming) {
+ sal_call_decline(op, SalReasonDeclined,NULL);
+ op->state=SalOpStateTerminated;
+ } else if (op->pending_client_trans){
+ if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){
+ cancelling_invite(op);
+ }else{
+ /* Case where the CANCEL cannot be sent because no provisional response was received so far.
+ * The Op must be kept for the time of the transaction in case a response is received later.
+ * The state is passed to Terminating to remember to terminate later.
+ */
+ op->state=SalOpStateTerminating;
+ }
+ }
+ break;
+ }
+ case BELLE_SIP_DIALOG_EARLY: {
+ if (op->dir == SalOpDirIncoming) {
+ sal_call_decline(op, SalReasonDeclined,NULL);
+ op->state=SalOpStateTerminated;
+ } else {
+ cancelling_invite(op);
+ }
+ break;
+ }
+ default: {
+ ms_error("sal_call_terminate not implemented yet for dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+bool_t sal_call_autoanswer_asked(SalOp *op){
+ return op->auto_answer_asked;
+}
+
+void sal_call_send_vfu_request(SalOp *op){
+ char info_body[] =
+ ""
+ ""
+ " "
+ " "
+ " "
+ " "
+ " "
+ "";
+ size_t content_lenth = sizeof(info_body) - 1;
+ belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; /*no dialog = dialog in NULL state*/
+ if (dialog_state == BELLE_SIP_DIALOG_CONFIRMED) {
+ belle_sip_request_t* info = belle_sip_dialog_create_queued_request(op->dialog,"INFO");
+ int error=TRUE;
+ if (info) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","media_control+xml")));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_lenth)));
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(info),info_body,content_lenth);
+ error=sal_op_send_request(op,info);
+ }
+ if (error)
+ ms_warning("Cannot send vfu request to [%s] ", sal_op_get_to(op));
+
+ } else {
+ ms_warning("Cannot send vfu request to [%s] because dialog [%p] in wrong state [%s]",sal_op_get_to(op)
+ ,op->dialog
+ ,belle_sip_dialog_state_to_string(dialog_state));
+ }
+
+ return ;
+}
+
+int sal_call_is_offerer(const SalOp *h){
+ return h->sdp_offering;
+}
+
+
+
+
diff --git a/coreapi/bellesip_sal/sal_op_call_transfer.c b/coreapi/bellesip_sal/sal_op_call_transfer.c
new file mode 100644
index 000000000..84e529091
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_call_transfer.c
@@ -0,0 +1,254 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+#include "offeranswer.h"
+
+
+
+/*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);
+ }
+ op->referred_by=referred_by;
+ belle_sip_object_ref(op->referred_by);
+}
+
+
+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*/
+ 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);
+ belle_sip_free(tmp);
+ return -1;
+ }
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(refer_to));
+ if (referred_by) belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(referred_by));
+ return sal_op_send_request(op,req);
+}
+
+int sal_call_refer(SalOp *op, const char *refer_to){
+ belle_sip_header_address_t *referred_by;
+ belle_sip_header_refer_to_t* refer_to_header;
+ if (op->dialog) {
+ referred_by=(belle_sip_header_address_t*)belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_dialog_get_local_party(op->dialog)));
+ }else{
+ referred_by=BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op));
+ }
+ refer_to_header=belle_sip_header_refer_to_create(belle_sip_header_address_parse(refer_to));
+
+ return sal_call_refer_to(op,refer_to_header,belle_sip_header_referred_by_create(referred_by));
+}
+
+int sal_call_refer_with_replaces(SalOp *op, SalOp *other_call_op){
+ belle_sip_dialog_state_t other_call_dialog_state=other_call_op->dialog?belle_sip_dialog_get_state(other_call_op->dialog):BELLE_SIP_DIALOG_NULL;
+ belle_sip_dialog_state_t op_dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
+ belle_sip_header_replaces_t* replaces;
+ belle_sip_header_refer_to_t* refer_to;
+ belle_sip_header_referred_by_t* referred_by;
+ const char* from_tag;
+ const char* to_tag;
+ char* escaped_replaces;
+ /*first, build refer to*/
+ if (other_call_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) {
+ ms_error(" wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED",belle_sip_dialog_state_to_string(other_call_dialog_state)
+ ,other_call_op);
+ return -1;
+ }
+ if (op_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) {
+ ms_error(" wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED",belle_sip_dialog_state_to_string(op_dialog_state)
+ ,op);
+ return -1;
+ }
+
+ 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);;
+ }
+ 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);
+ belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)),"Replaces",escaped_replaces);
+ belle_sip_free(escaped_replaces);
+ referred_by=belle_sip_header_referred_by_create(belle_sip_dialog_get_local_party(op->dialog));
+ belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(referred_by));
+ return sal_call_refer_to(op,refer_to,referred_by);
+}
+/*
+int sal_call_accept_refer(SalOp *h){
+ ms_fatal("sal_call_accept_refer not implemented yet");
+ return -1;
+}*/
+/*informs this call is consecutive to an incoming refer */
+int sal_call_set_referer(SalOp *h, SalOp *refered_call){
+ if (refered_call->replaces)
+ sal_op_set_replaces(h,refered_call->replaces);
+ if (refered_call->referred_by)
+ sal_op_set_referred_by(h,refered_call->referred_by);
+ return 0;
+}
+/* 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){
+ 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));
+
+ if (dialog) {
+ return (SalOp*)belle_sip_dialog_get_application_data(dialog);
+ }
+ }
+ return NULL;
+}
+
+static int send_notify_for_refer(SalOp* op, int code, const char *reason){
+ belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY");
+ char *sipfrag=belle_sip_strdup_printf("SIP/2.0 %i %s\r\n",code,reason);
+ size_t content_length=strlen(sipfrag);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
+ ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,-1)));
+
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),belle_sip_header_create("Event","refer"));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_type_create("message","sipfrag")));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length)));
+ belle_sip_message_assign_body(BELLE_SIP_MESSAGE(notify),sipfrag,content_length);
+ return sal_op_send_request(op,notify);
+}
+
+static void notify_last_response(SalOp *op, SalOp *newcall){
+ belle_sip_client_transaction_t *tr=newcall->pending_client_trans;
+ belle_sip_response_t *resp=NULL;
+ if (tr){
+ resp=belle_sip_transaction_get_response((belle_sip_transaction_t*)tr);
+ }
+ if (resp==NULL){
+ send_notify_for_refer(op, 100, "Trying");
+ }else{
+ send_notify_for_refer(op, belle_sip_response_get_status_code(resp), belle_sip_response_get_reason_phrase(resp));
+ }
+}
+
+int sal_call_notify_refer_state(SalOp *op, SalOp *newcall){
+ belle_sip_dialog_state_t state;
+ if(belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_TERMINATED){
+ return 0;
+ }
+ state = newcall->dialog?belle_sip_dialog_get_state(newcall->dialog):BELLE_SIP_DIALOG_NULL;
+ switch(state) {
+ case BELLE_SIP_DIALOG_EARLY:
+ send_notify_for_refer(op, 100, "Trying");
+ break;
+ case BELLE_SIP_DIALOG_CONFIRMED:
+ send_notify_for_refer(op, 200, "Ok");
+ break;
+ case BELLE_SIP_DIALOG_TERMINATED:
+ case BELLE_SIP_DIALOG_NULL:
+ notify_last_response(op,newcall);
+ break;
+ }
+ return 0;
+}
+
+
+void sal_op_process_refer(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t *server_transaction){
+ belle_sip_request_t* req = belle_sip_request_event_get_request(event);
+ belle_sip_header_refer_to_t *refer_to= belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_refer_to_t);
+ belle_sip_header_referred_by_t *referred_by= belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_referred_by_t);
+ belle_sip_response_t* resp;
+ belle_sip_uri_t* refer_to_uri;
+ char* refer_to_uri_str;
+
+ ms_message("Receiving REFER request on op [%p]",op);
+ if (refer_to) {
+ refer_to_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to));
+
+ if (refer_to_uri && belle_sip_uri_get_header(refer_to_uri,"Replaces")) {
+ sal_op_set_replaces(op,belle_sip_header_replaces_create2(belle_sip_uri_get_header(refer_to_uri,"Replaces")));
+ belle_sip_uri_remove_header(refer_to_uri,"Replaces");
+ }
+ if (referred_by){
+ sal_op_set_referred_by(op,referred_by);
+ }
+ refer_to_uri_str=belle_sip_uri_to_string(refer_to_uri);
+ resp = sal_op_create_response_from_request(op,req,202);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ op->base.root->callbacks.refer_received(op->base.root,op,refer_to_uri_str);
+ belle_sip_free(refer_to_uri_str);
+ } else {
+ ms_warning("cannot do anything with the refer without destination\n");
+ resp = sal_op_create_response_from_request(op,req,400);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ }
+
+}
+
+void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *event, belle_sip_server_transaction_t* server_transaction){
+ belle_sip_request_t* req = belle_sip_request_event_get_request(event);
+ const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
+ belle_sip_header_t* header_event=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event");
+ belle_sip_header_content_type_t* content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t);
+ belle_sip_response_t* resp;
+
+ ms_message("Receiving NOTIFY request on op [%p]",op);
+ if (header_event
+ && strncasecmp(belle_sip_header_get_unparsed_value(header_event),"refer",strlen("refer"))==0
+ && content_type
+ && strcmp(belle_sip_header_content_type_get_type(content_type),"message")==0
+ && strcmp(belle_sip_header_content_type_get_subtype(content_type),"sipfrag")==0
+ && body){
+ belle_sip_response_t* sipfrag=BELLE_SIP_RESPONSE(belle_sip_message_parse(body));
+
+ if (sipfrag){
+ int code=belle_sip_response_get_status_code(sipfrag);
+ SalReferStatus status=SalReferFailed;
+ if (code<200){
+ status=SalReferTrying;
+ }else if (code<300){
+ status=SalReferSuccess;
+ }else if (code>=400){
+ status=SalReferFailed;
+ }
+ belle_sip_object_unref(sipfrag);
+ resp = sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ op->base.root->callbacks.notify_refer(op,status);
+ }
+ }else{
+ ms_error("Notify without sipfrag, trashing");
+ resp = sal_op_create_response_from_request(op,req,501);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ }
+}
+
diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c
new file mode 100644
index 000000000..d4a24bd9b
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_events.c
@@ -0,0 +1,276 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+SalSubscribeStatus get_subscription_state(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){
+ if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED)==0)
+ sss=SalSubscribeTerminated;
+ else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_PENDING)==0)
+ sss=SalSubscribePending;
+ else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE)==0)
+ sss=SalSubscribeActive;
+ }
+ return sss;
+}
+
+static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
+ ,void* user_pointer
+ ,unsigned int status_code
+ ,const char* reason_phrase) {
+ 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);*/
+ SalSubscribeStatus sss=SalSubscribeTerminated;
+
+ ms_message("Subscribe refresher [%i] reason [%s] ",status_code,reason_phrase?reason_phrase:"none");
+ if (status_code>=200 && status_code<300){
+ 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);
+ }else if (status_code==0){
+ op->base.root->callbacks.on_expire(op);
+ }
+
+}
+
+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");
+}
+
+static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
+ SalOp* op= (SalOp*)ctx;
+ if (op->dialog) {
+ op->dialog=NULL;
+ sal_op_unref(op);
+ }
+}
+
+static void subscribe_response_event(void *op_base, const belle_sip_response_event_t *event){
+}
+
+static void subscribe_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
+}
+
+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){
+ 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;
+ belle_sip_server_transaction_t* server_transaction = op->pending_server_trans;
+
+ 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;
+ sal_op_ref(op);
+ op->base.root->callbacks.notify(op,sub_state,eventname,body);
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ sal_op_unref(op);
+}
+
+static void subscribe_process_request_event(void *op_base, const belle_sip_request_event_t *event) {
+ SalOp* op = (SalOp*)op_base;
+ 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_header_t *event_header;
+ SalBody body;
+ belle_sip_response_t* resp;
+ const char *eventname=NULL;
+ const char *method=belle_sip_request_get_method(req);
+
+ 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);
+
+ 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);
+ return;
+ }
+ if (op->event==NULL) {
+ op->event=event_header;
+ belle_sip_object_ref(op->event);
+ }
+ eventname=belle_sip_header_get_unparsed_value(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);
+ 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);
+ return;
+ }
+ }
+ dialog_state=belle_sip_dialog_get_state(op->dialog);
+ switch(dialog_state) {
+
+ case BELLE_SIP_DIALOG_NULL: {
+ op->base.root->callbacks.subscribe_received(op,eventname,body.type ? &body : NULL);
+ break;
+ }
+ case BELLE_SIP_DIALOG_EARLY:
+ ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",belle_sip_request_get_method(req),op->dialog);
+ break;
+
+ case BELLE_SIP_DIALOG_CONFIRMED:
+ if (strcmp("NOTIFY",method)==0) {
+ handle_notify(op,req,eventname,&body);
+ } else if (strcmp("SUBSCRIBE",method)==0) {
+ /*either a refresh of an unsubscribe*/
+ if (expires && belle_sip_header_expires_get_expires(expires)>0) {
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ } 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);
+ op->base.root->callbacks.subscribe_closed(op);
+ }
+ }
+ break;
+ default: {
+ ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
+ }
+ }
+}
+
+static belle_sip_listener_callbacks_t op_subscribe_callbacks={ 0 };
+
+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;
+ op_subscribe_callbacks.process_response_event=subscribe_response_event;
+ op_subscribe_callbacks.process_timeout=subscribe_process_timeout;
+ op_subscribe_callbacks.process_transaction_terminated=subscribe_process_transaction_terminated;
+ op_subscribe_callbacks.process_request_event=subscribe_process_request_event;
+ op_subscribe_callbacks.process_dialog_terminated=subscribe_process_dialog_terminated;
+ }
+ op->callbacks=&op_subscribe_callbacks;
+ op->type=SalOpSubscribe;
+}
+
+
+int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body){
+ belle_sip_request_t *req=NULL;
+
+ if (from)
+ sal_op_set_from(op,from);
+ if (to)
+ sal_op_set_to(op,to);
+
+ 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);
+ 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);
+ 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);
+ return belle_sip_refresher_refresh(op->refresher,expires);
+ }
+ ms_warning("sal_subscribe(): no dialog and no refresher ?");
+ return -1;
+}
+
+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_refresher_refresh(op->refresher,0);
+ return 0;
+ }
+ return -1;
+}
+
+int sal_subscribe_accept(SalOp *op){
+ belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans));
+ belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);
+ belle_sip_response_t* resp = sal_op_create_response_from_request(op,req,200);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(expires));
+ belle_sip_server_transaction_send_response(op->pending_server_trans,resp);
+ 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));
+ belle_sip_server_transaction_send_response(op->pending_server_trans,resp);
+ return 0;
+}
+
+int sal_notify(SalOp *op, const SalBody *body){
+ 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);
+
+ 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);
+ return sal_op_send_request(op,notify);
+}
+
+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);
+ 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
new file mode 100644
index 000000000..d19091c96
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_impl.c
@@ -0,0 +1,819 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+/*create an operation */
+SalOp * sal_op_new(Sal *sal){
+ SalOp *op=ms_new0(SalOp,1);
+ __sal_op_init(op,sal);
+ op->type=SalOpUnknown;
+ op->privacy=SalPrivacyNone;
+ op->manual_refresher=FALSE;/*tells that requests with expiry (SUBSCRIBE, PUBLISH) will be automatically refreshed*/
+ op->sdp_handling=sal->default_sdp_handling;
+ sal_op_ref(op);
+ return op;
+}
+
+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->refresher) {
+ belle_sip_refresher_stop(op->refresher);
+ }
+ sal_op_unref(op);
+}
+
+void sal_op_release_impl(SalOp *op){
+ ms_message("Destroying op [%p] of type [%s]",op,sal_op_type_to_string(op->type));
+ if (op->pending_auth_transaction) belle_sip_object_unref(op->pending_auth_transaction);
+ sal_remove_pending_auth(op->base.root,op);
+ if (op->auth_info) {
+ sal_auth_info_delete(op->auth_info);
+ }
+ if (op->sdp_answer) belle_sip_object_unref(op->sdp_answer);
+ if (op->refresher) {
+ belle_sip_object_unref(op->refresher);
+ op->refresher=NULL;
+ }
+ if (op->result)
+ sal_media_description_unref(op->result);
+ if(op->replaces) belle_sip_object_unref(op->replaces);
+ if(op->referred_by) belle_sip_object_unref(op->referred_by);
+
+ if (op->pending_client_trans) belle_sip_object_unref(op->pending_client_trans);
+ if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
+ if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans);
+ if (op->event) belle_sip_object_unref(op->event);
+ sal_error_info_reset(&op->error_info);
+ __sal_op_free(op);
+ return ;
+}
+
+void sal_op_authenticate(SalOp *op, const SalAuthInfo *info){
+ if (op->type == SalOpRegister) {
+ /*Registration authenticate is just about registering again*/
+ sal_register_refresh(op,-1);
+ }else {
+ /*for sure auth info will be accesible from the provider*/
+ sal_process_authentication(op);
+ }
+ return ;
+}
+
+void sal_op_cancel_authentication(SalOp *h){
+ ms_fatal("sal_op_cancel_authentication not implemented yet");
+ return ;
+}
+
+SalAuthInfo * sal_op_get_auth_requested(SalOp *op){
+ return op->auth_info;
+}
+
+belle_sip_header_contact_t* sal_op_create_contact(SalOp *op){
+ belle_sip_header_contact_t* contact_header;
+ belle_sip_uri_t* contact_uri;
+
+ if (sal_op_get_contact_address(op)) {
+ contact_header = belle_sip_header_contact_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_contact_address(op)));
+ } else {
+ contact_header= belle_sip_header_contact_new();
+ }
+
+ if (!(contact_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact_header)))) {
+ /*no uri, just creating a new one*/
+ contact_uri=belle_sip_uri_new();
+ belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact_header),contact_uri);
+ }
+
+ belle_sip_uri_set_user_password(contact_uri,NULL);
+ belle_sip_uri_set_secure(contact_uri,sal_op_is_secure(op));
+ if (op->privacy!=SalPrivacyNone){
+ belle_sip_uri_set_user(contact_uri,NULL);
+ }
+ belle_sip_header_contact_set_automatic(contact_header,op->base.root->auto_contacts);
+ if (op->base.root->uuid){
+ if (belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance")==0){
+ char *instance_id=belle_sip_strdup_printf("\"\"",op->base.root->uuid);
+ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance",instance_id);
+ belle_sip_free(instance_id);
+ }
+ }
+ return contact_header;
+}
+
+
+
+static void add_initial_route_set(belle_sip_request_t *request, const MSList *list){
+ const MSList *elem;
+ for (elem=list;elem!=NULL;elem=elem->next){
+ SalAddress *addr=(SalAddress*)elem->data;
+ belle_sip_header_route_t *route;
+ belle_sip_uri_t *uri;
+ /*Optimization: if the initial route set only contains one URI which is the same as the request URI, ommit it*/
+ if (elem==list && list->next==NULL){
+ belle_sip_uri_t *requri=belle_sip_request_get_uri(request);
+ /*skip the first route it is the same as the request uri*/
+ if (strcmp(sal_address_get_domain(addr),belle_sip_uri_get_host(requri))==0 ){
+ ms_message("Skipping top route of initial route-set because same as request-uri.");
+ continue;
+ }
+ }
+
+ route=belle_sip_header_route_create((belle_sip_header_address_t*)addr);
+ uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)route);
+ belle_sip_uri_set_lr_param(uri,1);
+ belle_sip_message_add_header((belle_sip_message_t*)request,(belle_sip_header_t*)route);
+ }
+}
+
+belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) {
+ belle_sip_header_from_t* from_header;
+ belle_sip_header_to_t* to_header;
+ belle_sip_provider_t* prov=op->base.root->prov;
+ belle_sip_request_t *req;
+ belle_sip_uri_t* req_uri;
+ belle_sip_uri_t* to_uri;
+
+ const SalAddress* to_address;
+ const MSList *elem=sal_op_get_route_addresses(op);
+ char token[10];
+
+ /* check that the op has a correct to address */
+ to_address = sal_op_get_to_address(op);
+ if( to_address == NULL ){
+ 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");
+ return NULL;
+ }
+
+ if (strcmp("REGISTER",method)==0 || op->privacy==SalPrivacyNone) {
+ from_header = belle_sip_header_from_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op))
+ ,belle_sip_random_token(token,sizeof(token)));
+ } else {
+ from_header=belle_sip_header_from_create2("Anonymous ",belle_sip_random_token(token,sizeof(token)));
+ }
+ /*make sure to preserve components like headers or port*/
+
+ req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)to_uri);
+ belle_sip_uri_set_secure(req_uri,sal_op_is_secure(op));
+
+ to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(to_address),NULL);
+
+ req=belle_sip_request_create(
+ req_uri,
+ method,
+ belle_sip_provider_create_call_id(prov),
+ belle_sip_header_cseq_create(20,method),
+ from_header,
+ to_header,
+ belle_sip_header_via_new(),
+ 70);
+
+ if (op->privacy & SalPrivacyId) {
+ belle_sip_header_p_preferred_identity_t* p_preferred_identity=belle_sip_header_p_preferred_identity_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op)));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(p_preferred_identity));
+ }
+
+ if (elem && strcmp(method,"REGISTER")!=0 && !op->base.root->no_initial_route){
+ add_initial_route_set(req,elem);
+ }
+
+ if (strcmp("REGISTER",method)!=0 && op->privacy!=SalPrivacyNone ){
+ belle_sip_header_privacy_t* privacy_header=belle_sip_header_privacy_new();
+ if (op->privacy&SalPrivacyCritical)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyCritical));
+ if (op->privacy&SalPrivacyHeader)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyHeader));
+ if (op->privacy&SalPrivacyId)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyId));
+ if (op->privacy&SalPrivacyNone)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyNone));
+ if (op->privacy&SalPrivacySession)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacySession));
+ if (op->privacy&SalPrivacyUser)
+ belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyUser));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(privacy_header));
+ }
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->base.root->supported);
+ return req;
+}
+
+belle_sip_response_t *sal_op_create_response_from_request(SalOp *op, belle_sip_request_t *req, int code){
+ return sal_create_response_from_request(op->base.root,req,code);
+}
+
+/*ping: main purpose is to obtain its own contact address behind firewalls*/
+int sal_ping(SalOp *op, const char *from, const char *to){
+ sal_op_set_from(op,from);
+ sal_op_set_to(op,to);
+ return sal_op_send_request(op,sal_op_build_request(op,"OPTIONS"));
+}
+
+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];
+ if (user_agent && belle_sip_header_user_agent_get_products_as_string(user_agent,user_agent_string,sizeof(user_agent_string))>0) {
+ if (op->base.remote_ua!=NULL){
+ ms_free(op->base.remote_ua);
+ }
+ op->base.remote_ua=ms_strdup(user_agent_string);
+ }
+}
+
+int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires) {
+ belle_sip_header_expires_t* expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_EXPIRES);
+
+ if (!expires_header && expires>=0) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header=belle_sip_header_expires_new()));
+ }
+ if (expires_header) belle_sip_header_expires_set_expires(expires_header,expires);
+ return sal_op_send_request(op,request);
+}
+
+void sal_op_resend_request(SalOp* op, belle_sip_request_t* request) {
+ belle_sip_header_cseq_t* cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CSEQ);
+ belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1);
+ sal_op_send_request(op,request);
+}
+
+static void add_headers(SalOp *op, belle_sip_header_t *h, belle_sip_message_t *msg){
+
+ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_contact_t)){
+ belle_sip_header_contact_t* newct;
+ /*special case for contact, we want to keep everything from the custom contact but set automatic mode and add our own parameters as well*/
+ sal_op_set_contact_address(op,(SalAddress*)BELLE_SIP_HEADER_ADDRESS(h));
+ newct = sal_op_create_contact(op);
+ belle_sip_message_set_header(BELLE_SIP_MESSAGE(msg),BELLE_SIP_HEADER(newct));
+ return;
+ }
+ /*if a header already exists in the message, replace it*/
+ belle_sip_message_set_header(msg,h);
+
+}
+
+void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg){
+ if (op->base.sent_custom_headers){
+ belle_sip_message_t *ch=(belle_sip_message_t*)op->base.sent_custom_headers;
+ belle_sip_list_t *l=belle_sip_message_get_all_headers(ch);
+ belle_sip_list_t *elem;
+ for(elem=l;elem!=NULL;elem=elem->next){
+ add_headers(op,(belle_sip_header_t*)elem->data,msg);
+ }
+ belle_sip_list_free(l);
+ }
+}
+
+static int _sal_op_send_request_with_contact(SalOp* op, belle_sip_request_t* request, bool_t add_contact) {
+ belle_sip_client_transaction_t* client_transaction;
+ belle_sip_provider_t* prov=op->base.root->prov;
+ belle_sip_uri_t* outbound_proxy=NULL;
+ belle_sip_header_contact_t* contact;
+ int result =-1;
+ belle_sip_uri_t *next_hop_uri=NULL;
+
+ if (add_contact && !belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_contact_t)) {
+ contact = sal_op_create_contact(op);
+ belle_sip_message_set_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(contact));
+ } /*keep existing*/
+
+ _sal_op_add_custom_headers(op, (belle_sip_message_t*)request);
+
+ if (!op->dialog || belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_NULL) {
+ /*don't put route header if dialog is in confirmed state*/
+ const MSList *elem=sal_op_get_route_addresses(op);
+ const char *transport;
+ const char *method=belle_sip_request_get_method(request);
+ belle_sip_listening_point_t *udplp=belle_sip_provider_get_listening_point(prov,"UDP");
+
+ if (elem) {
+ outbound_proxy=belle_sip_header_address_get_uri((belle_sip_header_address_t*)elem->data);
+ next_hop_uri=outbound_proxy;
+ }else{
+ next_hop_uri=(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_request_get_uri(request));
+ }
+ transport=belle_sip_uri_get_transport_param(next_hop_uri);
+ if (transport==NULL){
+ /*compatibility mode: by default it should be udp as not explicitely set and if no udp listening point is available, then use
+ * the first available transport*/
+ if (!belle_sip_uri_is_secure(next_hop_uri)){
+ if (udplp==NULL){
+ if (belle_sip_provider_get_listening_point(prov,"TCP")!=NULL){
+ transport="tcp";
+ }else if (belle_sip_provider_get_listening_point(prov,"TLS")!=NULL ){
+ transport="tls";
+ }
+ }
+ if (transport){
+ belle_sip_message("Transport is not specified, using %s because UDP is not available.",transport);
+ belle_sip_uri_set_transport_param(next_hop_uri,transport);
+ }
+ }
+ }else{
+#ifdef TUNNEL_ENABLED
+ if (udplp && BELLE_SIP_OBJECT_IS_INSTANCE_OF(udplp,belle_sip_tunnel_listening_point_t)){
+ /* our tunnel mode only supports UDP. Force transport to be set to UDP */
+ belle_sip_uri_set_transport_param(next_hop_uri,"udp");
+ }
+#endif
+ }
+ 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*/
+ belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_via_t);
+ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(via),"alias",NULL);
+ }
+ }
+
+ client_transaction = belle_sip_provider_create_client_transaction(prov,request);
+ belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),sal_op_ref(op));
+ if (op->pending_client_trans) belle_sip_object_unref(op->pending_client_trans);
+ op->pending_client_trans=client_transaction; /*update pending inv for being able to cancel*/
+ belle_sip_object_ref(op->pending_client_trans);
+
+ if (belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_user_agent_t)==NULL)
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(op->base.root->user_agent));
+
+ if (!belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION)
+ && !belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION)) {
+ /*hmm just in case we already have authentication param in cache*/
+ belle_sip_provider_add_authorization(op->base.root->prov,request,NULL,NULL,NULL,op->base.realm);
+ }
+ result = belle_sip_client_transaction_send_request_to(client_transaction,next_hop_uri/*might be null*/);
+
+ /*update call id if not set yet for this OP*/
+ if (result == 0 && !op->base.call_id) {
+ op->base.call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request), belle_sip_header_call_id_t))));
+ }
+
+ return result;
+
+}
+
+int sal_op_send_request(SalOp* op, belle_sip_request_t* request) {
+ bool_t need_contact=FALSE;
+ if (request==NULL) {
+ return -1; /*sanity check*/
+ }
+ /*
+ Header field where proxy ACK BYE CAN INV OPT REG
+ ___________________________________________________________
+ Contact R o - - m o o
+ */
+ if (strcmp(belle_sip_request_get_method(request),"INVITE")==0
+ ||strcmp(belle_sip_request_get_method(request),"REGISTER")==0
+ ||strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0
+ ||strcmp(belle_sip_request_get_method(request),"OPTIONS")==0
+ ||strcmp(belle_sip_request_get_method(request),"REFER")==0) /* Despite contact seems not mandatory, call flow example show a Contact in REFER requests*/
+ need_contact=TRUE;
+
+ return _sal_op_send_request_with_contact(op, request,need_contact);
+}
+
+SalReason sal_reason_to_sip_code(SalReason r){
+ int ret=500;
+ switch(r){
+ case SalReasonNone:
+ ret=200;
+ break;
+ case SalReasonIOError:
+ ret=503;
+ break;
+ case SalReasonUnknown:
+ ret=400;
+ break;
+ case SalReasonBusy:
+ ret=486;
+ break;
+ case SalReasonDeclined:
+ ret=603;
+ break;
+ case SalReasonDoNotDisturb:
+ ret=600;
+ break;
+ case SalReasonForbidden:
+ ret=403;
+ break;
+ case SalReasonUnsupportedContent:
+ ret=415;
+ break;
+ case SalReasonNotFound:
+ ret=404;
+ break;
+ case SalReasonRedirect:
+ ret=302;
+ break;
+ case SalReasonTemporarilyUnavailable:
+ ret=480;
+ break;
+ case SalReasonServiceUnavailable:
+ ret=503;
+ break;
+ case SalReasonRequestPending:
+ ret=491;
+ break;
+ case SalReasonUnauthorized:
+ ret=401;
+ break;
+ case SalReasonNotAcceptable:
+ ret=488; /*or maybe 606 Not Acceptable ?*/
+ break;
+ case SalReasonNoMatch:
+ ret=481;
+ break;
+ case SalReasonRequestTimeout:
+ ret=408;
+ break;
+ case SalReasonMovedPermanently:
+ ret=301;
+ break;
+ case SalReasonGone:
+ ret=410;
+ break;
+ case SalReasonAddressIncomplete:
+ ret=484;
+ break;
+ case SalReasonNotImplemented:
+ ret=501;
+ break;
+ case SalReasonServerTimeout:
+ ret=504;
+ break;
+ case SalReasonBadGateway:
+ ret=502;
+ break;
+ case SalReasonInternalError:
+ ret=500;
+ break;
+ }
+ return ret;
+}
+
+SalReason _sal_reason_from_sip_code(int code) {
+ if (code>=100 && code<300) return SalReasonNone;
+
+ switch(code) {
+ case 0:
+ return SalReasonIOError;
+ case 301:
+ return SalReasonMovedPermanently;
+ case 302:
+ return SalReasonRedirect;
+ case 401:
+ case 407:
+ return SalReasonUnauthorized;
+ case 403:
+ return SalReasonForbidden;
+ case 404:
+ return SalReasonNotFound;
+ case 408:
+ return SalReasonRequestTimeout;
+ case 410:
+ return SalReasonGone;
+ case 415:
+ return SalReasonUnsupportedContent;
+ case 422:
+ ms_error ("422 not implemented yet");;
+ break;
+ case 480:
+ return SalReasonTemporarilyUnavailable;
+ case 481:
+ return SalReasonNoMatch;
+ case 484:
+ return SalReasonAddressIncomplete;
+ case 486:
+ return SalReasonBusy;
+ case 487:
+ return SalReasonNone;
+ case 488:
+ return SalReasonNotAcceptable;
+ case 491:
+ return SalReasonRequestPending;
+ case 500:
+ return SalReasonInternalError;
+ case 501:
+ return SalReasonNotImplemented;
+ case 502:
+ return SalReasonBadGateway;
+ case 504:
+ return SalReasonServerTimeout;
+ case 600:
+ return SalReasonDoNotDisturb;
+ case 603:
+ return SalReasonDeclined;
+ case 503:
+ return SalReasonServiceUnavailable;
+ default:
+ return SalReasonUnknown;
+ }
+ return SalReasonUnknown;
+}
+
+const SalErrorInfo *sal_error_info_none(void){
+ static SalErrorInfo none={
+ SalReasonNone,
+ "Ok",
+ 200,
+ NULL,
+ NULL
+ };
+ return &none;
+}
+
+void sal_error_info_reset(SalErrorInfo *ei){
+ if (ei->status_string){
+ ms_free(ei->status_string);
+ ei->status_string=NULL;
+ }
+ if (ei->warnings){
+ ms_free(ei->warnings);
+ ei->warnings=NULL;
+
+ }
+ if (ei->full_string){
+ ms_free(ei->full_string);
+ ei->full_string=NULL;
+ }
+ ei->protocol_code=0;
+ ei->reason=SalReasonNone;
+}
+
+void sal_error_info_set(SalErrorInfo *ei, SalReason reason, int code, const char *status_string, const char *warning){
+ sal_error_info_reset(ei);
+ if (reason==SalReasonUnknown) ei->reason=_sal_reason_from_sip_code(code);
+ else ei->reason=reason;
+ ei->protocol_code=code;
+ ei->status_string=status_string ? ms_strdup(status_string) : NULL;
+ ei->warnings=warning ? ms_strdup(warning) : NULL;
+ if (ei->status_string){
+ if (ei->warnings)
+ ei->full_string=ms_strdup_printf("%s %s",ei->status_string,ei->warnings);
+ else ei->full_string=ms_strdup(ei->status_string);
+ }
+}
+
+void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response){
+ int code = belle_sip_response_get_status_code(response);
+ const char *reason_phrase=belle_sip_response_get_reason_phrase(response);
+ /*Remark: the reason header is to be used mainly in SIP requests, thus the use and prototype of this function should be changed.*/
+ belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
+ belle_sip_header_t *warning=belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Warning");
+ SalErrorInfo *ei=&op->error_info;
+ const char *warnings;
+
+ warnings=warning ? belle_sip_header_get_unparsed_value(warning) : NULL;
+ if (warnings==NULL) warnings=reason_header ? belle_sip_header_get_unparsed_value(reason_header) : NULL;
+ sal_error_info_set(ei,SalReasonUnknown,code,reason_phrase,warnings);
+}
+
+const SalErrorInfo *sal_op_get_error_info(const SalOp *op){
+ return &op->error_info;
+}
+
+static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
+ belle_sip_dialog_set_application_data(dialog,NULL);
+ sal_op_unref(op);
+ belle_sip_object_unref(dialog);
+}
+
+static belle_sip_dialog_t *link_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
+ belle_sip_dialog_set_application_data(dialog,sal_op_ref(op));
+ belle_sip_object_ref(dialog);
+ return dialog;
+}
+
+void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {
+ ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]",op,op->dialog,dialog);
+ sal_op_ref(op);
+ if (op->dialog!=dialog){
+ if (op->dialog){
+ /*FIXME: shouldn't we delete unconfirmed dialogs ?*/
+ unlink_op_with_dialog(op,op->dialog);
+ op->dialog=NULL;
+ }
+ if (dialog) {
+ op->dialog=link_op_with_dialog(op,dialog);
+ belle_sip_dialog_enable_pending_trans_checking(dialog,op->base.root->pending_trans_checking);
+ }
+ }
+ sal_op_unref(op);
+}
+/*return reffed op*/
+SalOp* sal_op_ref(SalOp* op) {
+ op->ref++;
+ return op;
+}
+/*return null, destroy op if ref count =0*/
+void* sal_op_unref(SalOp* op) {
+ op->ref--;
+ if (op->ref==0) {
+ sal_op_release_impl(op);
+ }else if (op->ref<0){
+ ms_fatal("SalOp [%p]: too many unrefs.",op);
+ }
+ return NULL;
+}
+
+int sal_op_send_and_create_refresher(SalOp* op,belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener ) {
+ if (sal_op_send_request_with_expires(op,req,expires)==0) {
+ if (op->refresher) {
+ belle_sip_refresher_stop(op->refresher);
+ belle_sip_object_unref(op->refresher);
+ }
+ if ((op->refresher = belle_sip_client_transaction_create_refresher(op->pending_client_trans))) {
+ /*since refresher acquires the transaction, we should remove our context from the transaction, because we won't be notified
+ * that it is terminated anymore.*/
+ sal_op_unref(op);/*loose the reference that was given to the transaction when creating it*/
+ /* Note that the refresher will replace our data with belle_sip_transaction_set_application_data().
+ Something in the design is not very good here, it makes things complicated to the belle-sip user.
+ Possible ideas to improve things: refresher shall not use belle_sip_transaction_set_application_data() internally, refresher should let the first transaction
+ notify the user as a normal transaction*/
+ belle_sip_refresher_set_listener(op->refresher,listener,op);
+ belle_sip_refresher_set_retry_after(op->refresher,op->base.root->refresher_retry_after);
+ belle_sip_refresher_set_realm(op->refresher,op->base.realm);
+ belle_sip_refresher_enable_manual_mode(op->refresher,op->manual_refresher);
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+const char* sal_op_state_to_string(const SalOpState value) {
+ switch(value) {
+ case SalOpStateEarly: return"SalOpStateEarly";
+ case SalOpStateActive: return "SalOpStateActive";
+ case SalOpStateTerminating: return "SalOpStateTerminating";
+ case SalOpStateTerminated: return "SalOpStateTerminated";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * Warning: this function takes owneship of the custom headers
+ */
+void sal_op_set_sent_custom_header(SalOp *op, SalCustomHeader* ch){
+ SalOpBase *b=(SalOpBase *)op;
+ if (b->sent_custom_headers){
+ sal_custom_header_free(b->sent_custom_headers);
+ b->sent_custom_headers=NULL;
+ }
+ if (ch) belle_sip_object_ref((belle_sip_message_t*)ch);
+ b->sent_custom_headers=ch;
+}
+
+void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming){
+ if (incoming) belle_sip_object_ref(incoming);
+ if (op->base.recv_custom_headers){
+ belle_sip_object_unref(op->base.recv_custom_headers);
+ op->base.recv_custom_headers=NULL;
+ }
+ if (incoming){
+ op->base.recv_custom_headers=(SalCustomHeader*)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));
+ }
+ }
+}
+
+
+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;
+}
+
+void sal_op_set_privacy(SalOp* op,SalPrivacyMask privacy) {
+ op->privacy=privacy;
+}
+SalPrivacyMask sal_op_get_privacy(const SalOp* op) {
+ return op->privacy;
+}
+
+bool_t sal_op_is_secure(const SalOp* op) {
+ const SalAddress* from = sal_op_get_from_address(op);
+ const SalAddress* to = sal_op_get_to_address(op);
+
+ return from && to && strcasecmp("sips",sal_address_get_scheme(from))==0 && strcasecmp("sips",sal_address_get_scheme(to))==0;
+}
+
+void sal_op_set_manual_refresher_mode(SalOp *op, bool_t enabled){
+ op->manual_refresher=enabled;
+}
+
+bool_t sal_op_is_ipv6(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);
+
+ if (tr==NULL)
+ 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;
+ }
+ 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);
+}
+
+bool_t sal_op_is_idle(SalOp *op){
+ if (op->dialog){
+ return !belle_sip_dialog_request_pending(op->dialog);
+ }
+ return TRUE;
+}
+
+void sal_op_stop_refreshing(SalOp *op){
+ if (op->refresher){
+ belle_sip_refresher_stop(op->refresher);
+ }
+}
+
+void sal_call_set_sdp_handling(SalOp *h, SalOpSDPHandling handling) {
+ if (handling != SalOpSDPNormal) ms_message("Enabling special SDP handling for SalOp[%p]!", h);
+ h->sdp_handling = 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;
+}
diff --git a/coreapi/bellesip_sal/sal_op_info.c b/coreapi/bellesip_sal/sal_op_info.c
new file mode 100644
index 000000000..3cffb8e55
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_info.c
@@ -0,0 +1,32 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+
+int sal_send_info(SalOp *op, const char *from, const char *to, const SalBody *body){
+ 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);
+ 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
new file mode 100644
index 000000000..921f8c0a6
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_message.c
@@ -0,0 +1,357 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+#include "linphonecore.h"
+#include "private.h"
+#include "lime.h"
+#include
+
+static void process_error( SalOp* op) {
+ if (op->dir == SalOpDirOutgoing) {
+ op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
+ } else {
+ ms_warning("unexpected io error for incoming message on op [%p]",op);
+ }
+ op->state=SalOpStateTerminated;
+
+}
+
+static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
+ SalOp* op = (SalOp*)user_ctx;
+ sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO Error",NULL);
+ process_error(op);
+}
+static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
+ SalOp* op=(SalOp*)user_ctx;
+ sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
+ process_error(op);
+
+}
+static void process_response_event(void *op_base, const belle_sip_response_event_t *event){
+ SalOp* op = (SalOp*)op_base;
+ int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
+ SalTextDeliveryStatus status;
+ sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
+
+ if (code>=100 && code <200)
+ status=SalTextDeliveryInProgress;
+ else if (code>=200 && code <300)
+ status=SalTextDeliveryDone;
+ else
+ status=SalTextDeliveryFailed;
+
+ op->base.root->callbacks.text_delivery_update(op,status);
+}
+
+static bool_t is_rcs_filetransfer(belle_sip_header_content_type_t* content_type) {
+ return (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0)
+ && ((strcmp("vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0) || (strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0));
+}
+
+static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) {
+ return strcmp("text",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0;
+}
+
+static bool_t is_cipher_xml(belle_sip_header_content_type_t* content_type) {
+ return (strcmp("xml",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("cipher",belle_sip_header_content_type_get_subtype(content_type))==0)
+
+ || (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0);
+}
+static bool_t is_external_body(belle_sip_header_content_type_t* content_type) {
+ return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0;
+}
+static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) {
+ return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
+ && strcmp("im-iscomposing+xml",belle_sip_header_content_type_get_subtype(content_type))==0;
+}
+
+static void add_message_accept(belle_sip_message_t *msg){
+ belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, xml/cipher, application/vnd.gsma.rcs-ft-http+xml, application/cipher.vnd.gsma.rcs-ft-http+xml"));
+}
+
+void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
+ belle_sip_request_t* req = belle_sip_request_event_get_request(event);
+ belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,req);
+ belle_sip_header_address_t* address;
+ belle_sip_header_from_t* from_header;
+ belle_sip_header_content_type_t* content_type;
+ belle_sip_response_t* resp;
+ int errcode=500;
+ belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t);
+ belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t);
+ belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t);
+ char* from;
+ bool_t plain_text=FALSE;
+ bool_t external_body=FALSE;
+ bool_t cipher_xml=FALSE;
+ bool_t rcs_filetransfer=FALSE;
+ uint8_t *decryptedMessage = NULL;
+
+ 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;
+ 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);
+ }
+
+ }
+
+ 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};
+
+ 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;
+ }
+ return;
+error:
+ resp = belle_sip_response_create_from_request(req, errcode);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ sal_op_release(op);
+}
+
+static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
+ SalOp* op = (SalOp*)op_base;
+ sal_process_incoming_message(op,event);
+}
+
+int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg, const char *peer_uri){
+ belle_sip_request_t* req;
+ char content_type_raw[256];
+ size_t content_length = msg?strlen(msg):0;
+ time_t curtime=time(NULL);
+ uint8_t *multipartEncryptedMessage = NULL;
+ int retval;
+
+ if (op->dialog){
+ /*for SIP MESSAGE that are sent in call's dialog*/
+ req=belle_sip_dialog_create_queued_request(op->dialog,"MESSAGE");
+ }else{
+ sal_op_message_fill_cbs(op);
+ if (from)
+ sal_op_set_from(op,from);
+ if (to)
+ sal_op_set_to(op,to);
+ op->dir=SalOpDirOutgoing;
+
+ req=sal_op_build_request(op,"MESSAGE");
+ 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)));
+ }
+ }
+
+ /* shall we try to encrypt the message?*/
+ if ((strcmp(content_type, "xml/cipher") == 0) || ((strcmp(content_type, "application/cipher.vnd.gsma.rcs-ft-http+xml") == 0))) {
+ /* access the zrtp cache to get keys needed to cipher 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 encrypt message");
+ /*probably not a good idea to do this:*/
+ sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
+ op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
+ return -1;
+ } else {
+ size_t cacheSize;
+ char *cacheString;
+ xmlDocPtr cacheXml;
+ int retval;
+
+ cacheString=ms_load_file_content(CACHEFD, &cacheSize);
+ if (!cacheString){
+ ms_warning("Unable to load content of ZRTP ZID cache to encrypt message");
+ return -1;
+ }
+ cacheString[cacheSize] = '\0';
+ cacheSize += 1;
+ fclose(CACHEFD);
+ cacheXml = xmlParseDoc((xmlChar*)cacheString);
+ ms_free(cacheString);
+ retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg, (uint8_t *)peer_uri, &multipartEncryptedMessage);
+ if (retval != 0) {
+ ms_warning("Unable to encrypt message for %s : %s - op [%p]", peer_uri, lime_error_code_to_string(retval), op);
+ xmlFreeDoc(cacheXml);
+ free(multipartEncryptedMessage);
+ /*probably not a good idea to do this:*/
+ sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
+ op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
+ return -1;
+ } 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("Unable to write zid cache");
+ }
+ xmlFree(xmlStringOutput);
+ fclose(CACHEFD);
+ content_length = strlen((const char *)multipartEncryptedMessage);
+ }
+ xmlFreeDoc(cacheXml);
+ }
+ }
+
+ snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type);
+ 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);
+ retval = sal_op_send_request(op,req);
+ free(multipartEncryptedMessage);
+
+ return retval;
+}
+
+int sal_message_reply(SalOp *op, SalReason reason){
+ if (op->pending_server_trans){
+ int code=sal_reason_to_sip_code(reason);
+ belle_sip_response_t *resp = belle_sip_response_create_from_request(
+ belle_sip_transaction_get_request((belle_sip_transaction_t*)op->pending_server_trans),code);
+ belle_sip_server_transaction_send_response(op->pending_server_trans,resp);
+ return 0;
+ }else ms_error("sal_message_reply(): no server transaction");
+ return -1;
+}
+
+int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) {
+ return sal_message_send(op,from,to,"text/plain",msg, NULL);
+}
+
+static belle_sip_listener_callbacks_t op_message_callbacks={0};
+
+void sal_op_message_fill_cbs(SalOp*op) {
+ if (op_message_callbacks.process_io_error==NULL){
+ op_message_callbacks.process_io_error=process_io_error;
+ op_message_callbacks.process_response_event=process_response_event;
+ op_message_callbacks.process_timeout=process_timeout;
+ op_message_callbacks.process_request_event=process_request_event;
+ }
+ op->callbacks=&op_message_callbacks;
+ op->type=SalOpMessage;
+}
diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c
new file mode 100644
index 000000000..6de0a0fbf
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_presence.c
@@ -0,0 +1,364 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+
+void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence) {
+ char *contact_info;
+ char *content = NULL;
+ size_t content_length;
+
+ if (presence){
+ belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(notify,belle_sip_header_from_t);
+ contact_info=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)));
+ op->base.root->callbacks.convert_presence_to_xml_requested(op, presence, contact_info, &content);
+ belle_sip_free(contact_info);
+ if (content == NULL) return;
+ }
+
+ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_TYPE);
+ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_LENGTH);
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),NULL,0);
+
+ if (content){
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
+ ,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","pidf+xml")));
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
+ ,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(content))));
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),content,content_length);
+ ms_free(content);
+ }
+}
+
+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");
+}
+
+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;
+ }
+}
+
+static void presence_refresher_listener(belle_sip_refresher_t* refresher, void* user_pointer, unsigned int status_code, const char* reason_phrase){
+ SalOp* op = (SalOp*)user_pointer;
+ switch(status_code){
+ case 481: {
+
+ 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;
+ }
+ }
+}
+
+
+static void presence_response_event(void *op_base, const belle_sip_response_event_t *event){
+ SalOp* op = (SalOp*)op_base;
+ belle_sip_dialog_state_t dialog_state;
+ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
+ belle_sip_response_t* response=belle_sip_response_event_get_response(event);
+ belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+ int code = belle_sip_response_get_status_code(response);
+ belle_sip_header_expires_t* expires;
+
+ 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;
+ }
+ set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event));
+ if (!op->dialog) {
+ ms_message("presence op [%p] receive out of dialog answer [%i]",op,code);
+ return;
+ }
+ dialog_state=belle_sip_dialog_get_state(op->dialog);
+
+ switch(dialog_state) {
+ case BELLE_SIP_DIALOG_NULL:
+ case BELLE_SIP_DIALOG_EARLY: {
+ ms_error("presence op [%p] receive an unexpected answer [%i]",op,code);
+ break;
+ }
+ case BELLE_SIP_DIALOG_CONFIRMED: {
+ if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0) {
+ expires=belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t);
+ if(op->refresher) {
+ belle_sip_refresher_stop(op->refresher);
+ belle_sip_object_unref(op->refresher);
+ op->refresher=NULL;
+ }
+ if (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);
+ }
+ }
+ 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");
+}
+
+static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
+ ms_message("presence_process_transaction_terminated not implemented yet");
+}
+
+static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_request_t *req) {
+ 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);
+ belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_length_t);
+ const char *body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
+ SalPresenceModel *result = NULL;
+
+ if ((content_type == NULL) || (content_length == NULL))
+ return NULL;
+ if (belle_sip_header_content_length_get_content_length(content_length) == 0)
+ return NULL;
+
+ if (body==NULL) return NULL;
+
+ 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){
+ 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 (!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;
+ }
+ 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);
+ } else if (body){
+ /* Formatting error in presence notification body. */
+ ms_warning("Wrongly formatted presence document.");
+ resp = sal_op_create_response_from_request(op, req, 488);
+ }
+ if (resp) belle_sip_server_transaction_send_response(server_transaction,resp);
+ }
+}
+
+static void presence_process_request_event(void *op_base, const belle_sip_request_event_t *event) {
+ SalOp* op = (SalOp*)op_base;
+ 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_object_ref(server_transaction);
+ if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
+ op->pending_server_trans=server_transaction;
+
+
+ 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);
+ ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
+ }else{ /* this is a NOTIFY */
+ ms_message("Receiving out of dialog notify");
+ handle_notify(op,req);
+ 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));
+ break;
+ }
+ case BELLE_SIP_DIALOG_EARLY:
+ ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",method,op->dialog);
+ break;
+
+ case BELLE_SIP_DIALOG_CONFIRMED:
+ if (strcmp("NOTIFY",method)==0) {
+ handle_notify(op,req);
+ } 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);
+ }
+ }
+ break;
+ default:
+ ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
+ break;
+ }
+}
+
+static belle_sip_listener_callbacks_t op_presence_callbacks={0};
+
+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;
+ op_presence_callbacks.process_response_event=presence_response_event;
+ op_presence_callbacks.process_timeout=presence_process_timeout;
+ op_presence_callbacks.process_transaction_terminated=presence_process_transaction_terminated;
+ op_presence_callbacks.process_request_event=presence_process_request_event;
+ op_presence_callbacks.process_dialog_terminated=presence_process_dialog_terminated;
+ }
+ op->callbacks=&op_presence_callbacks;
+ op->type=SalOpPresence;
+}
+
+
+/*presence Subscribe/notify*/
+int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expires){
+ belle_sip_request_t *req=NULL;
+ if (from)
+ sal_op_set_from(op,from);
+ if (to)
+ sal_op_set_to(op,to);
+
+ sal_op_presence_fill_cbs(op);
+
+ if (expires==-1){
+ if (op->refresher){
+ expires=belle_sip_refresher_get_expires(op->refresher);
+ belle_sip_object_unref(op->refresher);
+ op->refresher=NULL;
+ }else{
+ ms_error("sal_subscribe_presence(): cannot guess expires from previous refresher.");
+ return -1;
+ }
+ }
+ if (!op->event){
+ op->event=belle_sip_header_create("Event","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(belle_sip_header_expires_create(expires)));
+ }
+
+ return sal_op_send_request(op,req);
+}
+
+
+static belle_sip_request_t *create_presence_notify(SalOp *op){
+ belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY");
+ if (!notify) return NULL;
+
+ belle_sip_message_add_header((belle_sip_message_t*)notify,belle_sip_header_create("Event","presence"));
+ return notify;
+}
+
+static int sal_op_check_dialog_state(SalOp *op) {
+ belle_sip_dialog_state_t state=op->dialog?belle_sip_dialog_get_state(op->dialog): BELLE_SIP_DIALOG_NULL;
+ if (state != BELLE_SIP_DIALOG_CONFIRMED) {
+ ms_warning("Cannot notify presence for op [%p] because dialog in state [%s]",op, belle_sip_dialog_state_to_string(state));
+ 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)) {
+ return -1;
+ }
+ notify=create_presence_notify(op);
+ if (!notify) return-1;
+
+ sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),presence); /*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_ACTIVE,600)));
+ return sal_op_send_request(op,notify);
+}
+
+int sal_notify_presence_close(SalOp *op){
+ belle_sip_request_t* notify=NULL;
+ if (sal_op_check_dialog_state(op)) {
+ return -1;
+ }
+ notify=create_presence_notify(op);
+ if (!notify) return-1;
+
+ 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);
+}
+
+
+
diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c
new file mode 100644
index 000000000..17ef8e8db
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_publish.c
@@ -0,0 +1,129 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+
+static void publish_refresher_listener (belle_sip_refresher_t* refresher
+ ,void* user_pointer
+ ,unsigned int status_code
+ ,const char* reason_phrase) {
+ 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){
+ op->base.root->callbacks.on_expire(op);
+ }else if (status_code>=200){
+ 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);
+ }
+}
+
+static void publish_response_event(void *userctx, const belle_sip_response_event_t *event){
+ SalOp *op=(SalOp*)userctx;
+ sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
+ if (op->error_info.protocol_code>=200){
+ op->base.root->callbacks.on_publish_response(op);
+ }
+}
+
+static belle_sip_listener_callbacks_t op_publish_callbacks={0};
+
+void sal_op_publish_fill_cbs(SalOp *op) {
+ if (op_publish_callbacks.process_response_event==NULL){
+ op_publish_callbacks.process_response_event=publish_response_event;
+ }
+ op->callbacks=&op_publish_callbacks;
+ 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){
+ 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);
+
+ sal_op_publish_fill_cbs(op);
+ 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",eventname));
+ sal_op_add_body(op,BELLE_SIP_MESSAGE(req),body);
+ if (expires!=-1)
+ return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener);
+ else return sal_op_send_request(op,req);
+ } else {
+ /*update 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));
+ /*update body*/
+ sal_op_add_body(op,BELLE_SIP_MESSAGE(last_publish),expires!=0 ? body : NULL);
+ return belle_sip_refresher_refresh(op->refresher,expires==-1 ? BELLE_SIP_REFRESHER_REUSE_EXPIRES : expires);
+ }
+}
diff --git a/coreapi/bellesip_sal/sal_op_registration.c b/coreapi/bellesip_sal/sal_op_registration.c
new file mode 100644
index 000000000..961ba4a34
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_op_registration.c
@@ -0,0 +1,130 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+
+
+static void register_refresher_listener (belle_sip_refresher_t* refresher
+ ,void* user_pointer
+ ,unsigned int status_code
+ ,const char* reason_phrase) {
+ 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));
+
+ 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*/
+ op->auth_info=sal_auth_info_create((belle_sip_auth_event_t*)(belle_sip_refresher_get_auth_events(refresher)->data));
+ }
+ sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
+ if (status_code>=200){
+ sal_op_assign_recv_headers(op,(belle_sip_message_t*)response);
+ }
+ if(status_code == 200) {
+ /*check service route rfc3608*/
+ belle_sip_header_service_route_t* service_route;
+ belle_sip_header_address_t* service_route_address=NULL;
+ belle_sip_header_contact_t *contact = belle_sip_refresher_get_contact(refresher);
+ if ((service_route=belle_sip_message_get_header_by_type(response,belle_sip_header_service_route_t))) {
+ service_route_address=belle_sip_header_address_create(NULL,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(service_route)));
+ }
+ 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*/
+ }
+ op->base.root->callbacks.register_success(op,belle_sip_refresher_get_expires(op->refresher)>0);
+ } else if (status_code>=400) {
+ /* from rfc3608, 6.1.
+ If the UA refreshes the registration, the stored value of the Service-
+ Route is updated according to the Service-Route header field of the
+ latest 200 class response. If there is no Service-Route header field
+ in the response, the UA clears any service route for that address-
+ of-record previously stored by the UA. If the re-registration
+ request is refused or if an existing registration expires and the UA
+ chooses not to re-register, the UA SHOULD discard any stored service
+ route for that address-of-record. */
+ sal_op_set_service_route(op,NULL);
+ sal_op_ref(op); /*take a ref while invoking the callback to make sure the operations done after are valid*/
+ op->base.root->callbacks.register_failure(op);
+ if (op->state!=SalOpStateTerminated && op->auth_info) {
+ /*add pending auth*/
+ sal_add_pending_auth(op->base.root,op);
+ if (status_code==403 || status_code==401 || status_code==407 )
+ op->base.root->callbacks.auth_failure(op,op->auth_info);
+ }
+ sal_op_unref(op);
+ }
+}
+
+int sal_register(SalOp *op, const char *proxy, const char *from, int expires,SalAddress* old_contact){
+ 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);
+ sal_op_set_route(op,proxy);
+ req = sal_op_build_request(op,"REGISTER");
+ req_uri = belle_sip_request_get_uri(req);
+ belle_sip_uri_set_user(req_uri,NULL); /*remove userinfo if any*/
+ if (op->base.root->use_dates){
+ time_t curtime=time(NULL);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
+ }
+ accept_header = belle_sip_header_create("Accept", "application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml");
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), accept_header);
+ belle_sip_message_set_header(BELLE_SIP_MESSAGE(req),(belle_sip_header_t*)sal_op_create_contact(op));
+ if (old_contact) {
+ belle_sip_header_contact_t *contact=belle_sip_header_contact_create((const belle_sip_header_address_t *)old_contact);
+ if (contact) {
+ char * tmp;
+ belle_sip_header_contact_set_expires(contact,0); /*remove old aor*/
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), BELLE_SIP_HEADER(contact));
+ tmp = belle_sip_object_to_string(contact);
+ ms_message("Clearing contact [%s] for op [%p]",tmp,op);
+ ms_free(tmp);
+ } else {
+ ms_error("Cannot add old contact header to op [%p]",op);
+ }
+ }
+ return sal_op_send_and_create_refresher(op,req,expires,register_refresher_listener);
+}
+
+int sal_register_refresh(SalOp *op, int expires){
+ if (op->refresher)
+ return belle_sip_refresher_refresh(op->refresher,expires);
+ else
+ return -1;
+}
+
+int sal_unregister(SalOp *op){
+ return sal_register_refresh(op,0);
+}
+
+
diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c
new file mode 100644
index 000000000..9313a3d43
--- /dev/null
+++ b/coreapi/bellesip_sal/sal_sdp.c
@@ -0,0 +1,899 @@
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "sal_impl.h"
+#define keywordcmp(key,b) strncmp(key,b,sizeof(key))
+
+
+static void add_ice_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
+ char buffer[1024];
+ const SalIceCandidate *candidate;
+ int nb;
+ int i;
+
+ for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) {
+ candidate = &desc->ice_candidates[i];
+ if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break;
+ nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s",
+ candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type);
+ if (nb < 0) {
+ ms_error("Cannot add ICE candidate attribute!");
+ return;
+ }
+ if (candidate->raddr[0] != '\0') {
+ nb = snprintf(buffer + nb, sizeof(buffer) - nb, " raddr %s rport %d", candidate->raddr, candidate->rport);
+ if (nb < 0) {
+ ms_error("Cannot add ICE candidate attribute!");
+ return;
+ }
+ }
+ belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("candidate",buffer));
+ }
+}
+
+static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
+ char buffer[1024];
+ char *ptr = buffer;
+ const SalIceRemoteCandidate *candidate;
+ int offset = 0;
+ int i;
+
+ buffer[0] = '\0';
+ for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) {
+ candidate = &desc->ice_remote_candidates[i];
+ if ((candidate->addr[0] != '\0') && (candidate->port != 0)) {
+ offset = snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port);
+ if (offset < 0) {
+ ms_error("Cannot add ICE remote-candidates attribute!");
+ return;
+ }
+ ptr += offset;
+ }
+ }
+ if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer));
+}
+
+static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) {
+ MSList *pt_it;
+ bool_t first = TRUE;
+ for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
+ PayloadType *pt = (PayloadType *)pt_it->data;
+ if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
+ if (first == TRUE) {
+ *trr_int = payload_type_get_avpf_params(pt).trr_interval;
+ first = FALSE;
+ } else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint16_t trr_int) {
+ belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
+ belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
+ belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT);
+ belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int);
+ belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
+}
+
+static void add_rtcp_fb_ack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
+ belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
+ belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
+ belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_ACK);
+ belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
+ belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
+}
+
+static void add_rtcp_fb_nack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
+ belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
+ belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
+ belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK);
+ belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
+ belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
+}
+
+static void add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
+ belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
+ belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
+ belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_CCM);
+ belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
+ belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
+}
+
+static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) {
+ MSList *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) {
+ add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int);
+ }
+ if (stream->rtcp_fb.generic_nack_enabled == TRUE) {
+ add_rtcp_fb_nack_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_NONE);
+ }
+ if (stream->rtcp_fb.tmmbr_enabled == TRUE) {
+ add_rtcp_fb_ccm_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_TMMBR);
+ }
+
+ for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
+ pt = (PayloadType *)pt_it->data;
+
+ /* AVPF/SAVPF profile is used so enable AVPF for all paylad types. */
+ payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
+ avpf_params = payload_type_get_avpf_params(pt);
+
+ /* Add trr-int if not set generally. */
+ if (general_trr_int != TRUE) {
+ add_rtcp_fb_trr_int_attribute(media_desc, payload_type_get_number(pt), avpf_params.trr_interval);
+ }
+
+ /* Add rtcp-fb attributes according to the AVPF features of the payload types. */
+ if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) {
+ add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI);
+ }
+ if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) {
+ add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI);
+ }
+ if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) {
+ if (avpf_params.rpsi_compatibility == TRUE) {
+ add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
+ } else {
+ add_rtcp_fb_ack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
+ }
+ }
+ if (avpf_params.features & PAYLOAD_TYPE_AVPF_FIR) {
+ add_rtcp_fb_ccm_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_FIR);
+ }
+ }
+}
+
+static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) {
+ belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new();
+ if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
+ if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttAll) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "all");
+ else if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttSender) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "sender");
+ belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(attribute, config->rcvr_rtt_max_size);
+ }
+ belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, (config->stat_summary_enabled == TRUE));
+ if (config->stat_summary_enabled == TRUE) {
+ if (config->stat_summary_flags & OrtpRtcpXrStatSummaryLoss) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "loss");
+ if (config->stat_summary_flags & OrtpRtcpXrStatSummaryDup) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "dup");
+ if (config->stat_summary_flags & OrtpRtcpXrStatSummaryJitt) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "jitt");
+ if (config->stat_summary_flags & OrtpRtcpXrStatSummaryTTL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "TTL");
+ if (config->stat_summary_flags & OrtpRtcpXrStatSummaryHL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "HL");
+ }
+ belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, (config->voip_metrics_enabled == TRUE));
+ return BELLE_SDP_ATTRIBUTE(attribute);
+}
+
+static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) {
+ belle_sdp_mime_parameter_t* mime_param;
+ belle_sdp_media_description_t* media_desc;
+ int j;
+ MSList* pt_it;
+ PayloadType* pt;
+ char buffer[1024];
+ char* dir=NULL;
+ const char *rtp_addr;
+ const char *rtcp_addr;
+ int rtp_port;
+ int rtcp_port;
+ bool_t different_rtp_and_rtcp_addr;
+
+ rtp_addr=stream->rtp_addr;
+ rtcp_addr=stream->rtcp_addr;
+ rtp_port=stream->rtp_port;
+ rtcp_port=stream->rtcp_port;
+
+ media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream)
+ ,stream->rtp_port
+ ,1
+ ,sal_media_proto_to_string ( stream->proto )
+ ,NULL );
+ if (stream->payloads) {
+ for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) {
+ pt= ( PayloadType* ) pt_it->data;
+ mime_param= belle_sdp_mime_parameter_create ( pt->mime_type
+ , payload_type_get_number ( pt )
+ , pt->clock_rate
+ , pt->channels>0 ? pt->channels : -1 );
+ belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp );
+ if ( stream->ptime>0 ) {
+ belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime );
+ }
+ belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param );
+ belle_sip_object_unref ( mime_param );
+ }
+ } else {
+ /* to comply with SDP we cannot have an empty payload type number list */
+ /* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/
+ belle_sip_list_t* format = belle_sip_list_append(NULL,0);
+ belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format);
+ }
+ /*only add a c= line within the stream description if address are differents*/
+ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){
+ bool_t inet6;
+ belle_sdp_connection_t *connection;
+ if (strchr(rtp_addr,':')!=NULL){
+ inet6=TRUE;
+ }else inet6=FALSE;
+ connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr);
+ if (ms_is_multicast(rtp_addr)) {
+ /*remove session cline in case of multicast*/
+ belle_sdp_session_description_set_connection(session_desc,NULL);
+ if (inet6 == FALSE)
+ belle_sdp_connection_set_ttl(connection,stream->ttl);
+ }
+ belle_sdp_media_description_set_connection(media_desc,connection);
+ }
+
+ if ( stream->bandwidth>0 )
+ belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );
+
+ if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
+ /* add crypto lines */
+ for ( j=0; jcrypto[j].algo,&desc)==0){
+ if (desc.params)
+ snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params);
+ else
+ snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key );
+
+ belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer));
+ }else break;
+ }
+ }
+
+ /* insert DTLS session attribute if needed */
+ if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) {
+ char* ssrc_attribute = ms_strdup_printf("%u cname:%s",stream->rtp_ssrc,stream->rtcp_cname);
+ if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) {
+ switch(stream->dtls_role) {
+ case SalDtlsRoleIsClient:
+ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active"));
+ break;
+ case SalDtlsRoleIsServer:
+ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive"));
+ break;
+ case SalDtlsRoleUnset:
+ default:
+ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass"));
+ break;
+ }
+ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint));
+ }
+ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute));
+ ms_free(ssrc_attribute);
+ }
+
+ switch ( stream->dir ) {
+ case SalStreamSendRecv:
+ /*dir="sendrecv";*/
+ dir=NULL;
+ break;
+ case SalStreamRecvOnly:
+ dir="recvonly";
+ break;
+ case SalStreamSendOnly:
+ dir="sendonly";
+ break;
+ case SalStreamInactive:
+ dir="inactive";
+ break;
+ }
+ if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) );
+
+ if (rtp_port != 0) {
+ different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0);
+ if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) {
+ if (different_rtp_and_rtcp_addr == TRUE) {
+ snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr);
+ } else {
+ snprintf(buffer, sizeof(buffer), "%u",rtcp_port);
+ }
+ belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer));
+ }
+ }
+ if (stream->ice_completed == TRUE) {
+ belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes"));
+ }
+ if (stream->ice_mismatch == TRUE) {
+ belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL));
+ } else {
+ if (rtp_port != 0) {
+ if (stream->ice_pwd[0] != '\0')
+ belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd));
+ if (stream->ice_ufrag[0] != '\0')
+ belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag));
+ add_ice_candidates(media_desc,stream);
+ add_ice_remote_candidates(media_desc,stream);
+ }
+ }
+
+ if ((rtp_port != 0) && ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf))) {
+ add_rtcp_fb_attributes(media_desc, md, stream);
+ }
+
+ if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) {
+ char sastr[1024] = {0};
+ char mastr[1024] = {0};
+ size_t saoff = 0;
+ size_t maoff = 0;
+ const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr");
+ belle_sdp_attribute_t *media_attribute;
+ if (session_attribute != NULL) {
+ belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff);
+ }
+ media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr);
+ if (media_attribute != NULL) {
+ belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff);
+ }
+ if (strcmp(sastr, mastr) != 0) {
+ belle_sdp_media_description_add_attribute(media_desc, media_attribute);
+ } else {
+ belle_sip_object_unref((belle_sip_object_t*)media_attribute);
+ }
+ }
+ /*
+ * rfc5576
+ * 4.1. The "ssrc" Media Attribute
+ * is the synchronization source (SSRC) ID of the
+ * source being described, interpreted as a 32-bit unsigned integer in
+ * network byte order and represented in decimal.*/
+
+
+ belle_sdp_session_description_add_media_description(session_desc, media_desc);
+}
+
+belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescription *desc ) {
+ belle_sdp_session_description_t* session_desc=belle_sdp_session_description_new();
+ bool_t inet6;
+ belle_sdp_origin_t* origin;
+ int i;
+
+ if ( strchr ( desc->addr,':' ) !=NULL ) {
+ inet6=1;
+ } else inet6=0;
+ belle_sdp_session_description_set_version ( session_desc,belle_sdp_version_create ( 0 ) );
+
+ origin = belle_sdp_origin_create ( desc->username
+ ,desc->session_id
+ ,desc->session_ver
+ ,"IN"
+ , inet6 ? "IP6" :"IP4"
+ ,desc->addr );
+
+ belle_sdp_session_description_set_origin ( session_desc,origin );
+
+ belle_sdp_session_description_set_session_name ( session_desc,
+ belle_sdp_session_name_create ( desc->name[0]!='\0' ? desc->name : "Talk" ) );
+
+ if ( !sal_media_description_has_dir ( desc,SalStreamInactive ) || desc->ice_ufrag[0] != '\0' ) {
+ /*in case of sendonly, setting of the IP on cnx we give a chance to receive stun packets*/
+ belle_sdp_session_description_set_connection ( session_desc
+ ,belle_sdp_connection_create ( "IN",inet6 ? "IP6" :"IP4",desc->addr ) );
+
+ } else {
+ belle_sdp_session_description_set_connection ( session_desc
+ ,belle_sdp_connection_create ( "IN"
+ ,inet6 ? "IP6" :"IP4"
+ ,inet6 ? "::0" :"0.0.0.0" ) );
+
+ }
+
+ belle_sdp_session_description_set_time_description ( session_desc,belle_sdp_time_description_create ( 0,0 ) );
+
+ if ( desc->bandwidth>0 ) {
+ belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth );
+ }
+
+ if (desc->ice_completed == 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));
+ }
+
+ for ( i=0; inb_streams; i++ ) {
+ stream_description_to_sdp(session_desc, desc, &desc->streams[i]);
+ }
+ return session_desc;
+}
+
+
+static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
+ PayloadType *pt;
+ PayloadTypeAvpfParams avpf_params;
+ belle_sip_list_t* mime_param_it=NULL;
+ belle_sdp_mime_parameter_t* mime_param;
+ belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc );
+ memset(&avpf_params, 0, sizeof(avpf_params));
+ for ( mime_param_it=mime_params
+ ; mime_param_it!=NULL
+ ; mime_param_it=mime_param_it->next ) {
+ mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data );
+
+ pt=payload_type_new();
+ payload_type_set_number ( pt,belle_sdp_mime_parameter_get_media_format ( mime_param ) );
+ pt->clock_rate=belle_sdp_mime_parameter_get_rate ( mime_param );
+ pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) );
+ pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param );
+ payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) );
+ payload_type_set_avpf_params(pt, avpf_params);
+ stream->payloads=ms_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 : "" );
+ }
+ if ( mime_params ) belle_sip_list_free_with_data ( mime_params,belle_sip_object_unref );
+}
+
+static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
+ belle_sip_list_t *attribute_it;
+ belle_sdp_attribute_t *attribute;
+ char tmp[256], tmp2[256], parameters[256]={0};
+ int valid_count = 0;
+ int nb;
+
+ memset ( &stream->crypto, 0, sizeof ( stream->crypto ) );
+ for ( attribute_it=belle_sdp_media_description_get_attributes ( media_desc )
+ ; valid_count < SAL_CRYPTO_ALGO_MAX && attribute_it!=NULL;
+ attribute_it=attribute_it->next ) {
+ attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data );
+
+ if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) {
+ nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s %256s",
+ &stream->crypto[valid_count].tag,
+ tmp,
+ tmp2, parameters );
+
+ if ( nb >= 3 ) {
+ MSCryptoSuite cs;
+ MSCryptoSuiteNameParams np;
+
+ np.name=tmp;
+ np.params=parameters[0]!='\0' ? parameters : NULL;
+ cs=ms_crypto_suite_build_from_name_params(&np);
+ if (cs==MS_CRYPTO_SUITE_INVALID){
+ ms_warning ( "Failed to parse crypto-algo: '%s'", tmp );
+ stream->crypto[valid_count].algo = 0;
+ }else{
+ char *sep;
+ strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)-1 );
+ sep=strchr(stream->crypto[valid_count].master_key,'|');
+ if (sep) *sep='\0';
+ stream->crypto[valid_count].algo = cs;
+ ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'",
+ stream->crypto[valid_count].tag,
+ tmp,
+ stream->crypto[valid_count].master_key );
+ valid_count++;
+ }
+ }else{
+ ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb );
+ }
+ }
+ }
+ ms_message("Found: %d valid crypto lines", valid_count );
+}
+
+static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
+ belle_sip_list_t *attribute_it;
+ belle_sdp_attribute_t *attribute;
+ const char *att_name;
+ const char *value;
+ int nb_ice_candidates = 0;
+
+ for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) {
+ attribute=BELLE_SDP_ATTRIBUTE(attribute_it->data);
+ att_name = belle_sdp_attribute_get_name(attribute);
+ value = belle_sdp_attribute_get_value(attribute);
+
+ if ( (nb_ice_candidates < sizeof (stream->ice_candidates)/sizeof(SalIceCandidate))
+ && (keywordcmp("candidate", att_name) == 0)
+ && (value != NULL)) {
+ SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates];
+ char proto[4];
+ int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d",
+ candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port,
+ candidate->type, candidate->raddr, &candidate->rport);
+ if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++;
+ else memset(candidate, 0, sizeof(*candidate));
+ } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) {
+ SalIceRemoteCandidate candidate;
+ unsigned int componentID;
+ int offset;
+ const char *ptr = value;
+ const char *endptr = value + strlen(ptr);
+ while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) {
+ if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) {
+ SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1];
+ strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)-1);
+ remote_candidate->port = candidate.port;
+ }
+ ptr += offset;
+ if (ptr < endptr) {
+ if (ptr[offset] == ' ') ptr += 1;
+ } else break;
+ }
+ } else if ((keywordcmp("ice-ufrag", att_name) == 0) && (value != NULL)) {
+ strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)-1);
+ } else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) {
+ strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd) -1);
+ } else if (keywordcmp("ice-mismatch", att_name) == 0) {
+ stream->ice_mismatch = TRUE;
+ }
+ }
+}
+
+static void enable_avpf_for_stream(SalStreamDescription *stream) {
+ MSList *pt_it;
+ for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
+ PayloadType *pt = (PayloadType *)pt_it->data;
+ payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
+ }
+}
+
+static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, SalStreamDescription *stream, PayloadType *pt) {
+ PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt);
+ switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) {
+ case BELLE_SDP_RTCP_FB_ACK:
+ if (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute) == BELLE_SDP_RTCP_FB_RPSI) {
+ avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
+ }
+ break;
+ case BELLE_SDP_RTCP_FB_NACK:
+ switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
+ case BELLE_SDP_RTCP_FB_PLI:
+ avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI;
+ break;
+ case BELLE_SDP_RTCP_FB_SLI:
+ avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI;
+ break;
+ case BELLE_SDP_RTCP_FB_RPSI:
+ /* Linphone uses positive feeback for RPSI. However first versions handling
+ * AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility
+ * with these versions but will probably be removed at some point in time. */
+ avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
+ avpf_params.rpsi_compatibility = TRUE;
+ break;
+ case BELLE_SDP_RTCP_FB_NONE:
+ stream->rtcp_fb.generic_nack_enabled = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ case BELLE_SDP_RTCP_FB_TRR_INT:
+ avpf_params.trr_interval = belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute);
+ break;
+ case BELLE_SDP_RTCP_FB_CCM:
+ switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
+ case BELLE_SDP_RTCP_FB_FIR:
+ avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR;
+ break;
+ case BELLE_SDP_RTCP_FB_TMMBR:
+ stream->rtcp_fb.tmmbr_enabled = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ payload_type_set_avpf_params(pt, avpf_params);
+}
+
+static void 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;
+ PayloadType *pt;
+ int8_t pt_num;
+
+ /* Handle rtcp-fb attributes that concern all payload types. */
+ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
+ attribute = BELLE_SDP_ATTRIBUTE(it->data);
+ if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
+ fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
+ if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) {
+ for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
+ pt = (PayloadType *)pt_it->data;
+ apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
+ }
+ }
+ }
+ }
+
+ /* Handle rtcp-fb attributes that are specefic to a payload type. */
+ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
+ attribute = BELLE_SDP_ATTRIBUTE(it->data);
+ if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
+ fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
+ pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute);
+ for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
+ pt = (PayloadType *)pt_it->data;
+ if (payload_type_get_number(pt) == (int)pt_num) {
+ apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
+ }
+ }
+ }
+ }
+}
+
+static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) {
+ config->enabled = FALSE;
+ config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone;
+ config->rcvr_rtt_max_size = -1;
+ config->stat_summary_flags = 0;
+ config->voip_metrics_enabled = FALSE;
+}
+
+static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute, OrtpRtcpXrConfiguration *config) {
+ if (attribute != NULL) {
+ const belle_sdp_rtcp_xr_attribute_t *xr_attr;
+ const char *rcvr_rtt_mode;
+ sal_init_rtcp_xr_description(config);
+ xr_attr = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute);
+ rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(xr_attr);
+ if (rcvr_rtt_mode != NULL) {
+ if (strcasecmp(rcvr_rtt_mode, "all") == 0) {
+ config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll;
+ } else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) {
+ config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender;
+ }
+ config->rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(xr_attr);
+ }
+ config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0);
+ if (config->stat_summary_enabled) {
+ const belle_sip_list_t *stat_summary_flag_it;
+ for (stat_summary_flag_it = belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(xr_attr); stat_summary_flag_it != NULL; stat_summary_flag_it = stat_summary_flag_it->next ) {
+ const char *flag = (const char *)stat_summary_flag_it->data;
+ if (flag != NULL) {
+ if (strcasecmp(flag, "loss") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryLoss;
+ else if (strcasecmp(flag, "dup") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryDup;
+ else if (strcasecmp(flag, "jitt") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryJitt;
+ else if (strcasecmp(flag, "TTL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryTTL;
+ else if (strcasecmp(flag, "HL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryHL;
+ }
+ }
+ }
+ config->voip_metrics_enabled = (belle_sdp_rtcp_xr_attribute_has_voip_metrics(xr_attr) != 0);
+ config->enabled = TRUE;
+ }
+}
+
+static void sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t *session_desc, OrtpRtcpXrConfiguration *config) {
+ const belle_sdp_attribute_t *attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr");
+ sdp_parse_rtcp_xr_parameters(attribute, config);
+}
+
+static void sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t *media_desc, OrtpRtcpXrConfiguration *config) {
+ const belle_sdp_attribute_t *attribute = belle_sdp_media_description_get_attribute(media_desc, "rtcp-xr");
+ sdp_parse_rtcp_xr_parameters(attribute, config);
+}
+
+static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) {
+ SalStreamDescription *stream;
+ belle_sdp_connection_t* cnx;
+ belle_sdp_media_t* media;
+ belle_sdp_attribute_t* attribute;
+ const char* value;
+ const char *mtype,*proto;
+
+ stream=&md->streams[md->nb_streams];
+ media=belle_sdp_media_description_get_media ( media_desc );
+
+ proto = belle_sdp_media_get_protocol ( media );
+ stream->proto=SalProtoOther;
+ if ( proto ) {
+ if (strcasecmp(proto, "RTP/AVP") == 0) {
+ stream->proto = SalProtoRtpAvp;
+ } else if (strcasecmp(proto, "RTP/SAVP") == 0) {
+ stream->proto = SalProtoRtpSavp;
+ } else if (strcasecmp(proto, "RTP/AVPF") == 0) {
+ stream->proto = SalProtoRtpAvpf;
+ } else if (strcasecmp(proto, "RTP/SAVPF") == 0) {
+ stream->proto = SalProtoRtpSavpf;
+ } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) {
+ stream->proto = SalProtoUdpTlsRtpSavp;
+ } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) {
+ stream->proto = SalProtoUdpTlsRtpSavpf;
+ } else {
+ strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1);
+ }
+ }
+ if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
+ strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 );
+ stream->ttl=belle_sdp_connection_get_ttl(cnx);
+ }
+
+ stream->rtp_port=belle_sdp_media_get_media_port ( media );
+
+ mtype = belle_sdp_media_get_media_type ( media );
+ if ( strcasecmp ( "audio", mtype ) == 0 ) {
+ stream->type=SalAudio;
+ } else if ( strcasecmp ( "video", mtype ) == 0 ) {
+ stream->type=SalVideo;
+ } else {
+ stream->type=SalOther;
+ strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 );
+ }
+
+ if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) {
+ stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" );
+ }
+
+ if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) {
+ stream->dir=SalStreamSendRecv;
+ } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) {
+ stream->dir=SalStreamSendOnly;
+ } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) {
+ stream->dir=SalStreamRecvOnly;
+ } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) {
+ stream->dir=SalStreamInactive;
+ } else {
+ stream->dir=md->dir; /*takes default value if not present*/
+ }
+
+ /* Get media payload types */
+ sdp_parse_payload_types(media_desc, stream);
+
+ /* Get media specific RTCP attribute */
+ stream->rtcp_port = stream->rtp_port + 1;
+ snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr);
+ attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp");
+ if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){
+ char tmp[256];
+ int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp);
+ if (nb == 1) {
+ /* SDP rtcp attribute only contains the port */
+ } else if (nb == 2) {
+ strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1);
+ } else {
+ ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb);
+ }
+ }
+
+ /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */
+ if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) {
+ attribute=belle_sdp_media_description_get_attribute(media_desc,"setup");
+ if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){
+ if (strncmp(value, "actpass", 7) == 0) {
+ stream->dtls_role = SalDtlsRoleUnset;
+ } else if (strncmp(value, "active", 6) == 0) {
+ stream->dtls_role = SalDtlsRoleIsClient;
+ } else if (strncmp(value, "passive", 7) == 0) {
+ stream->dtls_role = SalDtlsRoleIsServer;
+ }
+ }
+ if (stream->dtls_role != SalDtlsRoleInvalid && (attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"))) {
+ strncpy(stream->dtls_fingerprint, belle_sdp_attribute_get_value(attribute),sizeof(stream->dtls_fingerprint));
+ }
+ }
+
+ /* Read crypto lines if any */
+ if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
+ sdp_parse_media_crypto_parameters(media_desc, stream);
+ }
+
+ /* Get ICE candidate attributes if any */
+ sdp_parse_media_ice_parameters(media_desc, stream);
+
+ /* Get RTCP-FB attributes if any */
+ if ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf)) {
+ enable_avpf_for_stream(stream);
+ sdp_parse_rtcp_fb_parameters(media_desc, stream);
+ }
+
+ /* 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);
+
+ md->nb_streams++;
+ return stream;
+}
+
+
+int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, SalMediaDescription *desc ) {
+ belle_sdp_connection_t* cnx;
+ belle_sip_list_t* media_desc_it;
+ belle_sdp_media_description_t* media_desc;
+ belle_sdp_session_name_t *sname;
+ const char* value;
+ SalDtlsRole session_role=SalDtlsRoleInvalid;
+ int i;
+
+ desc->nb_streams = 0;
+ desc->dir = SalStreamSendRecv;
+
+ if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
+ strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) -1 );
+ }
+ if ( (sname=belle_sdp_session_description_get_session_name(session_desc)) && belle_sdp_session_name_get_value(sname) ){
+ strncpy(desc->name,belle_sdp_session_name_get_value(sname),sizeof(desc->name) - 1);
+ }
+
+ if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) {
+ desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" );
+ }
+
+ /*in some very rare case, session attribute may set stream dir*/
+ if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) {
+ desc->dir=SalStreamSendRecv;
+ } else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) {
+ desc->dir=SalStreamSendOnly;
+ } else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) {
+ desc->dir=SalStreamRecvOnly;
+ } else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) {
+ desc->dir=SalStreamInactive;
+ }
+
+ /*DTLS attributes can be defined at session level.*/
+ value=belle_sdp_session_description_get_attribute_value(session_desc,"setup");
+ if (value){
+ if (strncmp(value, "actpass", 7) == 0) {
+ session_role = SalDtlsRoleUnset;
+ } else if (strncmp(value, "active", 6) == 0) {
+ session_role = SalDtlsRoleIsClient;
+ } else if (strncmp(value, "passive", 7) == 0) {
+ session_role = SalDtlsRoleIsServer;
+ }
+ }
+ value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint");
+ /*copy dtls attributes to every streams, might be overwritten stream by stream*/
+ for (i=0;istreams[i].dtls_fingerprint, value, sizeof(desc->streams[i].dtls_fingerprint));
+ desc->streams[i].dtls_role=session_role; /*set or reset value*/
+ }
+
+ /* Get ICE remote ufrag and remote pwd, and ice_lite flag */
+ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag");
+ if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1);
+
+ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd");
+ if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)-1);
+
+ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite");
+ if (value) desc->ice_lite = TRUE;
+
+ /* Get session RTCP-XR attributes if any */
+ sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr);
+
+ for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc )
+ ; media_desc_it!=NULL
+ ; media_desc_it=media_desc_it->next ) {
+ if (desc->nb_streams==SAL_MEDIA_DESCRIPTION_MAX_STREAMS){
+ ms_warning("Cannot convert mline at position [%i] from SDP to SalMediaDescription",desc->nb_streams);
+ break;
+ }
+ media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data );
+ sdp_to_stream_description(desc, media_desc);
+ }
+ return 0;
+}
diff --git a/coreapi/buffer.c b/coreapi/buffer.c
new file mode 100644
index 000000000..9db65798e
--- /dev/null
+++ b/coreapi/buffer.c
@@ -0,0 +1,106 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "linphonecore.h"
+#include "private.h"
+
+
+
+static void linphone_buffer_destroy(LinphoneBuffer *buffer) {
+ if (buffer->content) belle_sip_free(buffer->content);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneBuffer);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneBuffer, belle_sip_object_t,
+ (belle_sip_object_destroy_t)linphone_buffer_destroy,
+ NULL, // clone
+ NULL, // marshal
+ TRUE
+);
+
+
+LinphoneBuffer * linphone_buffer_new(void) {
+ LinphoneBuffer *buffer = belle_sip_object_new(LinphoneBuffer);
+ belle_sip_object_ref(buffer);
+ return buffer;
+}
+
+LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size) {
+ LinphoneBuffer *buffer = linphone_buffer_new();
+ linphone_buffer_set_content(buffer, data, size);
+ return buffer;
+}
+
+LinphoneBuffer * linphone_buffer_new_from_string(const char *data) {
+ LinphoneBuffer *buffer = linphone_buffer_new();
+ linphone_buffer_set_string_content(buffer, data);
+ return buffer;
+}
+
+LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer) {
+ belle_sip_object_ref(buffer);
+ return buffer;
+}
+
+void linphone_buffer_unref(LinphoneBuffer *buffer) {
+ belle_sip_object_unref(buffer);
+}
+
+void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer) {
+ return buffer->user_data;
+}
+
+void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud) {
+ buffer->user_data = ud;
+}
+
+const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer) {
+ return buffer->content;
+}
+
+void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size) {
+ buffer->size = size;
+ if (buffer->content) belle_sip_free(buffer->content);
+ buffer->content = belle_sip_malloc(size + 1);
+ memcpy(buffer->content, content, size);
+ ((char *)buffer->content)[size] = '\0';
+}
+
+const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer) {
+ return (const char *)buffer->content;
+}
+
+void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content) {
+ buffer->size = strlen(content);
+ if (buffer->content) belle_sip_free(buffer->content);
+ buffer->content = (uint8_t *)belle_sip_strdup(content);
+}
+
+size_t linphone_buffer_get_size(const LinphoneBuffer *buffer) {
+ return buffer->size;
+}
+
+void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size) {
+ buffer->size = size;
+}
+
+bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer) {
+ return (buffer->size == 0) ? TRUE : FALSE;
+}
diff --git a/coreapi/buffer.h b/coreapi/buffer.h
new file mode 100644
index 000000000..0d22e9ad2
--- /dev/null
+++ b/coreapi/buffer.h
@@ -0,0 +1,147 @@
+/*
+buffer.h
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef LINPHONE_BUFFER_H_
+#define LINPHONE_BUFFER_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @addtogroup misc
+ * @{
+ */
+
+/**
+ * The LinphoneContent object representing a data buffer.
+**/
+typedef struct _LinphoneBuffer LinphoneBuffer;
+
+
+/**
+ * Create a new empty LinphoneBuffer object.
+ * @return A new LinphoneBuffer object.
+ */
+LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new(void);
+
+/**
+ * Create a new LinphoneBuffer object from existing data.
+ * @param[in] data The initial data to store in the LinphoneBuffer.
+ * @param[in] size The size of the initial data to stroe in the LinphoneBuffer.
+ * @return A new LinphoneBuffer object.
+ */
+LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size);
+
+/**
+ * Create a new LinphoneBuffer object from a string.
+ * @param[in] data The initial string content of the LinphoneBuffer.
+ * @return A new LinphoneBuffer object.
+ */
+LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new_from_string(const char *data);
+
+/**
+ * Acquire a reference to the buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @return The same LinphoneBuffer object.
+**/
+LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer);
+
+/**
+ * Release reference to the buffer.
+ * @param[in] buffer LinphoneBuffer object.
+**/
+LINPHONE_PUBLIC void linphone_buffer_unref(LinphoneBuffer *buffer);
+
+/**
+ * Retrieve the user pointer associated with the buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @return The user pointer associated with the buffer.
+**/
+LINPHONE_PUBLIC void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer);
+
+/**
+ * Assign a user pointer to the buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @param[in] ud The user pointer to associate with the buffer.
+**/
+LINPHONE_PUBLIC void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud);
+
+/**
+ * Get the content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @return The content of the data buffer.
+ */
+LINPHONE_PUBLIC const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer);
+
+/**
+ * Set the content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @param[in] content The content of the data buffer.
+ * @param[in] size The size of the content of the data buffer.
+ */
+LINPHONE_PUBLIC void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size);
+
+/**
+ * Get the string content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object
+ * @return The string content of the data buffer.
+ */
+LINPHONE_PUBLIC const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer);
+
+/**
+ * Set the string content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @param[in] content The string content of the data buffer.
+ */
+LINPHONE_PUBLIC void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content);
+
+/**
+ * Get the size of the content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object.
+ * @return The size of the content of the data buffer.
+ */
+LINPHONE_PUBLIC size_t linphone_buffer_get_size(const LinphoneBuffer *buffer);
+
+/**
+ * Set the size of the content of the data buffer.
+ * @param[in] buffer LinphoneBuffer object
+ * @param[in] size The size of the content of the data buffer.
+ */
+LINPHONE_PUBLIC void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size);
+
+/**
+ * Tell whether the LinphoneBuffer is empty.
+ * @param[in] buffer LinphoneBuffer object
+ * @return A boolean value telling whether the LinphoneBuffer is empty or not.
+ */
+LINPHONE_PUBLIC bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer);
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINPHONE_CONTENT_H_ */
diff --git a/coreapi/call_log.c b/coreapi/call_log.c
new file mode 100644
index 000000000..9d16e8511
--- /dev/null
+++ b/coreapi/call_log.c
@@ -0,0 +1,300 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define _XOPEN_SOURCE 700 /*required for strptime of GNU libc*/
+
+#include
+#include "private.h"
+
+
+/*******************************************************************************
+ * Internal functions *
+ ******************************************************************************/
+
+/*prevent a gcc bug with %c*/
+static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){
+ return strftime(s, max, fmt, tm);
+}
+
+static time_t string_to_time(const char *date){
+#ifndef _WIN32
+ struct tm tmtime={0};
+ strptime(date,"%c",&tmtime);
+ return mktime(&tmtime);
+#else
+ return 0;
+#endif
+}
+
+static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
+ struct tm loctime;
+#ifdef _WIN32
+#if !defined(_WIN32_WCE)
+ loctime=*localtime(&start_time);
+ /*FIXME*/
+#endif /*_WIN32_WCE*/
+#else
+ localtime_r(&start_time,&loctime);
+#endif
+ my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime);
+}
+
+/*******************************************************************************
+ * Private functions *
+ ******************************************************************************/
+
+void call_logs_write_to_config_file(LinphoneCore *lc){
+ MSList *elem;
+ char logsection[32];
+ int i;
+ char *tmp;
+ LpConfig *cfg=lc->config;
+
+ if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
+
+ for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){
+ LinphoneCallLog *cl=(LinphoneCallLog*)elem->data;
+ snprintf(logsection,sizeof(logsection),"call_log_%i",i);
+ lp_config_clean_section(cfg,logsection);
+ lp_config_set_int(cfg,logsection,"dir",cl->dir);
+ lp_config_set_int(cfg,logsection,"status",cl->status);
+ tmp=linphone_address_as_string(cl->from);
+ lp_config_set_string(cfg,logsection,"from",tmp);
+ ms_free(tmp);
+ tmp=linphone_address_as_string(cl->to);
+ lp_config_set_string(cfg,logsection,"to",tmp);
+ ms_free(tmp);
+ if (cl->start_date_time)
+ lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time);
+ else lp_config_set_string(cfg,logsection,"start_date",cl->start_date);
+ lp_config_set_int(cfg,logsection,"duration",cl->duration);
+ if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
+ lp_config_set_float(cfg,logsection,"quality",cl->quality);
+ lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled);
+ lp_config_set_string(cfg,logsection,"call_id",cl->call_id);
+ }
+ for(;imax_call_logs;++i){
+ snprintf(logsection,sizeof(logsection),"call_log_%i",i);
+ lp_config_clean_section(cfg,logsection);
+ }
+}
+
+void call_logs_read_from_config_file(LinphoneCore *lc){
+ char logsection[32];
+ int i;
+ const char *tmp;
+ uint64_t sec;
+ LpConfig *cfg=lc->config;
+ for(i=0;;++i){
+ snprintf(logsection,sizeof(logsection),"call_log_%i",i);
+ if (lp_config_has_section(cfg,logsection)){
+ LinphoneCallLog *cl;
+ LinphoneAddress *from=NULL,*to=NULL;
+ tmp=lp_config_get_string(cfg,logsection,"from",NULL);
+ if (tmp) from=linphone_address_new(tmp);
+ tmp=lp_config_get_string(cfg,logsection,"to",NULL);
+ if (tmp) to=linphone_address_new(tmp);
+ if (!from || !to)
+ continue;
+ cl=linphone_call_log_new(lp_config_get_int(cfg,logsection,"dir",0),from,to);
+ cl->status=lp_config_get_int(cfg,logsection,"status",0);
+ sec=lp_config_get_int64(cfg,logsection,"start_date_time",0);
+ if (sec) {
+ /*new call log format with date expressed in seconds */
+ cl->start_date_time=(time_t)sec;
+ set_call_log_date(cl,cl->start_date_time);
+ }else{
+ tmp=lp_config_get_string(cfg,logsection,"start_date",NULL);
+ if (tmp) {
+ strncpy(cl->start_date,tmp,sizeof(cl->start_date));
+ cl->start_date_time=string_to_time(cl->start_date);
+ }
+ }
+ cl->duration=lp_config_get_int(cfg,logsection,"duration",0);
+ tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
+ if (tmp) cl->refkey=ms_strdup(tmp);
+ cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
+ cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0);
+ tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
+ if (tmp) cl->call_id=ms_strdup(tmp);
+ lc->call_logs=ms_list_append(lc->call_logs,cl);
+ }else break;
+ }
+}
+
+
+/*******************************************************************************
+ * Public functions *
+ ******************************************************************************/
+
+const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
+ return cl->call_id;
+}
+
+LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl){
+ return cl->dir;
+}
+
+int linphone_call_log_get_duration(LinphoneCallLog *cl){
+ return cl->duration;
+}
+
+LinphoneAddress *linphone_call_log_get_from_address(LinphoneCallLog *cl){
+ return cl->from;
+}
+
+const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){
+ return &cl->local_stats;
+}
+
+float linphone_call_log_get_quality(LinphoneCallLog *cl){
+ return cl->quality;
+}
+
+const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){
+ return cl->refkey;
+}
+
+LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl){
+ return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to;
+}
+
+const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){
+ return &cl->remote_stats;
+}
+
+time_t linphone_call_log_get_start_date(LinphoneCallLog *cl){
+ return cl->start_date_time;
+}
+
+LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl){
+ return cl->status;
+}
+
+LinphoneAddress *linphone_call_log_get_to_address(LinphoneCallLog *cl){
+ return cl->to;
+}
+
+void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){
+ if (cl->refkey!=NULL){
+ ms_free(cl->refkey);
+ cl->refkey=NULL;
+ }
+ if (refkey) cl->refkey=ms_strdup(refkey);
+}
+
+char * linphone_call_log_to_str(LinphoneCallLog *cl){
+ char *status;
+ char *tmp;
+ char *from=linphone_address_as_string (cl->from);
+ char *to=linphone_address_as_string (cl->to);
+ switch(cl->status){
+ case LinphoneCallAborted:
+ status=_("aborted");
+ break;
+ case LinphoneCallSuccess:
+ status=_("completed");
+ break;
+ case LinphoneCallMissed:
+ status=_("missed");
+ break;
+ default:
+ status=_("unknown");
+ }
+ tmp=ms_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
+ (cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
+ cl->start_date,
+ from,
+ to,
+ status,
+ cl->duration/60,
+ cl->duration%60);
+ ms_free(from);
+ ms_free(to);
+ return tmp;
+}
+
+bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl) {
+ return cl->video_enabled;
+}
+
+
+/*******************************************************************************
+ * Reference and user data handling functions *
+ ******************************************************************************/
+
+void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) {
+ return cl->user_data;
+}
+
+void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) {
+ cl->user_data = ud;
+}
+
+LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl) {
+ belle_sip_object_ref(cl);
+ return cl;
+}
+
+void linphone_call_log_unref(LinphoneCallLog *cl) {
+ belle_sip_object_unref(cl);
+}
+
+/*******************************************************************************
+ * Constructor and destructor functions *
+ ******************************************************************************/
+
+static void _linphone_call_log_destroy(LinphoneCallLog *cl){
+ if (cl->from!=NULL) linphone_address_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]);
+}
+
+LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to){
+ LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog);
+ cl->dir=dir;
+ cl->start_date_time=time(NULL);
+ set_call_log_date(cl,cl->start_date_time);
+ cl->from=from;
+ cl->to=to;
+ cl->status=LinphoneCallAborted; /*default status*/
+ cl->quality=-1;
+
+ cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
+ cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
+ cl->connected_date_time=0;
+ return cl;
+}
+
+/* DEPRECATED */
+void linphone_call_log_destroy(LinphoneCallLog *cl) {
+ belle_sip_object_unref(cl);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallLog);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t,
+ (belle_sip_object_destroy_t)_linphone_call_log_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
diff --git a/coreapi/call_log.h b/coreapi/call_log.h
new file mode 100644
index 000000000..6a3ec8dab
--- /dev/null
+++ b/coreapi/call_log.h
@@ -0,0 +1,244 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __LINPHONE_CALL_LOG_H__
+#define __LINPHONE_CALL_LOG_H__
+
+/**
+ * @addtogroup call_logs
+ * @{
+**/
+
+
+/*******************************************************************************
+ * Structures and enums *
+ ******************************************************************************/
+
+/**
+ * Enum representing the direction of a call.
+**/
+enum _LinphoneCallDir {
+ LinphoneCallOutgoing, /**< outgoing calls*/
+ LinphoneCallIncoming /**< incoming calls*/
+};
+
+/**
+ * Typedef for enum
+**/
+typedef enum _LinphoneCallDir LinphoneCallDir;
+
+/**
+ * Enum representing the status of a call
+**/
+typedef enum _LinphoneCallStatus {
+ LinphoneCallSuccess, /**< The call was sucessful */
+ LinphoneCallAborted, /**< The call was aborted */
+ LinphoneCallMissed, /**< The call was missed (unanswered) */
+ LinphoneCallDeclined /**< The call was declined, either locally or by remote end */
+} LinphoneCallStatus;
+
+/**
+ * Structure representing a call log.
+**/
+typedef struct _LinphoneCallLog LinphoneCallLog;
+
+
+/*******************************************************************************
+ * Public functions *
+ ******************************************************************************/
+
+/**
+ * Get the call ID used by the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The call ID used by the call as a string.
+**/
+LINPHONE_PUBLIC const char * linphone_call_log_get_call_id(const LinphoneCallLog *cl);
+
+/**
+ * Get the direction of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The direction of the call.
+**/
+LINPHONE_PUBLIC LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl);
+
+/**
+ * Get the duration of the call since connected.
+ * @param[in] cl LinphoneCallLog object
+ * @return The duration of the call in seconds.
+**/
+LINPHONE_PUBLIC int linphone_call_log_get_duration(LinphoneCallLog *cl);
+
+/**
+ * Get the origin address (ie from) of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The origin address (ie from) of the call.
+**/
+LINPHONE_PUBLIC LinphoneAddress * linphone_call_log_get_from_address(LinphoneCallLog *cl);
+
+/**
+ * Get the RTP statistics computed locally regarding the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The RTP statistics that have been computed locally for the call.
+**/
+LINPHONE_PUBLIC const rtp_stats_t * linphone_call_log_get_local_stats(const LinphoneCallLog *cl);
+
+/**
+ * Get the overall quality indication of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The overall quality indication of the call.
+**/
+LINPHONE_PUBLIC float linphone_call_log_get_quality(LinphoneCallLog *cl);
+
+/**
+ * Get the persistent reference key associated to the call log.
+ *
+ * The reference key can be for example an id to an external database.
+ * It is stored in the config file, thus can survive to process exits/restarts.
+ *
+ * @param[in] cl LinphoneCallLog object
+ * @return The reference key string that has been associated to the call log, or NULL if none has been associated.
+**/
+LINPHONE_PUBLIC const char * linphone_call_log_get_ref_key(const LinphoneCallLog *cl);
+
+/**
+ * Get the remote address (that is from or to depending on call direction).
+ * @param[in] cl LinphoneCallLog object
+ * @return The remote address of the call.
+**/
+LINPHONE_PUBLIC LinphoneAddress * linphone_call_log_get_remote_address(LinphoneCallLog *cl);
+
+/**
+ * Get the RTP statistics computed by the remote end and sent back via RTCP.
+ * @note Not implemented yet.
+ * @param[in] cl LinphoneCallLog object
+ * @return The RTP statistics that have been computed by the remote end for the call.
+**/
+LINPHONE_PUBLIC const rtp_stats_t * linphone_call_log_get_remote_stats(const LinphoneCallLog *cl);
+
+/**
+ * Get the start date of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The date of the beginning of the call.
+**/
+LINPHONE_PUBLIC time_t linphone_call_log_get_start_date(LinphoneCallLog *cl);
+
+/**
+ * Get the status of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The status of the call.
+**/
+LINPHONE_PUBLIC LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl);
+
+/**
+ * Get the destination address (ie to) of the call.
+ * @param[in] cl LinphoneCallLog object
+ * @return The destination address (ie to) of the call.
+**/
+LINPHONE_PUBLIC LinphoneAddress * linphone_call_log_get_to_address(LinphoneCallLog *cl);
+
+/**
+ * Associate a persistent reference key to the call log.
+ *
+ * The reference key can be for example an id to an external database.
+ * It is stored in the config file, thus can survive to process exits/restarts.
+ *
+ * @param[in] cl LinphoneCallLog object
+ * @param[in] refkey The reference key string to associate to the call log.
+**/
+LINPHONE_PUBLIC void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey);
+
+/**
+ * Tell whether video was enabled at the end of the call or not.
+ * @param[in] cl LinphoneCallLog object
+ * @return A boolean value telling whether video was enabled at the end of the call.
+**/
+LINPHONE_PUBLIC bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl);
+
+/**
+ * Get a human readable string describing the call.
+ * @note: the returned string must be freed by the application (use ms_free()).
+ * @param[in] cl LinphoneCallLog object
+ * @return A human readable string describing the call.
+**/
+LINPHONE_PUBLIC char * linphone_call_log_to_str(LinphoneCallLog *cl);
+
+
+/*******************************************************************************
+ * Reference and user data handling functions *
+ ******************************************************************************/
+
+/**
+ * Get the user data associated with the call log.
+ * @param[in] cl LinphoneCallLog object
+ * @return The user data associated with the call log.
+**/
+LINPHONE_PUBLIC void *linphone_call_log_get_user_data(const LinphoneCallLog *cl);
+
+/**
+ * Assign a user data to the call log.
+ * @param[in] cl LinphoneCallLog object
+ * @param[in] ud The user data to associate with the call log.
+**/
+LINPHONE_PUBLIC void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud);
+
+/**
+ * Acquire a reference to the call log.
+ * @param[in] cl LinphoneCallLog object
+ * @return The same LinphoneCallLog object
+**/
+LINPHONE_PUBLIC LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl);
+
+/**
+ * Release a reference to the call log.
+ * @param[in] cl LinphoneCallLog object
+**/
+LINPHONE_PUBLIC void linphone_call_log_unref(LinphoneCallLog *cl);
+
+
+/*******************************************************************************
+ * DEPRECATED *
+ ******************************************************************************/
+
+/** @deprecated Use linphone_call_log_get_from_address() instead. */
+#define linphone_call_log_get_from(cl) linphone_call_log_get_from_address(cl)
+
+/** @deprecated Use linphone_call_log_get_to_address() instead. */
+#define linphone_call_log_get_to(cl) linphone_call_log_get_to_address(cl)
+
+/** @deprecated Use linphone_call_log_set_user_data() instead. */
+#define linphone_call_log_set_user_pointer(cl, ud) linphone_call_log_set_user_data(cl, ud)
+
+/** @deprecated Use linphone_call_log_get_user_data() instead. */
+#define linphone_call_log_get_user_pointer(cl) linphone_call_log_get_user_data(cl)
+
+/**
+ * Destroy a LinphoneCallLog.
+ * @param cl LinphoneCallLog object
+ * @deprecated Use linphone_call_log_unref() instead.
+ */
+LINPHONE_PUBLIC void linphone_call_log_destroy(LinphoneCallLog *cl);
+
+
+/**
+ * @}
+**/
+
+
+#endif /* __LINPHONE_CALL_LOG_H__ */
diff --git a/coreapi/call_params.c b/coreapi/call_params.c
new file mode 100644
index 000000000..f9a1a1a4d
--- /dev/null
+++ b/coreapi/call_params.c
@@ -0,0 +1,284 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "private.h"
+
+
+/*******************************************************************************
+ * Internal functions *
+ ******************************************************************************/
+
+SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params) {
+ if ((params->media_encryption == LinphoneMediaEncryptionSRTP) && params->avpf_enabled) return SalProtoRtpSavpf;
+ if (params->media_encryption == LinphoneMediaEncryptionSRTP) return SalProtoRtpSavp;
+ if ((params->media_encryption == LinphoneMediaEncryptionDTLS) && params->avpf_enabled) return SalProtoUdpTlsRtpSavpf;
+ if (params->media_encryption == LinphoneMediaEncryptionDTLS) return SalProtoUdpTlsRtpSavp;
+ if (params->avpf_enabled) return SalProtoRtpAvpf;
+ return SalProtoRtpAvp;
+}
+
+SalStreamDir sal_dir_from_call_params_dir(LinphoneMediaDirection cpdir) {
+ switch (cpdir) {
+ case LinphoneMediaDirectionInactive:
+ return SalStreamInactive;
+ case LinphoneMediaDirectionSendOnly:
+ return SalStreamSendOnly;
+ case LinphoneMediaDirectionRecvOnly:
+ return SalStreamRecvOnly;
+ case LinphoneMediaDirectionSendRecv:
+ return SalStreamSendRecv;
+ case LinphoneMediaDirectionInvalid:
+ ms_error("LinphoneMediaDirectionInvalid shall not be used.");
+ return SalStreamInactive;
+ }
+ return SalStreamSendRecv;
+}
+
+LinphoneMediaDirection media_direction_from_sal_stream_dir(SalStreamDir dir){
+ switch (dir) {
+ case SalStreamInactive:
+ return LinphoneMediaDirectionInactive;
+ case SalStreamSendOnly:
+ return LinphoneMediaDirectionSendOnly;
+ case SalStreamRecvOnly:
+ return LinphoneMediaDirectionRecvOnly;
+ case SalStreamSendRecv:
+ return LinphoneMediaDirectionSendRecv;
+ }
+ return LinphoneMediaDirectionSendRecv;
+}
+
+SalStreamDir get_audio_dir_from_call_params(const LinphoneCallParams *params) {
+ return sal_dir_from_call_params_dir(linphone_call_params_get_audio_direction(params));
+}
+
+SalStreamDir get_video_dir_from_call_params(const LinphoneCallParams *params) {
+ return sal_dir_from_call_params_dir(linphone_call_params_get_video_direction(params));
+}
+
+
+/*******************************************************************************
+ * Public functions *
+ ******************************************************************************/
+
+void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char *header_value){
+ params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value);
+}
+
+LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
+ LinphoneCallParams *ncp=linphone_call_params_new();
+ memcpy(ncp,cp,sizeof(LinphoneCallParams));
+ if (cp->record_file) ncp->record_file=ms_strdup(cp->record_file);
+ if (cp->session_name) ncp->session_name=ms_strdup(cp->session_name);
+ /*
+ * 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);
+
+ return ncp;
+}
+
+bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
+ return cp->real_early_media;
+}
+
+void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
+ cp->real_early_media=enabled;
+}
+
+void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){
+ cp->low_bandwidth=enabled;
+}
+
+void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
+ cp->has_video=enabled;
+ if (enabled && cp->video_dir==LinphoneMediaDirectionInactive)
+ cp->video_dir=LinphoneMediaDirectionSendRecv;
+}
+
+const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name){
+ return sal_custom_header_find(params->custom_headers,header_name);
+}
+
+bool_t linphone_call_params_get_local_conference_mode(const LinphoneCallParams *cp){
+ return cp->in_conference;
+}
+
+LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
+ return cp->media_encryption;
+}
+
+LinphonePrivacyMask linphone_call_params_get_privacy(const LinphoneCallParams *params) {
+ return params->privacy;
+}
+
+float linphone_call_params_get_received_framerate(const LinphoneCallParams *cp){
+ return cp->received_fps;
+}
+
+MSVideoSize linphone_call_params_get_received_video_size(const LinphoneCallParams *cp) {
+ return cp->recv_vsize;
+}
+
+const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp){
+ return cp->record_file;
+}
+
+const char * linphone_call_params_get_rtp_profile(const LinphoneCallParams *cp) {
+ return sal_media_proto_to_string(get_proto_from_call_params(cp));
+}
+
+float linphone_call_params_get_sent_framerate(const LinphoneCallParams *cp){
+ return cp->sent_fps;
+}
+
+MSVideoSize linphone_call_params_get_sent_video_size(const LinphoneCallParams *cp) {
+ return cp->sent_vsize;
+}
+
+const char *linphone_call_params_get_session_name(const LinphoneCallParams *cp){
+ return cp->session_name;
+}
+
+const LinphonePayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
+ return cp->audio_codec;
+}
+
+const LinphonePayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
+ return cp->video_codec;
+}
+
+bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
+ return cp->low_bandwidth;
+}
+
+void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
+ cp->audio_bw=bandwidth;
+}
+
+void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, LinphoneMediaEncryption e) {
+ cp->media_encryption = e;
+}
+
+void linphone_call_params_set_privacy(LinphoneCallParams *params, LinphonePrivacyMask privacy) {
+ params->privacy=privacy;
+}
+
+void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
+ if (cp->record_file){
+ ms_free(cp->record_file);
+ cp->record_file=NULL;
+ }
+ if (path) cp->record_file=ms_strdup(path);
+}
+
+void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *name){
+ if (cp->session_name){
+ ms_free(cp->session_name);
+ cp->session_name=NULL;
+ }
+ if (name) cp->session_name=ms_strdup(name);
+}
+
+bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
+ return cp->has_video;
+}
+
+LinphoneMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp) {
+ return cp->audio_dir;
+}
+
+LinphoneMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp) {
+ return cp->video_dir;
+}
+
+void linphone_call_params_set_audio_direction(LinphoneCallParams *cp,LinphoneMediaDirection dir) {
+ cp->audio_dir=dir;
+}
+
+void linphone_call_params_set_video_direction(LinphoneCallParams *cp,LinphoneMediaDirection dir) {
+ cp->video_dir=dir;
+}
+
+
+/*******************************************************************************
+ * Reference and user data handling functions *
+ ******************************************************************************/
+
+void *linphone_call_params_get_user_data(const LinphoneCallParams *cp) {
+ return cp->user_data;
+}
+
+void linphone_call_params_set_user_data(LinphoneCallParams *cp, void *ud) {
+ cp->user_data = ud;
+}
+
+LinphoneCallParams * linphone_call_params_ref(LinphoneCallParams *cp) {
+ belle_sip_object_ref(cp);
+ return cp;
+}
+
+void linphone_call_params_unref(LinphoneCallParams *cp) {
+ belle_sip_object_unref(cp);
+}
+
+void linphone_call_params_enable_audio_multicast(LinphoneCallParams *params, bool_t yesno) {
+ params->audio_multicast_enabled=yesno;
+}
+
+bool_t linphone_call_params_audio_multicast_enabled(const LinphoneCallParams *params) {
+ return params->audio_multicast_enabled;
+}
+
+void linphone_call_params_enable_video_multicast(LinphoneCallParams *params, bool_t yesno) {
+ params->video_multicast_enabled=yesno;
+}
+bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *params) {
+ return params->video_multicast_enabled;
+}
+
+/*******************************************************************************
+ * Constructor and destructor functions *
+ ******************************************************************************/
+
+static void _linphone_call_params_destroy(LinphoneCallParams *cp){
+ if (cp->record_file) ms_free(cp->record_file);
+ if (cp->custom_headers) sal_custom_header_free(cp->custom_headers);
+}
+
+LinphoneCallParams * linphone_call_params_new(void) {
+ LinphoneCallParams *cp=belle_sip_object_new(LinphoneCallParams);
+ cp->audio_dir=LinphoneMediaDirectionSendRecv;
+ cp->video_dir=LinphoneMediaDirectionSendRecv;
+ return cp;
+}
+
+/* DEPRECATED */
+void linphone_call_params_destroy(LinphoneCallParams *cp) {
+ linphone_call_params_unref(cp);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallParams);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallParams, belle_sip_object_t,
+ (belle_sip_object_destroy_t)_linphone_call_params_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
diff --git a/coreapi/call_params.h b/coreapi/call_params.h
new file mode 100644
index 000000000..a45eb8999
--- /dev/null
+++ b/coreapi/call_params.h
@@ -0,0 +1,381 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __LINPHONE_CALL_PARAMS_H__
+#define __LINPHONE_CALL_PARAMS_H__
+
+/**
+ * @addtogroup call_control
+ * @{
+**/
+
+
+/*******************************************************************************
+ * Structures and enums *
+ ******************************************************************************/
+/**
+ * Indicates for a given media the stream direction
+ * */
+enum _LinphoneMediaDirection {
+ LinphoneMediaDirectionInvalid = -1,
+ LinphoneMediaDirectionInactive, /** No active media not supported yet*/
+ LinphoneMediaDirectionSendOnly, /** Send only mode*/
+ LinphoneMediaDirectionRecvOnly, /** recv only mode*/
+ LinphoneMediaDirectionSendRecv, /** send receive*/
+};
+/**
+ * Typedef for enum
+**/
+typedef enum _LinphoneMediaDirection LinphoneMediaDirection;
+
+/**
+ * Private structure definition for LinphoneCallParams.
+**/
+struct _LinphoneCallParams;
+
+/**
+ * The LinphoneCallParams is an object containing various call related parameters.
+ * It can be used to retrieve parameters from a currently running call or modify
+ * the call's characteristics dynamically.
+**/
+typedef struct _LinphoneCallParams LinphoneCallParams;
+
+
+/*******************************************************************************
+ * Public functions *
+ ******************************************************************************/
+
+/**
+ * Add a custom SIP header in the INVITE for a call.
+ * @param[in] cp The #LinphoneCallParams to add a custom SIP header to.
+ * @param[in] header_name The name of the header to add.
+ * @param[in] header_value The content of the header to add.
+**/
+LINPHONE_PUBLIC void linphone_call_params_add_custom_header(LinphoneCallParams *cp, const char *header_name, const char *header_value);
+
+/**
+ * Copy an existing LinphoneCallParams object to a new LinphoneCallParams object.
+ * @param[in] cp The LinphoneCallParams object to copy.
+ * @return A copy of the LinphoneCallParams object.
+**/
+LINPHONE_PUBLIC LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp);
+
+/**
+ * Indicate whether sending of early media was enabled.
+ * @param[in] cp LinphoneCallParams object
+ * @return A boolean value telling whether sending of early media was enabled.
+**/
+LINPHONE_PUBLIC bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp);
+
+/**
+ * Enable sending of real early media (during outgoing calls).
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] enabled A boolean value telling whether to enable early media sending or not.
+**/
+LINPHONE_PUBLIC void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled);
+
+/**
+ * Indicate low bandwith mode.
+ * Configuring a call to low bandwidth mode will result in the core to activate several settings for the call in order to ensure that bitrate usage
+ * is lowered to the minimum possible. Typically, ptime (packetization time) will be increased, audio codec's output bitrate will be targetted to 20kbit/s provided
+ * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] enabled A boolean value telling whether to activate the low bandwidth mode or not.
+**/
+LINPHONE_PUBLIC void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled);
+
+/**
+ * Enable video stream.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] enabled A boolean value telling whether to enable video or not.
+**/
+LINPHONE_PUBLIC void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled);
+
+/**
+ * Get a custom SIP header.
+ * @param[in] cp The #LinphoneCallParams to get the custom SIP header from.
+ * @param[in] header_name The name of the header to get.
+ * @return The content of the header or NULL if not found.
+**/
+LINPHONE_PUBLIC const char *linphone_call_params_get_custom_header(const LinphoneCallParams *cp, const char *header_name);
+
+/**
+ * Tell whether the call is part of the locally managed conference.
+ * @param[in] cp LinphoneCallParams object
+ * @return A boolean value telling whether the call is part of the locally managed conference.
+**/
+LINPHONE_PUBLIC bool_t linphone_call_params_get_local_conference_mode(const LinphoneCallParams *cp);
+
+/**
+ * Get the kind of media encryption selected for the call.
+ * @param[in] cp LinphoneCallParams object
+ * @return The kind of media encryption selected for the call.
+**/
+LINPHONE_PUBLIC LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp);
+
+/**
+ * Get requested level of privacy for the call.
+ * @param[in] cp LinphoneCallParams object
+ * @return The privacy mode used for the call.
+**/
+LINPHONE_PUBLIC LinphonePrivacyMask linphone_call_params_get_privacy(const LinphoneCallParams *cp);
+
+/**
+ * Get the framerate of the video that is received.
+ * @param[in] cp LinphoneCallParams object
+ * @return The actual received framerate in frames per seconds, 0 if not available.
+ */
+LINPHONE_PUBLIC float linphone_call_params_get_received_framerate(const LinphoneCallParams *cp);
+
+/**
+ * Get the size of the video that is received.
+ * @param[in] cp LinphoneCallParams object
+ * @return The received video size or MS_VIDEO_SIZE_UNKNOWN if not available.
+ */
+LINPHONE_PUBLIC MSVideoSize linphone_call_params_get_received_video_size(const LinphoneCallParams *cp);
+
+/**
+ * Get the path for the audio recording of the call.
+ * @param[in] cp LinphoneCallParams object
+ * @return The path to the audio recording of the call.
+**/
+LINPHONE_PUBLIC const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp);
+
+/**
+ * Get the RTP profile being used.
+ * @param[in] cp #LinphoneCallParams object
+ * @return The RTP profile.
+ */
+LINPHONE_PUBLIC const char * linphone_call_params_get_rtp_profile(const LinphoneCallParams *cp);
+
+/**
+ * Get the framerate of the video that is sent.
+ * @param[in] cp LinphoneCallParams object
+ * @return The actual sent framerate in frames per seconds, 0 if not available.
+ */
+LINPHONE_PUBLIC float linphone_call_params_get_sent_framerate(const LinphoneCallParams *cp);
+
+/**
+ * Gets the size of the video that is sent.
+ * @param[in] cp LinphoneCalParams object
+ * @return The sent video size or MS_VIDEO_SIZE_UNKNOWN if not available.
+ */
+LINPHONE_PUBLIC MSVideoSize linphone_call_params_get_sent_video_size(const LinphoneCallParams *cp);
+
+/**
+ * Get the session name of the media session (ie in SDP).
+ * Subject from the SIP message can be retrieved using linphone_call_params_get_custom_header() and is different.
+ * @param[in] cp LinphoneCallParams object
+ * @return The session name of the media session.
+**/
+LINPHONE_PUBLIC const char *linphone_call_params_get_session_name(const LinphoneCallParams *cp);
+
+/**
+ * Get the audio codec used in the call, described as a LinphonePayloadType object.
+ * @param[in] cp LinphoneCallParams object
+ * @return The LinphonePayloadType object corresponding to the audio codec being used in the call.
+**/
+LINPHONE_PUBLIC const LinphonePayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp);
+
+/**
+ * Get the video codec used in the call, described as a LinphonePayloadType structure.
+ * @param[in] cp LinphoneCallParams object
+ * @return The LinphonePayloadType object corresponding to the video codec being used in the call.
+**/
+LINPHONE_PUBLIC const LinphonePayloadType* linphone_call_params_get_used_video_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.
+ * An application that would have reliable way to know network capacity may not use activate_edge_workarounds=1 but instead manually configure
+ * low bandwidth mode with linphone_call_params_enable_low_bandwidth().
+ * When enabled, this param may transform a call request with video in audio only mode.
+ * @param[in] cp LinphoneCallParams object
+ * @return A boolean value telling whether the low bandwidth mode has been configured/detected.
+ */
+LINPHONE_PUBLIC bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp);
+
+/**
+ * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
+ * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] bw The audio bandwidth limit to set in kbit/s.
+**/
+LINPHONE_PUBLIC void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bw);
+
+/**
+ * Set requested media encryption for a call.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] enc The media encryption to use for the call.
+**/
+LINPHONE_PUBLIC void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, LinphoneMediaEncryption enc);
+
+/**
+ * Set requested level of privacy for the call.
+ * \xmlonly javascript \endxmlonly
+ * @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);
+
+/**
+ * Enable recording of the call.
+ * This function must be used before the call parameters are assigned to the call.
+ * The call recording can be started and paused after the call is established with
+ * linphone_call_start_recording() and linphone_call_pause_recording().
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] path A string containing the path and filename of the file where audio/video streams are to be written.
+ * The filename must have either .mkv or .wav extention. The video stream will be written only if a MKV file is given.
+**/
+LINPHONE_PUBLIC void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path);
+
+/**
+ * Set the session name of the media session (ie in SDP).
+ * Subject from the SIP message (which is different) can be set using linphone_call_params_set_custom_header().
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] name The session name to be used.
+**/
+LINPHONE_PUBLIC void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *name);
+
+/**
+ * Tell whether video is enabled or not.
+ * @param[in] cp LinphoneCallParams object
+ * @return A boolean value telling whether video is enabled or not.
+**/
+LINPHONE_PUBLIC bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp);
+
+/**
+ * Get the audio stream direction.
+ * @param[in] cl 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
+ * @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.
+**/
+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.
+**/
+LINPHONE_PUBLIC void linphone_call_params_set_video_direction(LinphoneCallParams *cp, LinphoneMediaDirection dir);
+
+
+/*******************************************************************************
+ * Reference and user data handling functions *
+ ******************************************************************************/
+
+/**
+ * Get the user data associated with the call params.
+ * @param[in] cl 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] 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
+ * @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
+**/
+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 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);
+
+/**
+ * Use to get multicast state of audio stream.
+ * @param core #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);
+
+/**
+ * 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 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);
+/**
+ * Use to get multicast state of video stream.
+ * @param core #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);
+
+
+/*******************************************************************************
+ * DEPRECATED *
+ ******************************************************************************/
+
+/** @deprecated Use linphone_call_params_get_local_conference_mode() instead. */
+#define linphone_call_params_local_conference_mode linphone_call_params_get_local_conference_mode
+
+/**
+ * Destroy a LinphoneCallParams object.
+ * @param[in] cp LinphoneCallParams object
+ * @deprecated Use linphone_call_params_unref() instead.
+**/
+LINPHONE_PUBLIC void linphone_call_params_destroy(LinphoneCallParams *cp);
+
+
+/**
+ * @}
+**/
+
+
+#endif /* __LINPHONE_CALL_PARAMS_H__ */
diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c
index cf2b5cb4a..5ce07dd3c 100644
--- a/coreapi/callbacks.c
+++ b/coreapi/callbacks.c
@@ -25,30 +25,32 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/mediastream.h"
#include "lpconfig.h"
-static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
+// stat
+#ifndef _WIN32
+#include
+#include
+#include
+#endif
+
+static void register_failure(SalOp *op);
static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd) {
- if (call->params.in_conference != call->current_params.in_conference) return SAL_MEDIA_DESCRIPTION_CHANGED;
- if (call->up_bw != linphone_core_get_upload_bandwidth(call->core)) return SAL_MEDIA_DESCRIPTION_CHANGED;
- return sal_media_description_equals(oldmd, newmd);
+ int result=0;
+ 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);
+ return result;
}
void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) {
- SalStreamDescription *old_audiodesc = NULL;
- SalStreamDescription *old_videodesc = NULL;
SalStreamDescription *new_audiodesc = NULL;
SalStreamDescription *new_videodesc = NULL;
char *rtp_addr, *rtcp_addr;
int i;
- for (i = 0; i < old_md->n_active_streams; i++) {
- if (old_md->streams[i].type == SalAudio) {
- old_audiodesc = &old_md->streams[i];
- } else if (old_md->streams[i].type == SalVideo) {
- old_videodesc = &old_md->streams[i];
- }
- }
- for (i = 0; i < new_md->n_active_streams; i++) {
+ for (i = 0; i < new_md->nb_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];
} else if (new_md->streams[i].type == SalVideo) {
@@ -59,110 +61,156 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c
rtp_addr = (new_audiodesc->rtp_addr[0] != '\0') ? new_audiodesc->rtp_addr : new_md->addr;
rtcp_addr = (new_audiodesc->rtcp_addr[0] != '\0') ? new_audiodesc->rtcp_addr : new_md->addr;
ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port);
- rtp_session_set_remote_addr_full(call->audiostream->ms.session, rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port);
+ rtp_session_set_remote_addr_full(call->audiostream->ms.sessions.rtp_session, rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port);
}
#ifdef VIDEO_ENABLED
if (call->videostream && new_videodesc) {
rtp_addr = (new_videodesc->rtp_addr[0] != '\0') ? new_videodesc->rtp_addr : new_md->addr;
rtcp_addr = (new_videodesc->rtcp_addr[0] != '\0') ? new_videodesc->rtcp_addr : new_md->addr;
ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port);
- rtp_session_set_remote_addr_full(call->videostream->ms.session, rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port);
+ rtp_session_set_remote_addr_full(call->videostream->ms.sessions.rtp_session, rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port);
}
+#else
+ (void)new_videodesc;
#endif
+}
- /* Copy address and port values from new_md to old_md since we will keep old_md as resultdesc */
- strcpy(old_md->addr, new_md->addr);
- if (old_audiodesc && new_audiodesc) {
- strcpy(old_audiodesc->rtp_addr, new_audiodesc->rtp_addr);
- strcpy(old_audiodesc->rtcp_addr, new_audiodesc->rtcp_addr);
- old_audiodesc->rtp_port = new_audiodesc->rtp_port;
- old_audiodesc->rtcp_port = new_audiodesc->rtcp_port;
+static void _clear_early_media_destinations(LinphoneCall *call, MediaStream *ms){
+ RtpSession *session=ms->sessions.rtp_session;
+ rtp_session_clear_aux_remote_addr(session);
+ if (!call->ice_session) rtp_session_set_symmetric_rtp(session,linphone_core_symmetric_rtp_enabled(call->core));/*restore symmetric rtp if ICE is not used*/
+}
+
+static void clear_early_media_destinations(LinphoneCall *call){
+ if (call->audiostream){
+ _clear_early_media_destinations(call,(MediaStream*)call->audiostream);
}
- if (old_videodesc && new_videodesc) {
- strcpy(old_videodesc->rtp_addr, new_videodesc->rtp_addr);
- strcpy(old_videodesc->rtcp_addr, new_videodesc->rtcp_addr);
- old_videodesc->rtp_port = new_videodesc->rtp_port;
- old_videodesc->rtcp_port = new_videodesc->rtcp_port;
+ if (call->videostream){
+ _clear_early_media_destinations(call,(MediaStream*)call->videostream);
}
}
-void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
+static void prepare_early_media_forking(LinphoneCall *call){
+ /*we need to disable symmetric rtp otherwise our outgoing streams will be switching permanently between the multiple destinations*/
+ if (call->audiostream){
+ rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session,FALSE);
+ }
+ if (call->videostream){
+ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE);
+ }
+}
+
+void linphone_call_update_frozen_payloads(LinphoneCall *call, SalMediaDescription *result_desc){
+ SalMediaDescription *local=call->localdesc;
+ int i;
+ for(i=0;inb_streams;++i){
+ MSList *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));
+ 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 : "");
+ }
+ }
+ }
+}
+
+void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state){
SalMediaDescription *oldmd=call->resultdesc;
-
- if (lc->ringstream!=NULL){
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
+ int md_changed=0;
+
+
+ if (!((call->state == LinphoneCallIncomingEarlyMedia) && (linphone_core_get_ring_during_incoming_early_media(lc)))) {
+ linphone_core_stop_ringing(lc);
}
- if (new_md!=NULL){
- sal_media_description_ref(new_md);
- call->media_pending=FALSE;
- }else{
- call->media_pending=TRUE;
+ if (!new_md) {
+ 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));
+ }
+ sal_media_description_ref(new_md);
call->resultdesc=new_md;
- if ((call->audiostream && call->audiostream->ms.ticker) || (call->videostream && call->videostream->ms.ticker)){
+ if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){
+ clear_early_media_destinations(call);
+
/* we already started media: check if we really need to restart it*/
if (oldmd){
- int md_changed = media_parameters_changed(call, oldmd, new_md);
- if ((md_changed & SAL_MEDIA_DESCRIPTION_CODEC_CHANGED) || call->playing_ringbacktone) {
+ md_changed = media_parameters_changed(call, oldmd, new_md);
+ if ((md_changed & ( SAL_MEDIA_DESCRIPTION_CODEC_CHANGED
+ |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED
+ |SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED
+ |SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION ))){
ms_message("Media descriptions are different, need to restart the streams.");
+ } else if ( call->playing_ringbacktone) {
+ ms_message("Playing ringback tone, will restart the streams.");
} else {
if (md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) {
- /*as nothing has changed, keep the oldmd */
- call->resultdesc=oldmd;
- sal_media_description_unref(new_md);
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));
if (call->audiostream)
- linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc));
+ linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc));
#ifdef VIDEO_ENABLED
- if (call->videostream && call->camera_active)
- video_stream_change_camera(call->videostream,lc->video_conf.device );
+ if (call->videostream && call->camera_enabled)
+ video_stream_change_camera(call->videostream,linphone_call_get_video_device(call));
#endif
}
+ /*FIXME ZRTP, might be restarted in any cases ? */
ms_message("No need to restart streams, SDP is unchanged.");
- return;
- }
- else {
+ goto end;
+ }else {
if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED) {
ms_message("Network parameters have changed, update them.");
linphone_core_update_streams_destinations(lc, call, oldmd, new_md);
}
- if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED) {
+ if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) {
ms_message("Crypto parameters have changed, update them.");
linphone_call_update_crypto_parameters(call, oldmd, new_md);
}
- call->resultdesc = oldmd;
- sal_media_description_unref(new_md);
- return;
+ goto end;
}
}
}
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]);
+ }
linphone_call_init_media_streams (call);
}
- if (oldmd)
- sal_media_description_unref(oldmd);
-
- if (new_md) {
- bool_t all_muted=FALSE;
- bool_t send_ringbacktone=FALSE;
-
- if (call->audiostream==NULL){
- /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/
- linphone_call_init_media_streams (call);
- }
- if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){
- send_ringbacktone=TRUE;
- }
- if (call->state==LinphoneCallIncomingEarlyMedia ||
- (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){
- all_muted=TRUE;
- }
- linphone_call_start_media_streams(call,all_muted,send_ringbacktone);
+
+ if (call->audiostream==NULL){
+ /*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){
+ linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
+ }
+ linphone_call_update_frozen_payloads(call, new_md);
+ end:
+ if (oldmd)
+ sal_media_description_unref(oldmd);
+
}
#if 0
static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
@@ -170,7 +218,7 @@ static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, c
for(elem=lc->calls;elem!=NULL;elem=elem->next){
LinphoneCall *call=(LinphoneCall*)elem->data;
if (linphone_address_weak_equal(call->log->from,from) &&
- linphone_address_weak_equal(call->log->to, to)){
+ linphone_address_weak_equal(call->log->to, to)){
return TRUE;
}
}
@@ -180,7 +228,7 @@ static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, c
static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) {
MSList *elem;
- ms_warning(" searching for already_a_call_with_remote_address.");
+ ms_message("Searching for already_a_call_with_remote_address.");
for(elem=lc->calls;elem!=NULL;elem=elem->next){
const LinphoneCall *call=(LinphoneCall*)elem->data;
@@ -193,69 +241,111 @@ static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const L
return FALSE;
}
-static bool_t already_a_call_pending(LinphoneCore *lc){
- MSList *elem;
- for(elem=lc->calls;elem!=NULL;elem=elem->next){
- LinphoneCall *call=(LinphoneCall*)elem->data;
- if (call->state==LinphoneCallIncomingReceived
- || call->state==LinphoneCallOutgoingInit
- || call->state==LinphoneCallOutgoingProgress
- || call->state==LinphoneCallOutgoingEarlyMedia
- || call->state==LinphoneCallOutgoingRinging){
- return TRUE;
- }
- }
- return FALSE;
-}
static void call_received(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call;
- const char *from,*to;
- LinphoneAddress *from_addr, *to_addr;
- bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE);
-
+ char *alt_contact;
+ LinphoneAddress *from_addr=NULL;
+ LinphoneAddress *to_addr=NULL;
+ LinphoneAddress *from_address_to_search_if_me=NULL; /*address used to know if I'm the caller*/
+ SalMediaDescription *md;
+ const char * p_asserted_id;
+
/* first check if we can answer successfully to this invite */
- if (lc->presence_mode==LinphoneStatusBusy ||
- lc->presence_mode==LinphoneStatusOffline ||
- lc->presence_mode==LinphoneStatusDoNotDisturb ||
- lc->presence_mode==LinphoneStatusMoved){
- if (lc->presence_mode==LinphoneStatusBusy )
- sal_call_decline(h,SalReasonBusy,NULL);
- else if (lc->presence_mode==LinphoneStatusOffline)
- sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
- else if (lc->presence_mode==LinphoneStatusDoNotDisturb)
- sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
- else if (lc->alt_contact!=NULL && lc->presence_mode==LinphoneStatusMoved)
- sal_call_decline(h,SalReasonRedirect,lc->alt_contact);
+ 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);
+ }
+ break;
+ default:
+ break;
+ }
sal_op_release(h);
return;
}
+
if (!linphone_core_can_we_add_call(lc)){/*busy*/
sal_call_decline(h,SalReasonBusy,NULL);
sal_op_release(h);
return;
}
- from=sal_op_get_from(h);
- to=sal_op_get_to(h);
- from_addr=linphone_address_new(from);
- to_addr=linphone_address_new(to);
+ p_asserted_id = sal_custom_header_find(sal_op_get_recv_custom_header(h),"P-Asserted-Identity");
+ /*in some situation, better to trust the network rather than the UAC*/
+ if (lp_config_get_int(lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0)) {
+ LinphoneAddress *p_asserted_id_addr;
+ if (!p_asserted_id) {
+ ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from",h);
+ } else {
+ p_asserted_id_addr = linphone_address_new(p_asserted_id);
+ if (!p_asserted_id_addr) {
+ ms_warning("Unsupported P-Asserted-Identity header for op [%p] ",h);
+ } else {
+ ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]",p_asserted_id,sal_op_get_from(h),h);
+ from_addr=p_asserted_id_addr;
+ }
+ }
+ }
- if ((already_a_call_with_remote_address(lc,from_addr) && prevent_colliding_calls) || already_a_call_pending(lc)){
- ms_warning("Receiving another call while one is ringing or initiated, refusing this one with busy message.");
+ if (!from_addr)
+ from_addr=linphone_address_new(sal_op_get_from(h));
+ to_addr=linphone_address_new(sal_op_get_to(h));
+
+ if (sal_op_get_privacy(h) == SalPrivacyNone) {
+ from_address_to_search_if_me=linphone_address_clone(from_addr);
+ } else if (p_asserted_id) {
+ from_address_to_search_if_me = linphone_address_new(p_asserted_id);
+ } else {
+ ms_warning ("Hidden from identity, don't know if it's me");
+ }
+
+ if (from_address_to_search_if_me && already_a_call_with_remote_address(lc,from_address_to_search_if_me)){
+ char *addr = linphone_address_as_string(from_addr);
+ ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message.",addr);
sal_call_decline(h,SalReasonBusy,NULL);
sal_op_release(h);
linphone_address_destroy(from_addr);
linphone_address_destroy(to_addr);
+ linphone_address_destroy(from_address_to_search_if_me);
+ ms_free(addr);
return;
+ } else if (from_address_to_search_if_me) {
+ linphone_address_destroy(from_address_to_search_if_me);
}
-
+
call=linphone_call_new_incoming(lc,from_addr,to_addr,h);
-
+
+ linphone_call_make_local_media_description(call);
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ md=sal_call_get_final_media_description(call->op);
+ if (md){
+ if (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md)){
+ sal_call_decline(call->op,SalReasonNotAcceptable,NULL);
+ linphone_call_unref(call);
+ return;
+ }
+ }
+
/* the call is acceptable so we can now add it to our list */
linphone_core_add_call(lc,call);
linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */
+ 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)) {
/* Defer ringing until the end of the ICE candidates gathering process. */
ms_message("Defer ringing to gather ICE candidates");
@@ -272,56 +362,236 @@ static void call_received(SalOp *h){
linphone_core_notify_incoming_call(lc,call);
}
+static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md){
+ SalMediaDescription *cur_md=call->resultdesc;
+ int i;
+ 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){
+ if (!sal_stream_description_active(&cur_md->streams[i])) continue;
+ ref_stream=&cur_md->streams[i];
+ new_stream=&md->streams[i];
+ if (ref_stream->type==new_stream->type && ref_stream->payloads && new_stream->payloads){
+ PayloadType *refpt, *newpt;
+ refpt=(PayloadType*)ref_stream->payloads->data;
+ newpt=(PayloadType*)new_stream->payloads->data;
+ if (strcmp(refpt->mime_type,newpt->mime_type)==0 && refpt->clock_rate==newpt->clock_rate
+ && payload_type_get_number(refpt)==payload_type_get_number(newpt)){
+ MediaStream *ms=NULL;
+ if (ref_stream->type==SalAudio){
+ ms=(MediaStream*)call->audiostream;
+ }else if (ref_stream->type==SalVideo){
+ ms=(MediaStream*)call->videostream;
+ }
+ if (ms){
+ RtpSession *session=ms->sessions.rtp_session;
+ const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr;
+ const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr;
+ if (ms_is_multicast(rtp_addr))
+ ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]",
+ rtp_addr,new_stream->rtp_port,call);
+ else
+ rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port);
+ }
+ }
+ }
+ }
+}
+
+static void start_remote_ring(LinphoneCore *lc, LinphoneCall *call) {
+ if (lc->sound_conf.play_sndcard!=NULL){
+ MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
+ if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate);
+ /*we release sound before playing ringback tone*/
+ 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);
+ }
+ }
+}
+
static void call_ringing(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h);
SalMediaDescription *md;
-
+
if (call==NULL) return;
-
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Remote ringing."));
-
+
+ /*set privacy*/
+ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
+
+ linphone_core_notify_display_status(lc,_("Remote ringing."));
+
md=sal_call_get_final_media_description(h);
if (md==NULL){
- if (lc->ringstream && lc->dmfs_playing_start_time!=0){
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- lc->dmfs_playing_start_time=0;
- }
- if (lc->ringstream!=NULL) return; /*already ringing !*/
- if (lc->sound_conf.play_sndcard!=NULL){
- MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
- if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate);
- /*we release sound before playing ringback tone*/
- if (call->audiostream)
- audio_stream_unprepare_sound(call->audiostream);
- lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
+ linphone_core_stop_dtmf_stream(lc);
+ if (call->state==LinphoneCallOutgoingEarlyMedia){
+ /*already doing early media */
+ return;
}
+ if (lc->ringstream!=NULL) return;/*already ringing !*/
+ start_remote_ring(lc, call);
ms_message("Remote ringing...");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Remote ringing..."));
+ linphone_core_notify_display_status(lc,_("Remote ringing..."));
linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing");
}else{
/*accept early media */
- if (call->audiostream && audio_stream_started(call->audiostream)){
+ if ((call->audiostream && audio_stream_started(call->audiostream))
+#ifdef VIDEO_ENABLED
+ || (call->videostream && video_stream_started(call->videostream))
+#endif
+ ) {
/*streams already started */
- ms_message("Early media already started.");
- return;
+ try_early_media_forking(call,md);
+ #ifdef VIDEO_ENABLED
+ if (call->videostream){
+ /*just request for iframe*/
+ video_stream_send_vfu(call->videostream);
+ }
+ #endif
+ return;
}
- if (lc->vtable.show) lc->vtable.show(lc);
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Early media."));
+
+ linphone_core_notify_show_interface(lc);
+ linphone_core_notify_display_status(lc,_("Early media."));
linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media");
- if (lc->ringstream!=NULL){
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- }
+ linphone_core_stop_ringing(lc);
ms_message("Doing early media...");
- linphone_core_update_streams(lc,call,md);
+ linphone_core_update_streams(lc,call,md, call->state);
+ if ((linphone_call_params_get_audio_direction(linphone_call_get_current_params(call)) == LinphoneMediaDirectionInactive) && call->audiostream) {
+ if (lc->ringstream != NULL) return; /* Already ringing! */
+ start_remote_ring(lc, call);
+ }
}
}
+static void start_pending_refer(LinphoneCall *call){
+ linphone_core_start_refered_call(call->core, call,NULL);
+}
+
+static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *op){
+ SalMediaDescription *md, *rmd;
+ 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:
+ case LinphoneCallOutgoingEarlyMedia:
+ /*immediately notify the connected state*/
+ linphone_call_set_state(call,LinphoneCallConnected,"Connected");
+ {
+ char *tmp=linphone_call_get_remote_address_as_string (call);
+ char *msg=ms_strdup_printf(_("Call answered by %s"),tmp);
+ linphone_core_notify_display_status(lc,msg);
+ ms_free(tmp);
+ ms_free(msg);
+ }
+ break;
+ default:
+ break;
+ }
+
+ linphone_task_list_init(&tl);
+ rmd=sal_call_get_remote_media_description(op);
+ /*set privacy*/
+ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
+ /*reset the internal call update flag, so it doesn't risk to be copied and used in further re-INVITEs*/
+ 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);
+ }
+#endif //BUILD_UPNP
+
+ md=sal_call_get_final_media_description(op);
+ if (md == NULL && call->prevstate == LinphoneCallOutgoingEarlyMedia && call->resultdesc != NULL){
+ ms_message("Using early media SDP since none was received with the 200 OK");
+ md = call->resultdesc;
+ }
+ if (md && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){
+ 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*/
+ switch (call->state){
+ case LinphoneCallResuming:
+ linphone_core_notify_display_status(lc,_("Call resumed."));
+ /*intentionally no break*/
+ case LinphoneCallConnected:
+ if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call);
+ /*intentionally no break*/
+ case LinphoneCallUpdating:
+ case LinphoneCallUpdatedByRemote:
+ if (!sal_media_description_has_dir(call->localdesc, SalStreamInactive) &&
+ (sal_media_description_has_dir(md,SalStreamRecvOnly) ||
+ sal_media_description_has_dir(md,SalStreamInactive))){
+ next_state = LinphoneCallPausedByRemote;
+ next_state_str = "Call paused by remote";
+ }else{
+ if (!call->current_params->in_conference)
+ lc->current_call=call;
+ next_state = LinphoneCallStreamsRunning;
+ next_state_str = "Streams running";
+ }
+ break;
+ case LinphoneCallEarlyUpdating:
+ next_state_str = "Early update accepted";
+ next_state = call->prevstate;
+ break;
+ case LinphoneCallPausing:
+ /*when we entered the pausing state, we always reach the paused state whatever the content of the remote SDP is.
+ Our streams are all send-only (with music), soundcard and camera are never used*/
+ next_state = LinphoneCallPaused;
+ next_state_str = "Call paused";
+ if (call->refer_pending)
+ linphone_task_list_add(&tl, (LinphoneCoreIterateHook)start_pending_refer, call);
+ break;
+ default:
+ 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_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));
+ }
+ }else{ /*invalid or no SDP*/
+ switch (call->prevstate){
+ /*send a bye only in case of early states*/
+ case LinphoneCallOutgoingInit:
+ case LinphoneCallOutgoingProgress:
+ case LinphoneCallOutgoingRinging:
+ case LinphoneCallOutgoingEarlyMedia:
+ case LinphoneCallIncomingReceived:
+ case LinphoneCallIncomingEarlyMedia:
+ ms_error("Incompatible SDP answer received, need to abort the call");
+ linphone_core_abort_call(lc,call,_("Incompatible, check codecs or security settings..."));
+ break;
+ /*otherwise we are able to resume previous state*/
+ default:
+ ms_message("Incompatible SDP answer received, restoring previous state [%s]",linphone_call_state_to_string(call->prevstate));
+ linphone_call_set_state(call,call->prevstate,_("Incompatible media parameters."));
+ break;
+ }
+ }
+ linphone_task_list_run(&tl);
+ linphone_task_list_free(&tl);
+}
+
/*
* could be reach :
* - when the call is accepted
@@ -330,182 +600,159 @@ static void call_ringing(SalOp *h){
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);
- SalMediaDescription *md;
- if (call==NULL){
- ms_warning("No call to accept.");
+ if (call == NULL){
+ ms_warning("call_accepted: call does no longer exist.");
return ;
}
-
- /* Handle remote ICE attributes if any. */
- if (call->ice_session != NULL) {
- linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
- }
-#ifdef BUILD_UPNP
- if (call->upnp_session != NULL) {
- linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op));
- }
-#endif //BUILD_UPNP
-
- md=sal_call_get_final_media_description(op);
- call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
-
- if (call->state==LinphoneCallOutgoingProgress ||
- call->state==LinphoneCallOutgoingRinging ||
- call->state==LinphoneCallOutgoingEarlyMedia){
- linphone_call_set_state(call,LinphoneCallConnected,"Connected");
- if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call);
- }
- if (md && !sal_media_description_empty(md) && !linphone_core_incompatible_security(lc,md)){
- linphone_call_update_remote_session_id_and_ver(call);
- if (sal_media_description_has_dir(md,SalStreamSendOnly) ||
- sal_media_description_has_dir(md,SalStreamInactive)){
- if (lc->vtable.display_status){
- char *tmp=linphone_call_get_remote_address_as_string (call);
- char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp);
- lc->vtable.display_status(lc,msg);
- ms_free(tmp);
- ms_free(msg);
- }
- linphone_core_update_streams (lc,call,md);
- linphone_call_set_state(call,LinphoneCallPaused,"Call paused");
- if (call->refer_pending)
- linphone_core_start_refered_call(lc,call);
- }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){
- /*we are put on hold when the call is initially accepted */
- if (lc->vtable.display_status){
- char *tmp=linphone_call_get_remote_address_as_string (call);
- char *msg=ms_strdup_printf(_("Call answered by %s - on hold."),tmp);
- lc->vtable.display_status(lc,msg);
- ms_free(tmp);
- ms_free(msg);
- }
- linphone_core_update_streams (lc,call,md);
- linphone_call_set_state(call,LinphoneCallPausedByRemote,"Call paused by remote");
- }else{
- if (call->state!=LinphoneCallUpdating){
- if (call->state==LinphoneCallResuming){
- if (lc->vtable.display_status){
- lc->vtable.display_status(lc,_("Call resumed."));
- }
- }else{
- if (lc->vtable.display_status){
- char *tmp=linphone_call_get_remote_address_as_string (call);
- char *msg=ms_strdup_printf(_("Call answered by %s."),tmp);
- lc->vtable.display_status(lc,msg);
- ms_free(tmp);
- ms_free(msg);
- }
- }
- }
- linphone_core_update_streams (lc,call,md);
- if (!call->current_params.in_conference)
- lc->current_call=call;
- linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
- }
- }else{
- /*send a bye*/
- ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
- linphone_core_abort_call(lc,call,_("Incompatible, check codecs or security settings..."));
- }
-}
-
-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("No call to be ACK'd");
- return ;
- }
- if (call->media_pending){
- SalMediaDescription *md=sal_call_get_final_media_description(op);
- if (md && !sal_media_description_empty(md)){
- linphone_core_update_streams (lc,call,md);
- linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
- }else{
- /*send a bye*/
- ms_error("Incompatible SDP response received in ACK, need to abort the call");
- linphone_core_abort_call(lc,call,"No codec intersection");
- return;
- }
- }
-}
-
-static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){
- SalMediaDescription *md;
- SalMediaDescription *rmd=sal_call_get_remote_media_description(call->op);
- if ((rmd!=NULL) && (call->ice_session!=NULL)) {
- linphone_core_update_ice_from_remote_media_description(call,rmd);
- linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session);
- }
-#ifdef BUILD_UPNP
- if(call->upnp_session != NULL) {
- linphone_core_update_upnp_from_remote_media_description(call, rmd);
- linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session);
- }
-#endif //BUILD_UPNP
- linphone_call_update_remote_session_id_and_ver(call);
- sal_call_accept(call->op);
- md=sal_call_get_final_media_description(call->op);
- if (md && !sal_media_description_empty(md))
- linphone_core_update_streams(lc,call,md);
+ process_call_accepted(lc, call, op);
}
static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
- call_accept_update(lc,call);
- if(lc->vtable.display_status)
- lc->vtable.display_status(lc,_("We have been resumed."));
- linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
- linphone_call_set_transfer_state(call, LinphoneCallIdle);
+ linphone_core_notify_display_status(lc,_("We have been resumed."));
+ _linphone_core_accept_call_update(lc,call,NULL,LinphoneCallStreamsRunning,"Connected (streams running)");
}
static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
- call_accept_update(lc,call);
+ LinphoneCallParams *params;
+
/* we are being paused */
- if(lc->vtable.display_status)
- lc->vtable.display_status(lc,_("We are paused by other party."));
- linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
-}
-
-static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){
- if(lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Call is updated by remote."));
- call->defer_update=FALSE;
- linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
- if (call->defer_update==FALSE){
- linphone_core_accept_call_update(lc,call,NULL);
+ linphone_core_notify_display_status(lc,_("We are paused by other party."));
+ params = linphone_call_params_copy(call->params);
+ if (lp_config_get_int(lc->config, "sip", "inactive_video_on_pause", 0)) {
+ linphone_call_params_set_video_direction(params, LinphoneMediaDirectionInactive);
}
+ _linphone_core_accept_call_update(lc,call,params,LinphoneCallPausedByRemote,"Call paused by remote");
+ linphone_call_params_unref(params);
}
-/* this callback is called when an incoming re-INVITE modifies the session*/
-static void call_updating(SalOp *op){
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+/* 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);
-
- if (rmd==NULL){
- /* case of a reINVITE without SDP */
- call_accept_update(lc,call);
- call->media_pending=TRUE;
- return;
- }
-
+
+ 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 call_paused_by_remote(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);
+ }
+ }
break;
+ /*SIP UPDATE CASE*/
+ case LinphoneCallOutgoingRinging:
+ case LinphoneCallOutgoingEarlyMedia:
+ case LinphoneCallIncomingEarlyMedia:
+ if (is_update) {
+ linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote");
+ _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate));
+ }
+ break;
case LinphoneCallStreamsRunning:
case LinphoneCallConnected:
if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){
call_paused_by_remote(lc,call);
}else{
- call_updated_by_remote(lc,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){
+ linphone_core_accept_call_update(lc,call,NULL);
+ }
}
break;
- default:
- call_accept_update(lc,call);
+ case LinphoneCallPaused:
+ /*we'll remain in pause state but accept the offer anyway according to default parameters*/
+ _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state));
+ break;
+ case LinphoneCallUpdating:
+ case LinphoneCallPausing:
+ case LinphoneCallResuming:
+ case LinphoneCallUpdatedByRemote:
+ sal_call_decline(call->op,SalReasonInternalError,NULL);
+ /*no break*/
+ case LinphoneCallIdle:
+ case LinphoneCallOutgoingInit:
+ case LinphoneCallEnd:
+ case LinphoneCallIncomingReceived:
+ case LinphoneCallOutgoingProgress:
+ case LinphoneCallRefered:
+ case LinphoneCallError:
+ case LinphoneCallReleased:
+ case LinphoneCallEarlyUpdatedByRemote:
+ case LinphoneCallEarlyUpdating:
+ ms_warning("Receiving reINVITE or UPDATE while in state [%s], should not happen.",linphone_call_state_to_string(call->state));
+ break;
+ }
+}
+
+/* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/
+static void call_updating(SalOp *op, bool_t is_update){
+ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+ SalMediaDescription *rmd=sal_call_get_remote_media_description(op);
+
+ if (!call) {
+ ms_error("call_updating(): call doesn't exist anymore");
+ return ;
+ }
+ if (call->state!=LinphoneCallPaused){
+ /*Refresh the local description, but in paused state, we don't change anything.*/
+ linphone_call_make_local_media_description(call);
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
+ if (rmd == NULL){
+ /* case of a reINVITE or UPDATE without SDP */
+ call->expect_media_in_ack = TRUE;
+ sal_call_accept(op); /*respond with an offer*/
+ /*don't do anything else in this case, wait for the ACK to receive to notify the app*/
+ }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);
+ return;
+ }
+ if (is_update && prev_result_desc && md){
+ int diff=sal_media_description_equals(prev_result_desc,md);
+ if (diff & (SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED|SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED)){
+ ms_warning("Cannot accept this update, it is changing parameters that require user approval");
+ sal_call_decline(call->op,SalReasonNotAcceptable,NULL); /*FIXME should send 504 Cannot change the session parameters without prompting the user"*/
+ return;
+ }
+ }
+ call_updated(lc, call, op, 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;
+ }
+ if (call->expect_media_in_ack){
+ switch(call->state){
+ case LinphoneCallStreamsRunning:
+ case LinphoneCallPausedByRemote:
+ linphone_call_set_state(call, LinphoneCallUpdatedByRemote, "UpdatedByRemote");
+ break;
+ default:
+ break;
+ }
+ process_call_accepted(lc, call, op);
}
}
@@ -514,31 +761,31 @@ static void call_terminated(SalOp *op, const char *from){
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
if (call==NULL) return;
-
+
switch(linphone_call_get_state(call)){
case LinphoneCallEnd:
case LinphoneCallError:
- ms_warning("call_terminated: ignoring.");
+ ms_warning("call_terminated: already terminated, ignoring.");
return;
break;
case LinphoneCallIncomingReceived:
case LinphoneCallIncomingEarlyMedia:
- call->reason=LinphoneReasonNotAnswered;
+ sal_error_info_set(&call->non_op_error,SalReasonRequestTimeout,0,"Incoming call cancelled",NULL);
break;
default:
break;
}
ms_message("Current call terminated...");
+ if (call->refer_pending){
+ 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) )) {
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
+ linphone_core_stop_ringing(lc);
}
linphone_call_stop_media_streams(call);
- if (lc->vtable.show!=NULL)
- lc->vtable.show(lc);
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Call terminated."));
+ linphone_core_notify_show_interface(lc);
+ linphone_core_notify_display_status(lc,_("Call terminated."));
#ifdef BUILD_UPNP
linphone_call_delete_upnp_session(call);
@@ -547,115 +794,161 @@ static void call_terminated(SalOp *op, const char *from){
linphone_call_set_state(call, LinphoneCallEnd,"Call ended");
}
-static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){
+static int resume_call_after_failed_transfer(LinphoneCall *call){
+ if (call->was_automatically_paused && call->state==LinphoneCallPausing)
+ return BELLE_SIP_CONTINUE; /*was still in pausing state*/
+
+ if (call->was_automatically_paused && call->state==LinphoneCallPaused){
+ if (sal_op_is_idle(call->op)){
+ linphone_core_resume_call(call->core,call);
+ }else {
+ ms_message("resume_call_after_failed_transfer(), salop was busy");
+ return BELLE_SIP_CONTINUE;
+ }
+ }
+ linphone_call_unref(call);
+ return BELLE_SIP_STOP;
+}
+
+static void call_failure(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ const SalErrorInfo *ei=sal_op_get_error_info(op);
char *msg486=_("User is busy.");
char *msg480=_("User is temporarily unavailable.");
/*char *retrymsg=_("%s. Retry after %i minute(s).");*/
char *msg600=_("User does not want to be disturbed.");
char *msg603=_("Call declined.");
- const char *msg=details;
+ const char *msg=ei->full_string;
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+ LinphoneCall *referer=call->referer;
if (call==NULL){
- ms_warning("Call faillure reported on already cleaned call ?");
+ ms_warning("Call faillure reported on already terminated call.");
return ;
}
-
- if (lc->vtable.show) lc->vtable.show(lc);
- if (error==SalErrorNoResponse){
- msg=_("No response.");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg);
- }else if (error==SalErrorProtocol){
- msg=details ? details : _("Protocol error.");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc, msg);
- }else if (error==SalErrorFailure){
- switch(sr){
- case SalReasonDeclined:
- msg=msg603;
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg603);
- break;
- case SalReasonBusy:
- msg=msg486;
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg486);
- break;
- case SalReasonRedirect:
- msg=_("Redirected");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg);
- break;
- case SalReasonTemporarilyUnavailable:
- msg=msg480;
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg480);
- break;
- case SalReasonNotFound:
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg);
- break;
- case SalReasonDoNotDisturb:
- msg=msg600;
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg600);
- break;
- case SalReasonMedia:
- //media_encryption_mandatory
- if (call->params.media_encryption == LinphoneMediaEncryptionSRTP &&
- !linphone_core_is_media_encryption_mandatory(lc)) {
- int i;
- ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP");
- linphone_call_stop_media_streams(call);
- if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
- /* clear SRTP local params */
- call->params.media_encryption = LinphoneMediaEncryptionNone;
- for(i=0; ilocaldesc->n_active_streams; i++) {
- call->localdesc->streams[i].proto = SalProtoRtpAvp;
- memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
- }
- linphone_core_start_invite(lc, call);
+ linphone_core_notify_show_interface(lc);
+ switch(ei->reason){
+ case SalReasonNone:
+ break;
+ case SalReasonRequestTimeout:
+ msg=_("Request timeout.");
+ linphone_core_notify_display_status(lc,msg);
+ break;
+ case SalReasonDeclined:
+ msg=msg603;
+ linphone_core_notify_display_status(lc,msg603);
+ break;
+ case SalReasonBusy:
+ msg=msg486;
+ linphone_core_notify_display_status(lc,msg486);
+ break;
+ case SalReasonRedirect:
+ {
+ linphone_call_stop_media_streams(call);
+ if ( call->state==LinphoneCallOutgoingInit
+ || call->state==LinphoneCallOutgoingProgress
+ || call->state==LinphoneCallOutgoingRinging /*push case*/
+ || call->state==LinphoneCallOutgoingEarlyMedia){
+ LinphoneAddress* redirection_to = (LinphoneAddress*)sal_op_get_remote_contact_address(call->op);
+ if( redirection_to ){
+ char* url = linphone_address_as_string(redirection_to);
+ ms_warning("Redirecting call [%p] to %s",call, url);
+ ms_free(url);
+ if( call->log->to != NULL ) {
+ linphone_address_unref(call->log->to);
}
+ call->log->to = linphone_address_ref(redirection_to);
+ linphone_core_restart_invite(lc, call);
return;
}
- msg=_("Incompatible media parameters.");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,msg);
- break;
- default:
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Call failed."));
+ }
+ msg=_("Redirected");
+ linphone_core_notify_display_status(lc,msg);
}
+ break;
+ case SalReasonTemporarilyUnavailable:
+ msg=msg480;
+ linphone_core_notify_display_status(lc,msg480);
+ break;
+ case SalReasonNotFound:
+ linphone_core_notify_display_status(lc,msg);
+ break;
+ case SalReasonDoNotDisturb:
+ msg=msg600;
+ linphone_core_notify_display_status(lc,msg600);
+ break;
+ case SalReasonUnsupportedContent: /*state == LinphoneCallOutgoingInit)
+ || (call->state == LinphoneCallOutgoingProgress)
+ || (call->state == LinphoneCallOutgoingRinging) /* Push notification case */
+ || (call->state == LinphoneCallOutgoingEarlyMedia)) {
+ int i;
+ for (i = 0; i < call->localdesc->nb_streams; i++) {
+ if (!sal_stream_description_active(&call->localdesc->streams[i])) continue;
+ if (call->params->media_encryption == LinphoneMediaEncryptionSRTP) {
+ if (call->params->avpf_enabled == TRUE) {
+ if (i == 0) ms_message("Retrying call [%p] with SAVP", call);
+ call->params->avpf_enabled = FALSE;
+ linphone_core_restart_invite(lc, call);
+ return;
+ } else if (!linphone_core_is_media_encryption_mandatory(lc)) {
+ if (i == 0) ms_message("Retrying call [%p] with AVP", call);
+ call->params->media_encryption = LinphoneMediaEncryptionNone;
+ memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
+ linphone_core_restart_invite(lc, call);
+ return;
+ }
+ } else if (call->params->avpf_enabled == TRUE) {
+ if (i == 0) ms_message("Retrying call [%p] with AVP", call);
+ call->params->avpf_enabled = FALSE;
+ linphone_core_restart_invite(lc, call);
+ return;
+ }
+ }
+ }
+ msg=_("Incompatible media parameters.");
+ linphone_core_notify_display_status(lc,msg);
+ break;
+ default:
+ linphone_core_notify_display_status(lc,_("Call failed."));
}
- if (lc->ringstream!=NULL) {
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- }
- linphone_call_stop_media_streams (call);
- if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){
- /*resume to the call that send us the refer automatically*/
- linphone_core_resume_call(lc,call->referer);
+ /*some call errors are not fatal*/
+ switch (call->state) {
+ 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;
+ default:
+ break; /*nothing to do*/
}
+ linphone_core_stop_ringing(lc);
+ linphone_call_stop_media_streams(call);
+
#ifdef BUILD_UPNP
linphone_call_delete_upnp_session(call);
#endif //BUILD_UPNP
- if (sr == SalReasonDeclined) {
- call->reason=LinphoneReasonDeclined;
- linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
- } else if (sr == SalReasonNotFound) {
- call->reason=LinphoneReasonNotFound;
- linphone_call_set_state(call,LinphoneCallError,"User not found.");
- } else if (sr == SalReasonBusy) {
- call->reason=LinphoneReasonBusy;
- linphone_call_set_state(call,LinphoneCallError,"User is busy.");
- } else {
- linphone_call_set_state(call,LinphoneCallError,msg);
+ if (call->state!=LinphoneCallEnd && call->state!=LinphoneCallError){
+ if (ei->reason==SalReasonDeclined){
+ linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
+ }else{
+ linphone_call_set_state(call,LinphoneCallError,ei->full_string);
+ }
+ if (ei->reason!=SalReasonNone) linphone_core_play_call_error_tone(lc,linphone_reason_from_sal(ei->reason));
+ }
+
+ if (referer){
+ /*notify referer of the failure*/
+ linphone_core_notify_refer_state(lc,referer,call);
+ /*schedule automatic resume of the call. This must be done only after the notifications are completed due to dialog serialization of requests.*/
+ linphone_core_queue_task(lc,(belle_sip_source_func_t)resume_call_after_failed_transfer,linphone_call_ref(referer),"Automatic call resuming after failed transfer");
}
}
@@ -663,113 +956,79 @@ static void call_released(SalOp *op){
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
if (call!=NULL){
linphone_call_set_state(call,LinphoneCallReleased,"Call released");
- }else ms_error("call_released() for already destroyed call ?");
+ }else{
+ /*we can arrive here when the core manages call at Sal level without creating a LinphoneCall object. Typicially:
+ * - when declining an incoming call with busy because maximum number of calls is reached.
+ */
+ }
}
-static void auth_requested(SalOp *h, const char *realm, const char *username){
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
- LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
- LinphoneCall *call=is_a_linphone_call(sal_op_get_user_pointer(h));
+static void auth_failure(SalOp *op, SalAuthInfo* info) {
+ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ LinphoneAuthInfo *ai=NULL;
- if (call && call->ping_op==h){
- /*don't request authentication for ping requests. Their purpose is just to get any
- * answer to get the Via's received and rport parameters.
- */
- ms_message("auth_requested(): ignored for ping request.");
- return;
- }
-
- ms_message("auth_requested() for realm=%s, username=%s",realm,username);
+ if( info != NULL ){
+ ai = (LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
- if (ai && ai->works==FALSE && ai->usecount>=3){
- /*case we tried 3 times to authenticate, without success */
- /*Better is to stop (implemeted below in else statement), and retry later*/
- if (ms_time(NULL)-ai->last_use_time>30){
- ai->usecount=0; /*so that we can allow to retry */
+ if (ai){
+ ms_message("%s/%s/%s authentication fails.",info->realm,info->username,info->domain);
+ /*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);
}
}
-
- if (ai && (ai->works || ai->usecount<3)){
- SalAuthInfo sai;
- sai.username=ai->username;
- sai.userid=ai->userid;
- sai.realm=ai->realm;
- sai.password=ai->passwd;
- ms_message("auth_requested(): authenticating realm=%s, username=%s",realm,username);
- sal_op_authenticate(h,&sai);
- ai->usecount++;
- ai->last_use_time=ms_time(NULL);
- }else{
- if (ai && ai->works==FALSE) {
- sal_op_cancel_authentication(h);
- }
- if (lc->vtable.auth_info_requested)
- lc->vtable.auth_info_requested(lc,realm,username);
- }
-}
-static void auth_success(SalOp *h, const char *realm, const char *username){
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
- LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
- if (ai){
- ms_message("%s/%s authentication works.",realm,username);
- ai->works=TRUE;
- }
}
static void register_success(SalOp *op, bool_t registered){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
char *msg;
-
- if (cfg->deletion_date!=0){
- ms_message("Registration success for removed proxy config, ignored");
+
+ if (!cfg){
+ ms_message("Registration success for deleted proxy config, ignored");
return;
}
- linphone_proxy_config_set_error(cfg,LinphoneReasonNone);
linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
- registered ? "Registration sucessful" : "Unregistration done");
- if (lc->vtable.display_status){
+ registered ? "Registration successful" : "Unregistration done");
+ {
if (registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op));
else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op));
- lc->vtable.display_status(lc,msg);
+ linphone_core_notify_display_status(lc,msg);
ms_free(msg);
}
-
+
}
-static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){
+static void register_failure(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
+ const SalErrorInfo *ei=sal_op_get_error_info(op);
+ const char *details=ei->full_string;
if (cfg==NULL){
ms_warning("Registration failed for unknown proxy config.");
return ;
}
- if (cfg->deletion_date!=0){
- ms_message("Registration failed for removed proxy config, ignored");
- return;
- }
if (details==NULL)
details=_("no response timeout");
-
- if (lc->vtable.display_status) {
- char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),details );
- lc->vtable.display_status(lc,msg);
+
+ {
+ char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op), details);
+ linphone_core_notify_display_status(lc,msg);
ms_free(msg);
}
- if (error== SalErrorFailure && reason == SalReasonForbidden) {
- linphone_proxy_config_set_error(cfg, LinphoneReasonBadCredentials);
- } else if (error == SalErrorNoResponse) {
- linphone_proxy_config_set_error(cfg, LinphoneReasonNoResponse);
+
+ if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError)
+ && linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) {
+ linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,_("Service unavailable, retrying"));
+ } else {
+ linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
}
- linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
- if (error== SalErrorFailure && reason == SalReasonForbidden) {
- const char *realm=NULL,*username=NULL;
- if (sal_op_get_auth_requested(op,&realm,&username)==0){
- if (lc->vtable.auth_info_requested)
- lc->vtable.auth_info_requested(lc,realm,username);
- }
+ if (cfg->publish_op){
+ /*prevent publish to be sent now until registration gets successful*/
+ sal_op_release(cfg->publish_op);
+ cfg->publish_op=NULL;
+ cfg->send_publish=cfg->publish;
}
}
@@ -788,8 +1047,7 @@ 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 (lc->vtable.dtmf_received != NULL)
- lc->vtable.dtmf_received(lc, call, dtmf);
+ linphone_core_notify_dtmf_received(lc, call, dtmf);
}
static void refer_received(Sal *sal, SalOp *op, const char *referto){
@@ -802,21 +1060,14 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){
call->refer_to=ms_strdup(referto);
call->refer_pending=TRUE;
linphone_call_set_state(call,LinphoneCallRefered,"Refered");
- if (lc->vtable.display_status){
+ {
char *msg=ms_strdup_printf(_("We are transferred to %s"),referto);
- lc->vtable.display_status(lc,msg);
+ linphone_core_notify_display_status(lc,msg);
ms_free(msg);
}
- if (call->state!=LinphoneCallPaused){
- ms_message("Automatically pausing current call to accept transfer.");
- linphone_core_pause_call(lc,call);
- call->was_automatically_paused=TRUE;
- /*then we will start the refered when the pause is accepted, in order to serialize transactions within the dialog.
- * Indeed we need to avoid to send a NOTIFY to inform about of state of the refered call while the pause isn't completed.
- **/
- }else linphone_core_start_refered_call(lc,call);
- }else if (lc->vtable.refer_received){
- lc->vtable.refer_received(lc,referto);
+ if (call->refer_pending) linphone_core_start_refered_call(lc,call,NULL);
+ }else {
+ linphone_core_notify_refer_received(lc,referto);
}
}
@@ -844,30 +1095,39 @@ static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
static void text_received(SalOp *op, const SalMessage *msg){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- if (is_duplicate_msg(lc,msg->message_id)==FALSE){
+ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+ if (lc->chat_deny_code==LinphoneReasonNone && is_duplicate_msg(lc,msg->message_id)==FALSE){
linphone_core_message_received(lc,op,msg);
}
+ sal_message_reply(op,linphone_reason_to_sal(lc->chat_deny_code));
+ if (!call) sal_op_release(op);
}
-static void notify(SalOp *op, const char *from, const char *msg){
+static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) {
+ LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ linphone_core_is_composing_received(lc, op, is_composing);
+ sal_op_release(op);
+}
+
+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);
+}
+
+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);
+}
+
+static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op);
- ms_message("get a %s notify from %s",msg,from);
- if(lc->vtable.notify_recv)
- lc->vtable.notify_recv(lc,call,from,msg);
+ linphone_notify_recv(lc,op,ss,model);
}
-static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- linphone_notify_recv(lc,op,ss,status);
-}
-
-static void subscribe_received(SalOp *op, const char *from){
+static void subscribe_presence_received(SalOp *op, const char *from){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
linphone_subscription_new(lc,op,from);
}
-static void subscribe_closed(SalOp *op, const char *from){
+static void subscribe_presence_closed(SalOp *op, const char *from){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
linphone_subscription_closed(lc,op);
}
@@ -887,6 +1147,62 @@ 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);;
+
+#ifndef _WIN32
+ {
+ // optinal check for files
+ struct stat st;
+ if (stat(key_file,&st)) {
+ ms_warning("No client certificate key found in %s", key_file);
+ return FALSE;
+ }
+ if (stat(chain_file,&st)) {
+ ms_warning("No client certificate chain found in %s", chain_file);
+ return FALSE;
+ }
+ }
+#endif
+
+ sal_certificates_chain_parse_file(sai, chain_file, SAL_CERTIFICATE_RAW_FORMAT_PEM );
+ sal_signing_key_parse_file(sai, key_file, "");
+ 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);
+ 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;
+ return TRUE;
+ } else {
+ 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);
+ } else {
+ return FALSE;
+ }
+}
+
static void notify_refer(SalOp *op, SalReferStatus status){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
@@ -927,23 +1243,118 @@ static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus sta
return LinphoneChatMessageStateIdle;
}
-static int op_equals(LinphoneCall *a, SalOp *b) {
- return a->op !=b; /*return 0 if equals*/
-}
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
- const MSList* calls = linphone_core_get_calls(chat_msg->chat_room->lc);
-
- if (chat_msg && chat_msg->cb) {
- chat_msg->cb(chat_msg
- ,chatStatusSal2Linphone(status)
- ,chat_msg->cb_ud);
+
+ if (chat_msg == NULL) {
+ // Do not handle delivery status for isComposing messages.
+ return;
}
- linphone_chat_message_destroy(chat_msg);
-
- if (!ms_list_find_custom((MSList*)calls, (MSCompareFunc) op_equals, op)) {
- /*op was only create for messaging purpose, destroying*/
- sal_op_release(op);
+
+ 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);
+ }
+ }
+ 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){
+ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ linphone_core_notify_info_message(lc,op,body);
+}
+
+static void subscribe_response(SalOp *op, SalSubscribeStatus status){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+ const SalErrorInfo *ei=sal_op_get_error_info(op);
+
+ if (lev==NULL) return;
+
+ if (status==SalSubscribeActive){
+ linphone_event_set_state(lev,LinphoneSubscriptionActive);
+ }else if (status==SalSubscribePending){
+ linphone_event_set_state(lev,LinphoneSubscriptionPending);
+ }else{
+ if (lev->subscription_state==LinphoneSubscriptionActive && ei->reason==SalReasonIOError){
+ 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){
+ 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);
+ }
+ {
+ LinphoneContent *ct=linphone_content_from_sal_body(body);
+ if (ct) linphone_core_notify_notify_received(lc,lev,eventname,ct);
+ }
+ 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){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+
+ if (lev==NULL) {
+ lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming,eventname);
+ linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived);
+ }else{
+ /*subscribe refresh, unhandled*/
+ }
+
+}
+
+static void subscribe_closed(SalOp *op){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+
+ linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
+}
+
+static void on_publish_response(SalOp* op){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+ const SalErrorInfo *ei=sal_op_get_error_info(op);
+
+ if (lev==NULL) return;
+ if (ei->reason==SalReasonNone){
+ if (!lev->terminating)
+ linphone_event_set_publish_state(lev,LinphonePublishOk);
+ else
+ linphone_event_set_publish_state(lev,LinphonePublishCleared);
+ }else{
+ if (lev->publish_state==LinphonePublishOk){
+ linphone_event_set_publish_state(lev,LinphonePublishProgress);
+ }else{
+ linphone_event_set_publish_state(lev,LinphonePublishError);
+ }
+ }
+}
+
+static void on_expire(SalOp *op){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+
+ if (lev==NULL) return;
+
+ if (linphone_event_get_publish_state(lev)==LinphonePublishOk){
+ linphone_event_set_publish_state(lev,LinphonePublishExpiring);
+ }else if (linphone_event_get_subscription_state(lev)==LinphoneSubscriptionActive){
+ linphone_event_set_state(lev,LinphoneSubscriptionExpiring);
}
}
@@ -956,8 +1367,7 @@ SalCallbacks linphone_sal_callbacks={
call_terminated,
call_failure,
call_released,
- auth_requested,
- auth_success,
+ auth_failure,
register_success,
register_failure,
vfu_request,
@@ -965,12 +1375,22 @@ SalCallbacks linphone_sal_callbacks={
refer_received,
text_received,
text_delivery_update,
- notify,
- notify_presence,
+ is_composing_received,
notify_refer,
subscribe_received,
subscribe_closed,
+ subscribe_response,
+ notify,
+ subscribe_presence_received,
+ subscribe_presence_closed,
+ parse_presence_requested,
+ convert_presence_to_xml_requested,
+ notify_presence,
ping_reply,
+ auth_requested,
+ info_received,
+ on_publish_response,
+ on_expire
};
diff --git a/coreapi/chat.c b/coreapi/chat.c
index b9894ea8f..990f06998 100644
--- a/coreapi/chat.c
+++ b/coreapi/chat.c
@@ -21,54 +21,550 @@
* 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 "lpconfig.h"
+#include "belle-sip/belle-sip.h"
+#include "lime.h"
+#include "ortp/b64.h"
-/**
- * @addtogroup chatroom
- * @{
- */
+#include
+#include
+#include
-/**
- * Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org
- * @param lc #LinphoneCore object
- * @param to destination address for messages
- * @return #LinphoneChatRoom where messaging can take place.
- */
-LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char *to){
- LinphoneAddress *parsed_url=NULL;
+#define COMPOSING_DEFAULT_IDLE_TIMEOUT 15
+#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
+#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
- if ((parsed_url=linphone_core_interpret_url(lc,to))!=NULL){
- LinphoneChatRoom *cr=ms_new0(LinphoneChatRoom,1);
- cr->lc=lc;
- cr->peer=linphone_address_as_string(parsed_url);
- cr->peer_url=parsed_url;
- lc->chatrooms=ms_list_append(lc->chatrooms,(void *)cr);
- return cr;
+#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);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessageCbs, belle_sip_object_t,
+ NULL, // destroy
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
+
+LinphoneChatMessageCbs * linphone_chat_message_cbs_ref(LinphoneChatMessageCbs *cbs) {
+ belle_sip_object_ref(cbs);
+ return cbs;
+}
+
+void linphone_chat_message_cbs_unref(LinphoneChatMessageCbs *cbs) {
+ belle_sip_object_unref(cbs);
+}
+
+void *linphone_chat_message_cbs_get_user_data(const LinphoneChatMessageCbs *cbs) {
+ return cbs->user_data;
+}
+
+void linphone_chat_message_cbs_set_user_data(LinphoneChatMessageCbs *cbs, void *ud) {
+ cbs->user_data = ud;
+}
+
+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) {
+ cbs->msg_state_changed = cb;
+}
+
+LinphoneChatMessageCbsFileTransferRecvCb linphone_chat_message_cbs_get_file_transfer_recv(const LinphoneChatMessageCbs *cbs) {
+ return cbs->file_transfer_recv;
+}
+
+void linphone_chat_message_cbs_set_file_transfer_recv(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferRecvCb cb) {
+ cbs->file_transfer_recv = cb;
+}
+
+LinphoneChatMessageCbsFileTransferSendCb linphone_chat_message_cbs_get_file_transfer_send(const LinphoneChatMessageCbs *cbs) {
+ return cbs->file_transfer_send;
+}
+
+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) {
+ return cbs->file_transfer_progress_indication;
+}
+
+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;
+}
+
+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 MSList* 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 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
+);
+
+static LinphoneChatRoom * _linphone_core_create_chat_room(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);
+ 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);
}
return NULL;
}
-
-/**
- * Destroy a LinphoneChatRoom.
- * @param cr #LinphoneChatRoom object
- */
-void linphone_chat_room_destroy(LinphoneChatRoom *cr){
- LinphoneCore *lc=cr->lc;
- lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr);
+
+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)){
+ break;
+ }
+ 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);
+ LinphoneChatRoom *ret;
+
+ 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);
+ linphone_address_destroy(to_addr);
+ 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 *ret = _linphone_core_get_chat_room(lc, addr);
+ if (!ret) {
+ ret = _linphone_core_create_chat_room(lc, linphone_address_clone(addr));
+ }
+ 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);
+ 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);
+ }
+
+}
+
+LinphoneChatRoom * linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) {
+ return _linphone_core_get_or_create_chat_room(lc, to);
+}
+
+static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) {
+ if (cr->composing_idle_timer) {
+ 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;
+ }
+}
+
+static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr) {
+ if (cr->composing_refresh_timer) {
+ 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;
+ }
+}
+
+static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr) {
+ if (cr->remote_composing_refresh_timer) {
+ 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) {
+ linphone_chat_room_unref(cr);
+}
+
+void linphone_chat_room_release(LinphoneChatRoom *cr) {
+ cr->lc = NULL;
+ linphone_chat_room_unref(cr);
+}
+
+LinphoneChatRoom * linphone_chat_room_ref(LinphoneChatRoom *cr) {
+ belle_sip_object_ref(cr);
+ return cr;
+}
+
+void linphone_chat_room_unref(LinphoneChatRoom *cr) {
+ belle_sip_object_unref(cr);
+}
+
+void * linphone_chat_room_get_user_data(const LinphoneChatRoom *cr) {
+ return cr->user_data;
+}
+
+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){
- const char *route=NULL;
- const char *identity=linphone_core_find_best_identity(cr->lc,cr->peer_url,&route);
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);
+ 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 ||
@@ -78,26 +574,75 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
call->state==LinphoneCallPausedByRemote){
ms_message("send SIP message through the existing call.");
op = call->op;
- call->pending_message=msg;
+ identity=linphone_core_find_best_identity(cr->lc,linphone_call_get_remote_address(call));
}
}
}
+ 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*/
- op = sal_op_new(cr->lc->sal);
- sal_op_set_route(op,route);
+ 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->custom_headers){
- sal_op_set_custom_header(op,msg->custom_headers);
- msg->custom_headers=NULL; /*transfered to the SalOp*/
- }
}
+
+
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);
+ sal_message_send(op,identity,cr->peer,content_type, NULL, NULL);
ms_free(content_type);
} else {
- sal_text_send(op, identity, cr->peer,msg->message);
+ 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";
+ }
+ } 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);
+ }
+
+ 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);
}
}
@@ -111,233 +656,696 @@ void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) {
_linphone_chat_room_send_message(cr,linphone_chat_room_create_message(cr,msg));
}
-bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from){
- if (linphone_address_get_username(cr->peer_url) && linphone_address_get_username(from) &&
- strcmp(linphone_address_get_username(cr->peer_url),linphone_address_get_username(from))==0) return TRUE;
- return FALSE;
-}
-
void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg){
- if (msg->message)
- //legacy API
- if (lc->vtable.text_received!=NULL) lc->vtable.text_received(lc, cr, msg->from, msg->message);
- if (lc->vtable.message_received!=NULL) lc->vtable.message_received(lc, cr,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);
+ 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){
- MSList *elem;
LinphoneChatRoom *cr=NULL;
LinphoneAddress *addr;
- char *cleanfrom;
LinphoneChatMessage* msg;
const SalCustomHeader *ch;
-
+
addr=linphone_address_new(sal_msg->from);
linphone_address_clean(addr);
- for(elem=lc->chatrooms;elem!=NULL;elem=ms_list_next(elem)){
- cr=(LinphoneChatRoom*)elem->data;
- if (linphone_chat_room_matches(cr,addr)){
- break;
+ 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" */
+ 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->file_transfer_information = linphone_content_new();
+
+ /* parse the message 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 */
+ xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
+ 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) {
+ 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));
+ 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));
+ }
+ 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') {
+ contentTypeIndex++;
+ }
+ type = ms_strndup((char *)contentType, contentTypeIndex);
+ 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);
+ ms_free(type);
+ xmlFree(contentType);
+ }
+ if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
+ 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 */
+ /* convert the key from base 64 */
+ xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
+ int 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 */
+ xmlFree(keyb64);
+ free(keyBuffer);
+ }
+
+ cur=cur->next;
+ }
+ xmlFree(typeAttribute);
+ break;
+ }
+ xmlFree(typeAttribute);
+ }
+ cur = cur->next;
+ }
}
- cr=NULL;
+ xmlFreeDoc(xmlMessageBody);
+
+ 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 */
+ msg = linphone_chat_room_create_message(cr, sal_msg->text);
}
- cleanfrom=linphone_address_as_string(addr);
- if (cr==NULL){
- /* create a new chat room */
- cr=linphone_core_create_chat_room(lc,cleanfrom);
- }
- 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;
+ }
+
msg->time=sal_msg->time;
- ch=sal_op_get_custom_header(op);
+ 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);
+
+ if(cr->unread_count < 0) cr->unread_count = 1;
+ else cr->unread_count++;
+
linphone_chat_room_message_received(cr,lc,msg);
- ms_free(cleanfrom);
+ linphone_chat_message_unref(msg);
+}
+
+static int linphone_chat_room_remote_refresh_composing_expired(void *data, unsigned int revents) {
+ LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
+ belle_sip_object_unref(cr->remote_composing_refresh_timer);
+ cr->remote_composing_refresh_timer = NULL;
+ cr->remote_is_composing = LinphoneIsComposingIdle;
+ linphone_core_notify_is_composing_received(cr->lc, cr);
+ return BELLE_SIP_STOP;
+}
+
+static const char *iscomposing_prefix = "/xsi:isComposing";
+
+static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsing_context_t *xml_ctx) {
+ char xpath_str[MAX_XPATH_LENGTH];
+ 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 i;
+ LinphoneIsComposingState state = LinphoneIsComposingIdle;
+
+ 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");
+ iscomposing_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, iscomposing_prefix);
+ 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;
+ snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:refresh", iscomposing_prefix, i);
+ refresh_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
+ }
+ }
+ xmlXPathFreeObject(iscomposing_object);
+ }
+
+ if (state_str != NULL) {
+ if (strcmp(state_str, "active") == 0) {
+ state = LinphoneIsComposingActive;
+ if (refresh_str != NULL) {
+ 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");
+ } else {
+ belle_sip_source_set_timeout(cr->remote_composing_refresh_timer, refresh_duration * 1000);
+ }
+ } else {
+ linphone_chat_room_delete_remote_composing_refresh_timer(cr);
+ }
+
+ cr->remote_is_composing = state;
+ linphone_core_notify_is_composing_received(cr->lc, cr);
+ linphone_free_xml_text_content(state_str);
+ }
+ if (refresh_str != NULL) {
+ linphone_free_xml_text_content(refresh_str);
+ }
+}
+
+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);
+ if (xml_ctx->doc != NULL) {
+ process_im_is_composing_notification(cr, xml_ctx);
+ } else {
+ ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer);
+ }
+ linphone_xmlparsing_context_destroy(xml_ctx);
+}
+
+void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing) {
+ LinphoneAddress *addr = linphone_address_new(is_composing->from);
+ LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr);
+ if (cr != NULL) {
+ linphone_chat_room_notify_is_composing(cr, is_composing->text);
+ }
+ linphone_address_destroy(addr);
+}
+
+bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
+ return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE;
}
-/**
- * Returns back pointer to LinphoneCore object.
-**/
LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr){
return cr->lc;
}
-/**
- * Assign a user pointer to the chat room.
-**/
-void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud){
- cr->user_data=ud;
+LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr){
+ return cr->lc;
}
-/**
- * Retrieve the user pointer associated with the chat room.
-**/
-void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr){
- return cr->user_data;
-}
-
-/**
- * get peer address \link linphone_core_create_chat_room() associated to \endlink this #LinphoneChatRoom
- * @param cr #LinphoneChatRoom object
- * @return #LinphoneAddress peer address
- */
const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) {
return cr->peer_url;
}
-/**
- * Create a message attached to a dedicated chat room;
- * @param cr the chat room.
- * @param message text message, NULL if absent.
- * @return a new #LinphoneChatMessage
- */
LinphoneChatMessage* linphone_chat_room_create_message(LinphoneChatRoom *cr, const char* message) {
- LinphoneChatMessage* msg = ms_new0(LinphoneChatMessage,1);
+ 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 */
+ msg->file_transfer_information = NULL; /* this property is used only when transfering file */
+ msg->http_request = NULL;
return msg;
}
-/**
- * Send a message to peer member of this chat room.
- * @param cr #LinphoneChatRoom object
- * @param msg #LinphoneChatMessage message to be sent
- * @param status_cb LinphoneChatMessageStateChangeCb status callback invoked when message is delivered or could not be delivered. May be NULL
- * @param ud user data for the status cb.
- * @note The LinphoneChatMessage must not be destroyed until the the callback is called.
- */
-void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* msg,LinphoneChatMessageStateChangeCb status_cb, void* ud) {
+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 */
+ if (is_incoming) {
+ 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)));
+ } else {
+ 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)));
+ }
+ 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;
_linphone_chat_room_send_message(cr, msg);
}
-/**
- * Returns a #LinphoneChatMessageState as a string.
- */
-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";
- default: return "Unknown state";
- }
-
+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) {
+ xmlBufferPtr buf;
+ xmlTextWriterPtr writer;
+ int err;
+ char *content = NULL;
+
+ buf = xmlBufferCreate();
+ if (buf == NULL) {
+ ms_error("Error creating the XML buffer");
+ return content;
+ }
+ writer = xmlNewTextWriterMemory(buf, 0);
+ if (writer == NULL) {
+ ms_error("Error creating the XML writer");
+ return content;
+ }
+
+ 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");
+ }
+ 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 = 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");
+ }
+ 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);
+ snprintf(refresh_str, sizeof(refresh_str), "%u", refresh_timeout);
+ err = xmlTextWriterWriteElement(writer, (const xmlChar *)"refresh", (const xmlChar *)refresh_str);
+ }
+ if (err >= 0) {
+ /* Close the "isComposing" element. */
+ err = xmlTextWriterEndElement(writer);
+ }
+ if (err >= 0) {
+ err = xmlTextWriterEndDocument(writer);
+ }
+ if (err > 0) {
+ /* xmlTextWriterEndDocument returns the size of the content. */
+ content = ms_strdup((char *)buf->content);
+ }
+ xmlFreeTextWriter(writer);
+ xmlBufferFree(buf);
+ return content;
+}
+
+static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *cr) {
+ SalOp *op = NULL;
+ const char *identity = NULL;
+ char *content = 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*/
+ 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));
+
+ content = linphone_chat_room_create_is_composing_xml(cr);
+ if (content != NULL) {
+ sal_message_send(op, identity, cr->peer, "application/im-iscomposing+xml", content, NULL);
+ ms_free(content);
+ sal_op_unref(op);
+ }
+}
+
+static int linphone_chat_room_stop_composing(void *data, unsigned int revents) {
+ LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
+ cr->is_composing = LinphoneIsComposingIdle;
+ linphone_chat_room_send_is_composing_notification(cr);
+ linphone_chat_room_delete_composing_refresh_timer(cr);
+ belle_sip_object_unref(cr->composing_idle_timer);
+ cr->composing_idle_timer = NULL;
+ return BELLE_SIP_STOP;
+}
+
+static int linphone_chat_room_refresh_composing(void *data, unsigned int revents) {
+ LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
+ linphone_chat_room_send_is_composing_notification(cr);
+ return BELLE_SIP_CONTINUE;
+}
+
+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);
+ 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");
+ } 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");
+ }
+ }
+ belle_sip_source_set_timeout(cr->composing_idle_timer, idle_timeout * 1000);
+}
+
+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 ";
+ }
+ return NULL;
}
-/**
- * Returns the chatroom this message belongs to.
-**/
LinphoneChatRoom* linphone_chat_message_get_chat_room(LinphoneChatMessage *msg){
return msg->chat_room;
}
-/**
- * Returns the peer (remote) address for the message.
-**/
const LinphoneAddress* linphone_chat_message_get_peer_address(LinphoneChatMessage *msg) {
return linphone_chat_room_get_peer_address(msg->chat_room);
}
-/**
- *User pointer set function
- */
void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void* ud) {
message->message_userdata=ud;
}
-/**
- * User pointer get function
- */
void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message) {
return message->message_userdata;
}
-/**
- * Linphone message can carry external body as defined by rfc2017
- * @param message #LinphoneChatMessage
- * @return external body url or NULL if not present.
- */
const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message) {
return message->external_body_url;
}
-/**
- * Linphone message can carry external body as defined by rfc2017
- *
- * @param message a LinphoneChatMessage
- * @param url ex: access-type=URL; URL="http://www.foo.com/file"
- */
-void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* 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);
}
message->external_body_url=url?ms_strdup(url):NULL;
}
-/**
- * Set origin of the message
- *@param message #LinphoneChatMessage obj
- *@param from #LinphoneAddress origin of this message (copied)
- */
-void linphone_chat_message_set_from(LinphoneChatMessage* message, const LinphoneAddress* from) {
+const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message){
+ return message->appdata;
+}
+
+void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data){
+ if ( message->appdata ){
+ ms_free(message->appdata);
+ }
+ message->appdata = data? ms_strdup(data) : NULL;
+ linphone_chat_message_store_appdata(message);
+}
+
+
+const LinphoneContent *linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage*message) {
+ return message->file_transfer_information;
+}
+
+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;
+}
+
+
+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;
+}
+
+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);
}
-/**
- * Get origin of the message
- *@param message #LinphoneChatMessage obj
- *@return #LinphoneAddress
- */
-LinphoneAddress* linphone_chat_message_get_from(const LinphoneChatMessage* message) {
+const LinphoneAddress* linphone_chat_message_get_from_address(const LinphoneChatMessage* message) {
return message->from;
}
-/**
- * Get the time the message was sent.
- */
+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;
+ }
+ return NULL;
+}
+
+LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage* message){
+ return message->dir==LinphoneChatMessageOutgoing ? message->from : message->to;
+}
+
time_t linphone_chat_message_get_time(const LinphoneChatMessage* message) {
return message->time;
}
-/**
- * Get text part of this message
- * @return text or NULL if no text.
- */
+LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage* message) {
+ return message->state;
+}
+
const char * linphone_chat_message_get_text(const LinphoneChatMessage* message) {
return message->message;
}
-/**
- * Add custom headers to the message.
- * @param message the message
- * @param header_name name of the header_name
- * @param header_value header value
-**/
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);
}
-/**
- * Retrieve a custom header value given its name.
- * @param message the message
- * @param header_name header name searched
-**/
const char * linphone_chat_message_get_custom_header(LinphoneChatMessage* message, const char *header_name){
return sal_custom_header_find(message->custom_headers,header_name);
}
-/**
- * Duplicate a LinphoneChatMessage
-**/
+bool_t linphone_chat_message_is_read(LinphoneChatMessage* message) {
+ return message->is_read;
+}
+
+bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message) {
+ return message->dir == LinphoneChatMessageOutgoing;
+}
+
+unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message) {
+ return message->storage_id;
+}
+
LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg) {
/*struct _LinphoneChatMessage {
char* message;
@@ -347,31 +1355,98 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg)
void* message_userdata;
char* external_body_url;
LinphoneAddress* from;
+ time_t time;
+ 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);
return new_message;
}
-/**
- * Destroys a LinphoneChatMessage.
-**/
-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);
- ms_free(msg);
+ 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);
+}
+
+LinphoneChatMessage * linphone_chat_message_ref(LinphoneChatMessage *msg){
+ belle_sip_object_ref(msg);
+ return 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*/
+ msg->chat_room = NULL;
+ linphone_chat_message_unref(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) {
+ 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) {
+ 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;
+}
diff --git a/coreapi/conference.c b/coreapi/conference.c
index c98392e95..5479c8315 100644
--- a/coreapi/conference.c
+++ b/coreapi/conference.c
@@ -22,7 +22,7 @@
* 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"
@@ -41,6 +41,7 @@ static void conference_check_init(LinphoneConference *ctx, int samplerate){
MSAudioConferenceParams params;
params.samplerate=samplerate;
ctx->conf=ms_audio_conference_new(¶ms);
+ ctx->terminated=FALSE;
}
}
@@ -74,7 +75,7 @@ void linphone_core_conference_check_uninit(LinphoneCore *lc){
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){
+ if (remote_count==1 && !ctx->terminated){
convert_conference_to_call(lc);
}
if (remote_count==0){
@@ -86,7 +87,7 @@ void linphone_core_conference_check_uninit(LinphoneCore *lc){
ctx->record_endpoint=NULL;
}
}
-
+
if (ms_audio_conference_get_size(ctx->conf)==0){
ms_audio_conference_destroy(ctx->conf);
ctx->conf=NULL;
@@ -98,8 +99,8 @@ 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_active = FALSE;
+ 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);
@@ -109,7 +110,7 @@ void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted){
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;
@@ -127,12 +128,12 @@ 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 ?
+ 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,
@@ -150,13 +151,13 @@ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){
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
- * @returns the measured input volume expressed in dbm0.
+ * @return the measured input volume expressed in dbm0.
**/
float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
LinphoneConference *conf=&lc->conf_ctx;
@@ -165,7 +166,7 @@ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
float vol=0;
ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
return vol;
-
+
}
return LINPHONE_VOLUME_DB_LOWEST;
}
@@ -174,37 +175,42 @@ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
* 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.
- *
- * @returns 0 if successful, -1 otherwise.
+ *
+ * @return 0 if successful, -1 otherwise.
**/
int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
- LinphoneCallParams params;
LinphoneConference *conf=&lc->conf_ctx;
-
- if (call->current_params.in_conference){
+
+ 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));
- call->params.in_conference=TRUE;
- call->params.has_video=FALSE;
- call->params.media_encryption=LinphoneMediaEncryptionNone;
- params=call->params;
- if (call->state==LinphoneCallPaused)
+
+ 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){
- /*this will trigger a reINVITE that will later redraw the streams */
+ }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_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;
}
- linphone_core_update_call(lc,call,¶ms);
+ /*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));
@@ -217,8 +223,8 @@ static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t a
int err=0;
char *str;
- if (!call->current_params.in_conference){
- if (call->params.in_conference){
+ if (!call->current_params->in_conference){
+ if (call->params->in_conference){
ms_warning("Not (yet) in conference, be patient");
return -1;
}else{
@@ -226,24 +232,26 @@ static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t a
return -1;
}
}
- call->params.in_conference=FALSE;
+ 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,&call->params);
+ 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);
+ err=_linphone_core_pause_call(lc,call);
}
-
return err;
}
@@ -259,7 +267,7 @@ static int convert_conference_to_call(LinphoneCore *lc){
while (calls) {
LinphoneCall *rc=(LinphoneCall*)calls->data;
calls=calls->next;
- if (rc->params.in_conference) { // not using current_param
+ 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;
@@ -272,16 +280,16 @@ static int convert_conference_to_call(LinphoneCore *lc){
* 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.
- *
- * @returns 0 if successful, -1 otherwise.
+ *
+ * @return 0 if successful, -1 otherwise.
**/
int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
int err;
@@ -303,11 +311,6 @@ int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
return err;
}
-/**
- * Indicates whether the local participant is part of the conference.
- * @param lc the linphone core
- * @returns TRUE if the local participant is in the conference, FALSE otherwise.
-**/
bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
return lc->conf_ctx.local_participant!=NULL;
}
@@ -316,7 +319,7 @@ bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
* 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.
- * @returns 0 if successful, -1 otherwise.
+ * @return 0 if successful, -1 otherwise.
**/
int linphone_core_leave_conference(LinphoneCore *lc){
LinphoneConference *conf=&lc->conf_ctx;
@@ -328,13 +331,13 @@ int linphone_core_leave_conference(LinphoneCore *lc){
/**
* Moves the local participant inside the conference.
* @param lc the linphone core
- *
- * Makes the local participant to join the conference.
+ *
+ * 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.
- *
- * @returns 0 if successful, -1 otherwise
+ *
+ * @return 0 if successful, -1 otherwise
**/
int linphone_core_enter_conference(LinphoneCore *lc){
LinphoneConference *conf;
@@ -342,7 +345,7 @@ int linphone_core_enter_conference(LinphoneCore *lc){
return -1;
}
if (lc->current_call != NULL) {
- linphone_core_pause_call(lc, lc->current_call);
+ _linphone_core_pause_call(lc, lc->current_call);
}
conf=&lc->conf_ctx;
if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
@@ -352,17 +355,17 @@ int linphone_core_enter_conference(LinphoneCore *lc){
/**
* Add all calls into a conference.
* @param lc the linphone core
- *
+ *
* Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference.
- *
- * @returns 0 if successful, -1 otherwise
+ *
+ * @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) {
+ if (!call->current_params->in_conference) {
linphone_core_add_to_conference(lc, call);
}
}
@@ -373,17 +376,20 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) {
/**
* 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.
- *
- * @returns 0 if successful, -1 otherwise
+ *
+ * @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) {
+ if (call->current_params->in_conference) {
linphone_core_terminate_call(lc, call);
}
}
@@ -393,11 +399,11 @@ int linphone_core_terminate_conference(LinphoneCore *lc) {
/**
* 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.
- *
- * @returns the number of participants to the conference
+ *
+ * @return the number of participants to the conference
**/
int linphone_core_get_conference_size(LinphoneCore *lc) {
LinphoneConference *conf=&lc->conf_ctx;
diff --git a/coreapi/contact_providers_priv.h b/coreapi/contact_providers_priv.h
new file mode 100644
index 000000000..b3d667df6
--- /dev/null
+++ b/coreapi/contact_providers_priv.h
@@ -0,0 +1,67 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CONTACT_PROVIDERS_PRIV_H
+#define CONTACT_PROVIDERS_PRIV_H
+
+#include "private.h"
+#include "linphonecore.h"
+
+/* Base for contact search and contact provider */
+
+struct _LinphoneContactSearch{
+ belle_sip_object_t base;
+ ContactSearchID id;
+ char* predicate;
+ ContactSearchCallback cb;
+ void* data;
+};
+
+#define LINPHONE_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneContactSearch)
+BELLE_SIP_DECLARE_VPTR(LinphoneContactSearch)
+
+
+struct _LinphoneContactProvider {
+ belle_sip_object_t base;
+ LinphoneCore* lc;
+};
+
+#define LINPHONE_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneContactProvider)
+
+typedef LinphoneContactSearch* (*LinphoneContactProviderStartSearchMethod)( LinphoneContactProvider* thiz, const char* predicate, ContactSearchCallback cb, void* data );
+typedef unsigned int (*LinphoneContactProviderCancelSearchMethod)( LinphoneContactProvider* thiz, LinphoneContactSearch *request );
+
+BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider,belle_sip_object_t)
+ const char* name; /*!< Name of the contact provider (LDAP, Google, ...) */
+
+ /* pure virtual methods: inheriting objects must implement these */
+ LinphoneContactProviderStartSearchMethod begin_search;
+ LinphoneContactProviderCancelSearchMethod cancel_search;
+BELLE_SIP_DECLARE_CUSTOM_VPTR_END
+
+/* LDAP search and contact providers */
+
+
+#define LINPHONE_LDAP_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactSearch)
+BELLE_SIP_DECLARE_VPTR(LinphoneLDAPContactSearch)
+
+#define LINPHONE_LDAP_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactProvider)
+
+BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider,LinphoneContactProvider)
+BELLE_SIP_DECLARE_CUSTOM_VPTR_END
+
+
+#endif // CONTACT_PROVIDERS_PRIV_H
diff --git a/coreapi/contactprovider.c b/coreapi/contactprovider.c
new file mode 100644
index 000000000..2ee037c78
--- /dev/null
+++ b/coreapi/contactprovider.c
@@ -0,0 +1,138 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "contact_providers_priv.h"
+#include "contactprovider.h"
+#include
+
+/* ############################ *
+ * LinphoneContactSearchRequest *
+ * ############################ */
+
+void linphone_contact_search_init(LinphoneContactSearch* obj,
+ const char* predicate,
+ ContactSearchCallback cb,
+ void* cb_data)
+{
+ static unsigned int request_id_counter = 1;
+ obj->id = request_id_counter++; // unique id
+ obj->predicate = ms_strdup(predicate?predicate:"");
+ obj->cb = cb;
+ obj->data = cb_data;
+}
+
+static void linphone_contact_search_destroy( LinphoneContactSearch* req) {
+ if( req->predicate ) ms_free(req->predicate);
+}
+
+ContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj)
+{
+ return obj->id;
+}
+
+const char*linphone_contact_search_get_predicate(LinphoneContactSearch* obj)
+{
+ return obj->predicate;
+}
+
+void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends)
+{
+ if( req->cb ) req->cb(req, friends, req->data);
+}
+
+int linphone_contact_search_compare(const void* a, const void* b) {
+ LinphoneContactSearch *ra=((LinphoneContactSearch*)a);
+ LinphoneContactSearch *rb=((LinphoneContactSearch*)b);
+ return !(ra->id == rb->id); // return 0 if id is equal, 1 otherwise
+}
+
+LinphoneContactSearch*linphone_contact_search_ref(void* obj)
+{
+ return LINPHONE_CONTACT_SEARCH(belle_sip_object_ref(obj));
+}
+
+void linphone_ldap_contact_search_unref(void* obj)
+{
+ belle_sip_object_unref(obj);
+}
+
+LinphoneContactSearch* linphone_contact_search_cast(void* obj)
+{
+ return LINPHONE_CONTACT_SEARCH(obj);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactSearch);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneContactSearch,belle_sip_object_t,
+ (belle_sip_object_destroy_t)linphone_contact_search_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
+
+
+
+/* ####################### *
+ * LinphoneContactProvider *
+ * ####################### */
+
+
+void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc){
+ obj->lc = lc;
+}
+
+static void contact_provider_destroy(LinphoneContactProvider* obj){
+ (void)obj;
+}
+
+LinphoneContactSearch* linphone_contact_provider_begin_search(LinphoneContactProvider* obj, const char* predicate, ContactSearchCallback cb, void* data)
+{
+ return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->begin_search( LINPHONE_CONTACT_PROVIDER(obj), predicate, cb, data);
+}
+
+unsigned int linphone_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch* request)
+{
+ return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->cancel_search( LINPHONE_CONTACT_PROVIDER(obj), request);
+}
+
+LinphoneContactProvider* linphone_contact_provider_ref(void* obj)
+{
+ return LINPHONE_CONTACT_PROVIDER(belle_sip_object_ref(obj));
+}
+
+void linphone_contact_provider_unref(void* obj)
+{
+ belle_sip_object_unref(obj);
+}
+
+LinphoneContactProvider*linphone_contact_provider_cast(void* obj)
+{
+ return LINPHONE_CONTACT_PROVIDER(obj);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactProvider);
+BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider)
+ {
+ BELLE_SIP_VPTR_INIT(LinphoneContactProvider,belle_sip_object_t,TRUE),
+ (belle_sip_object_destroy_t) contact_provider_destroy,
+ NULL,/*no clone*/
+ NULL,/*no marshal*/
+ },
+ "",
+ // Pure virtual
+ NULL, /* begin_search -> pure virtual */
+ NULL /* cancel_search -> pure virtual */
+BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END
diff --git a/coreapi/contactprovider.h b/coreapi/contactprovider.h
new file mode 100644
index 000000000..d5c016562
--- /dev/null
+++ b/coreapi/contactprovider.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "linphonecore.h"
+
+/* LinphoneContactSearchRequest */
+
+void linphone_contact_search_init(LinphoneContactSearch* obj, const char* predicate, ContactSearchCallback cb, void* cb_data);
+ContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj);
+const char* linphone_contact_search_get_predicate(LinphoneContactSearch* obj);
+void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends);
+LINPHONE_PUBLIC LinphoneContactSearch* linphone_contact_search_ref(void* obj);
+void linphone_contact_search_unref(void* obj);
+LinphoneContactSearch* linphone_contact_search_cast( void*obj );
+
+/* LinphoneContactProvider */
+
+void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc);
+LinphoneCore* linphone_contact_provider_get_core(LinphoneContactProvider* obj);
+const char* linphone_contact_provider_get_name(LinphoneContactProvider* obj);
+LinphoneContactProvider* linphone_contact_provider_ref(void* obj);
+LINPHONE_PUBLIC void linphone_contact_provider_unref(void* obj);
+LINPHONE_PUBLIC LinphoneContactProvider* linphone_contact_provider_cast( void*obj );
+
+LINPHONE_PUBLIC LinphoneContactSearch* linphone_contact_provider_begin_search(LinphoneContactProvider* obj,
+ const char* predicate,
+ ContactSearchCallback cb,
+ void* data);
+unsigned int linphone_contact_provider_cancel_search(LinphoneContactProvider* obj,
+ LinphoneContactSearch* request);
diff --git a/coreapi/content.c b/coreapi/content.c
new file mode 100644
index 000000000..9f5cd4fee
--- /dev/null
+++ b/coreapi/content.c
@@ -0,0 +1,243 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "linphonecore.h"
+#include "private.h"
+
+
+
+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);
+ /* 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_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) {
+ linphone_content_set_buffer(obj, linphone_content_get_buffer(ref), linphone_content_get_size(ref));
+ } else {
+ linphone_content_set_size(obj, linphone_content_get_size(ref));
+ }
+}
+
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContent);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneContent, belle_sip_object_t,
+ (belle_sip_object_destroy_t)linphone_content_destroy,
+ (belle_sip_object_clone_t)linphone_content_clone,
+ NULL, // marshal
+ TRUE
+);
+
+
+LinphoneContent * linphone_core_create_content(LinphoneCore *lc) {
+ return linphone_content_new();
+}
+
+LinphoneContent * linphone_content_ref(LinphoneContent *content) {
+ belle_sip_object_ref(content);
+ return content;
+}
+
+void linphone_content_unref(LinphoneContent *content) {
+ belle_sip_object_unref(content);
+}
+
+void *linphone_content_get_user_data(const LinphoneContent *content) {
+ return content->user_data;
+}
+
+void linphone_content_set_user_data(LinphoneContent *content, void *ud) {
+ content->user_data = ud;
+}
+
+const char * linphone_content_get_type(const LinphoneContent *content) {
+ return content->lcp.type;
+}
+
+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);
+ }
+}
+
+const char * linphone_content_get_subtype(const LinphoneContent *content) {
+ return content->lcp.subtype;
+}
+
+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);
+ }
+}
+
+void * linphone_content_get_buffer(const LinphoneContent *content) {
+ return content->lcp.data;
+}
+
+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';
+}
+
+const char * linphone_content_get_string_buffer(const LinphoneContent *content) {
+ return (char *)content->lcp.data;
+}
+
+void linphone_content_set_string_buffer(LinphoneContent *content, const char *buffer) {
+ content->lcp.size = strlen(buffer);
+ content->lcp.data = belle_sip_strdup(buffer);
+}
+
+size_t linphone_content_get_size(const LinphoneContent *content) {
+ return content->lcp.size;
+}
+
+void linphone_content_set_size(LinphoneContent *content, size_t size) {
+ content->lcp.size = size;
+}
+
+const char * linphone_content_get_encoding(const LinphoneContent *content) {
+ return content->lcp.encoding;
+}
+
+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);
+ }
+}
+
+const char * linphone_content_get_name(const LinphoneContent *content) {
+ return content->lcp.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 (name != NULL) {
+ content->lcp.name = belle_sip_strdup(name);
+ }
+}
+
+size_t linphone_content_get_key_size(const LinphoneContent *content) {
+ return content->lcp.keyLength;
+}
+
+const char * linphone_content_get_key(const LinphoneContent *content) {
+ return content->lcp.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 (key != NULL) {
+ content->lcp.key = belle_sip_malloc(keyLength);
+ memcpy(content->lcp.key, key, 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);
+}
+
+
+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;
+}
+
+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;
+ }
+ 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;
+}
diff --git a/coreapi/content.h b/coreapi/content.h
new file mode 100644
index 000000000..1e21138eb
--- /dev/null
+++ b/coreapi/content.h
@@ -0,0 +1,231 @@
+/*
+content.h
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef LINPHONE_CONTENT_H_
+#define LINPHONE_CONTENT_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @addtogroup misc
+ * @{
+ */
+
+/**
+ * The LinphoneContent object holds data that can be embedded in a signaling message.
+**/
+struct _LinphoneContent;
+/**
+ * The LinphoneContent object holds data that can be embedded in a signaling message.
+**/
+typedef struct _LinphoneContent LinphoneContent;
+
+/**
+ * @deprecated Use LinphoneContent objects instead of this structure.
+ */
+struct _LinphoneContentPrivate{
+ char *type; /**
+#include
+#include
+
+
+/**
+ * @addtogroup misc
+ * @{
+**/
+
+
+LinphoneDictionary* linphone_dictionary_new()
+{
+ return belle_sip_dict_create();
+}
+
+LinphoneDictionary* linphone_dictionary_clone(const LinphoneDictionary* src)
+{
+ LinphoneDictionary* cloned = linphone_dictionary_new();
+ if( cloned ){
+ belle_sip_dict_clone(src, cloned);
+ }
+ return cloned;
+}
+
+LinphoneDictionary* linphone_dictionary_ref(LinphoneDictionary* obj)
+{
+ return BELLE_SIP_DICT(belle_sip_object_ref(obj));
+}
+
+void linphone_dictionary_unref(LinphoneDictionary *obj)
+{
+ belle_sip_object_unref(obj);
+}
+
+void linphone_dictionary_set_int(LinphoneDictionary* obj, const char* key, int value)
+{
+ belle_sip_dict_set_int(obj, key, value);
+}
+
+int linphone_dictionary_get_int(LinphoneDictionary* obj, const char* key, int default_value)
+{
+ return belle_sip_dict_get_int(obj, key, default_value);
+}
+
+void linphone_dictionary_set_string(LinphoneDictionary* obj, const char* key, const char*value)
+{
+ belle_sip_dict_set_string(obj, key, value);
+}
+
+const char* linphone_dictionary_get_string(LinphoneDictionary* obj, const char* key, const char* default_value)
+{
+ return belle_sip_dict_get_string(obj, key, default_value);
+}
+
+void linphone_dictionary_set_int64(LinphoneDictionary* obj, const char* key, int64_t value)
+{
+ belle_sip_dict_set_int64(obj, key, value);
+}
+
+int64_t linphone_dictionary_get_int64(LinphoneDictionary* obj, const char* key, int64_t default_value)
+{
+ return belle_sip_dict_get_int64(obj, key, default_value);
+}
+
+int linphone_dictionary_remove(LinphoneDictionary* obj, const char* key)
+{
+ return belle_sip_dict_remove(obj, key);
+}
+
+void linphone_dictionary_clear(LinphoneDictionary* obj)
+{
+ belle_sip_dict_clear(obj);
+}
+
+int linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key)
+{
+ return belle_sip_dict_haskey(obj, key);
+}
+
+void linphone_dictionary_foreach(const LinphoneDictionary* obj, void (*apply_func)(const char*, void*, void*), void* userdata)
+{
+ return belle_sip_dict_foreach(obj, apply_func, userdata);
+}
+
+struct lp_config_to_dict {
+ const char* section;
+ const LpConfig* config;
+ LinphoneDictionary* dict;
+};
+
+static void lp_config_section_to_dict_cb(const char*key, struct lp_config_to_dict* userdata)
+{
+ const char* value = lp_config_get_string(userdata->config, userdata->section, key, "");
+ linphone_dictionary_set_string(userdata->dict, key, value);
+}
+
+LinphoneDictionary* lp_config_section_to_dict(const LpConfig* lpconfig, const char* section)
+{
+ LinphoneDictionary* dict = NULL;
+ struct lp_config_to_dict fd;
+ fd.config = lpconfig;
+ fd.section = section;
+
+ dict = linphone_dictionary_new();
+ fd.dict = dict;
+
+ lp_config_for_each_entry(lpconfig, section,
+ (void (*)(const char*, void*))lp_config_section_to_dict_cb,
+ &fd);
+
+ return dict;
+}
+
+struct lp_config_from_dict {
+ const char* section;
+ LpConfig* config;
+};
+
+static void lp_config_dict_dump_cb( const char* key, void* value, void* userdata)
+{
+ struct lp_config_from_dict* fd= (struct lp_config_from_dict*)userdata;
+ lp_config_set_string(fd->config, fd->section, key, (const char*)value);
+}
+
+void lp_config_load_dict_to_section(LpConfig* lpconfig, const char* section, const LinphoneDictionary* dict)
+{
+ struct lp_config_from_dict pvdata = { section, lpconfig };
+ linphone_dictionary_foreach(dict,lp_config_dict_dump_cb, &pvdata);
+}
+
+
+
+/**
+ * @}
+**/
diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c
index f0f009747..bff0a00f1 100644
--- a/coreapi/ec-calibrator.c
+++ b/coreapi/ec-calibrator.c
@@ -29,37 +29,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
static void ecc_init_filters(EcCalibrator *ecc){
unsigned int rate;
+ int channels = 1;
+ int ecc_channels = 1;
MSTickerParams params={0};
params.name="Echo calibrator";
params.prio=MS_TICKER_PRIO_HIGH;
ecc->ticker=ms_ticker_new_with_params(¶ms);
- ecc->sndread=ms_snd_card_create_reader(ecc->play_card);
+ ecc->sndread=ms_snd_card_create_reader(ecc->capt_card);
ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate);
+ ms_filter_call_method(ecc->sndread,MS_FILTER_SET_NCHANNELS,&ecc_channels);
+ ms_filter_call_method(ecc->sndread,MS_FILTER_GET_NCHANNELS,&channels);
ecc->read_resampler=ms_filter_new(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);
ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
- ecc->rec=ms_filter_new(MS_FILE_REC_ID);
+ ecc->rec=ms_filter_new(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_FILE_PLAYER_ID);
+ ecc->play=ms_filter_new(MS_VOID_SOURCE_ID);
ecc->gen=ms_filter_new(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->sndwrite=ms_snd_card_create_writer(ecc->capt_card);
+ ecc->sndwrite=ms_snd_card_create_writer(ecc->play_card);
ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate);
+ ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_NCHANNELS,&ecc_channels);
+ ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_NCHANNELS,&channels);
ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate);
+ ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_NCHANNELS,&ecc_channels);
+ ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
ms_filter_link(ecc->play,0,ecc->gen,0);
ms_filter_link(ecc->gen,0,ecc->write_resampler,0);
@@ -67,10 +77,17 @@ static void ecc_init_filters(EcCalibrator *ecc){
ms_ticker_attach(ecc->ticker,ecc->sndread);
ms_ticker_attach(ecc->ticker,ecc->play);
-
+
+ if (ecc->audio_init_cb != NULL) {
+ (*ecc->audio_init_cb)(ecc->cb_data);
+ }
}
static void ecc_deinit_filters(EcCalibrator *ecc){
+ if (ecc->audio_uninit_cb != NULL) {
+ (*ecc->audio_uninit_cb)(ecc->cb_data);
+ }
+
ms_ticker_detach(ecc->ticker,ecc->sndread);
ms_ticker_detach(ecc->ticker,ecc->play);
@@ -133,8 +150,11 @@ static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, voi
static void ecc_play_tones(EcCalibrator *ecc){
MSDtmfGenCustomTone tone;
MSToneDetectorDef expected_tone;
+
+ memset(&tone,0,sizeof(tone));
+ memset(&expected_tone,0,sizeof(expected_tone));
- ms_filter_set_notify_callback(ecc->det,on_tone_received,ecc);
+ ms_filter_add_notify_callback(ecc->det,on_tone_received,ecc,TRUE);
/* configure the tones to be scanned */
@@ -161,28 +181,28 @@ static void ecc_play_tones(EcCalibrator *ecc){
/*play an initial tone to startup the audio playback/capture*/
- tone.frequency=140;
+ tone.frequencies[0]=140;
tone.duration=1000;
tone.amplitude=0.5;
ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
ms_sleep(2);
- ms_filter_set_notify_callback(ecc->gen,on_tone_sent,ecc);
+ ms_filter_add_notify_callback(ecc->gen,on_tone_sent,ecc,TRUE);
/* play the three tones*/
- tone.frequency=2000;
+ tone.frequencies[0]=2000;
tone.duration=100;
ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
ms_usleep(300000);
- tone.frequency=2300;
+ tone.frequencies[0]=2300;
tone.duration=100;
ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
ms_usleep(300000);
- tone.frequency=2500;
+ tone.frequencies[0]=2500;
tone.duration=100;
ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
ms_sleep(1);
@@ -219,12 +239,15 @@ static void * ecc_thread(void *p){
return NULL;
}
-EcCalibrator * ec_calibrator_new(MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb, void *cb_data ){
+EcCalibrator * ec_calibrator_new(MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb,
+ LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){
EcCalibrator *ecc=ms_new0(EcCalibrator,1);
ecc->rate=rate;
ecc->cb=cb;
ecc->cb_data=cb_data;
+ ecc->audio_init_cb=audio_init_cb;
+ ecc->audio_uninit_cb=audio_uninit_cb;
ecc->capt_card=capt_card;
ecc->play_card=play_card;
ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc);
@@ -240,7 +263,8 @@ void ec_calibrator_destroy(EcCalibrator *ecc){
ms_free(ecc);
}
-int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, void *cb_data){
+int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb,
+ LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){
unsigned int rate;
if (lc->ecc!=NULL){
@@ -248,7 +272,7 @@ 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,cb_data);
+ 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);
return 0;
}
diff --git a/coreapi/event.c b/coreapi/event.c
new file mode 100644
index 000000000..836b929a7
--- /dev/null
+++ b/coreapi/event.c
@@ -0,0 +1,368 @@
+/*
+linphone
+Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.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"
+
+
+LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss){
+ switch(ss){
+ case SalSubscribeNone: return LinphoneSubscriptionNone;
+ case SalSubscribePending: return LinphoneSubscriptionPending;
+ case SalSubscribeTerminated: return LinphoneSubscriptionTerminated;
+ case SalSubscribeActive: return LinphoneSubscriptionActive;
+ }
+ return LinphoneSubscriptionNone;
+}
+
+const char *linphone_subscription_state_to_string(LinphoneSubscriptionState state){
+ switch(state){
+ case LinphoneSubscriptionNone: return "LinphoneSubscriptionNone";
+ case LinphoneSubscriptionIncomingReceived: return "LinphoneSubscriptionIncomingReceived";
+ case LinphoneSubscriptionOutgoingInit: return "LinphoneSubscriptionOutoingInit";
+ case LinphoneSubscriptionPending: return "LinphoneSubscriptionPending";
+ case LinphoneSubscriptionActive: return "LinphoneSubscriptionActive";
+ case LinphoneSubscriptionTerminated: return "LinphoneSubscriptionTerminated";
+ case LinphoneSubscriptionError: return "LinphoneSubscriptionError";
+ case LinphoneSubscriptionExpiring: return "LinphoneSubscriptionExpiring";
+ }
+ return NULL;
+}
+
+LINPHONE_PUBLIC const char *linphone_publish_state_to_string(LinphonePublishState state){
+ switch(state){
+ case LinphonePublishNone: return "LinphonePublishNone";
+ case LinphonePublishProgress: return "LinphonePublishProgress";
+ case LinphonePublishOk: return "LinphonePublishOk";
+ case LinphonePublishError: return "LinphonePublishError";
+ case LinphonePublishCleared: return "LinphonePublishCleared";
+ case LinphonePublishExpiring: return "LinphonePublishExpiring";
+ }
+ return NULL;
+}
+
+static LinphoneEvent * linphone_event_new_base(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, SalOp *op){
+ LinphoneEvent *lev=ms_new0(LinphoneEvent,1);
+ 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;
+}
+
+LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, int expires){
+ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, sal_op_new(lc->sal));
+ lev->expires=expires;
+ return lev;
+}
+
+static LinphoneEvent *linphone_event_new_with_op_base(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name, bool_t is_out_of_dialog){
+ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, op);
+ lev->is_out_of_dialog_op=is_out_of_dialog;
+ return lev;
+}
+
+LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) {
+ return linphone_event_new_with_op_base(lc,op,dir,name,FALSE);
+}
+
+LinphoneEvent *linphone_event_new_with_out_of_dialog_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) {
+ return linphone_event_new_with_op_base(lc,op,dir,name,TRUE);
+}
+
+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);
+ }
+ }
+}
+
+void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state){
+ if (lev->publish_state!=state){
+ ms_message("LinphoneEvent [%p] moving to publish state %s",lev,linphone_publish_state_to_string(state));
+ lev->publish_state=state;
+ linphone_core_notify_publish_state_changed(lev->lc,lev,state);
+ switch(state){
+ case LinphonePublishCleared:
+ if (lev->expires!=-1)
+ linphone_event_unref(lev);
+ break;
+ case LinphonePublishOk:
+ case LinphonePublishError:
+ if (lev->expires==-1)
+ linphone_event_unref(lev);
+ break;
+ case LinphonePublishNone:
+ case LinphonePublishProgress:
+ case LinphonePublishExpiring:
+ /*nothing special to do*/
+ break;
+ }
+
+ }
+}
+
+LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){
+ return lev->publish_state;
+}
+
+const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev){
+ return linphone_error_info_from_sal_op(lev->op);
+}
+
+LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){
+ return linphone_error_info_get_reason(linphone_event_get_error_info(lev));
+}
+
+LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){
+ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event, expires);
+ linphone_configure_op(lc,lev->op,resource,NULL,TRUE);
+ sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_subscribe",1));
+ 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;
+ int err;
+
+ if (lev->dir!=LinphoneSubscriptionOutgoing){
+ ms_error("linphone_event_send_subscribe(): cannot send or update something that is not an outgoing subscription.");
+ return -1;
+ }
+ switch (lev->subscription_state){
+ case LinphoneSubscriptionIncomingReceived:
+ case LinphoneSubscriptionTerminated:
+ case LinphoneSubscriptionOutgoingInit:
+ ms_error("linphone_event_send_subscribe(): cannot update subscription while in state [%s]", linphone_subscription_state_to_string(lev->subscription_state));
+ return -1;
+ break;
+ case LinphoneSubscriptionNone:
+ case LinphoneSubscriptionActive:
+ case LinphoneSubscriptionExpiring:
+ case LinphoneSubscriptionError:
+ case LinphoneSubscriptionPending:
+ /*those states are ok*/
+ break;
+ }
+
+ if (lev->send_custom_headers){
+ sal_op_set_sent_custom_header(lev->op,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));
+ if (err==0){
+ if (lev->subscription_state==LinphoneSubscriptionNone)
+ linphone_event_set_state(lev,LinphoneSubscriptionOutgoingInit);
+ }
+ return err;
+}
+
+int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){
+ return linphone_event_send_subscribe(lev,body);
+}
+
+int linphone_event_accept_subscription(LinphoneEvent *lev){
+ int err;
+ if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
+ ms_error("linphone_event_accept_subscription(): cannot accept subscription if subscription wasn't just received.");
+ return -1;
+ }
+ err=sal_subscribe_accept(lev->op);
+ if (err==0){
+ linphone_event_set_state(lev,LinphoneSubscriptionActive);
+ }
+ return err;
+}
+
+int linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){
+ int err;
+ if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
+ ms_error("linphone_event_deny_subscription(): cannot deny subscription if subscription wasn't just received.");
+ return -1;
+ }
+ err=sal_subscribe_decline(lev->op,linphone_reason_to_sal(reason));
+ linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
+ return err;
+}
+
+int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){
+ SalBody salbody;
+ if (lev->subscription_state!=LinphoneSubscriptionActive){
+ ms_error("linphone_event_notify(): cannot notify if subscription is not active.");
+ return -1;
+ }
+ if (lev->dir!=LinphoneSubscriptionIncoming){
+ ms_error("linphone_event_notify(): cannot notify if not an incoming subscription.");
+ return -1;
+ }
+ return sal_notify(lev->op,sal_body_from_content(&salbody,body));
+}
+
+LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){
+ LinphoneEvent *lev=linphone_event_new(lc,LinphoneSubscriptionInvalidDir, event,expires);
+ linphone_configure_op(lc,lev->op,resource,NULL,lp_config_get_int(lc->config,"sip","publish_msg_with_contact",0));
+ sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_publish",1));
+ return lev;
+}
+
+static int _linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body, bool_t notify_err){
+ SalBody salbody;
+ int err;
+
+ if (lev->dir!=LinphoneSubscriptionInvalidDir){
+ ms_error("linphone_event_update_publish(): this is not a PUBLISH event.");
+ return -1;
+ }
+ if (lev->send_custom_headers){
+ sal_op_set_sent_custom_header(lev->op,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));
+ if (err==0){
+ linphone_event_set_publish_state(lev,LinphonePublishProgress);
+ }else if (notify_err){
+ linphone_event_set_publish_state(lev,LinphonePublishError);
+ }
+ return err;
+}
+
+LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){
+ int err;
+ LinphoneEvent *lev=linphone_core_create_publish(lc,resource,event,expires);
+ err=_linphone_event_send_publish(lev,body,FALSE);
+ if (err==-1){
+ linphone_event_unref(lev);
+ lev=NULL;
+ }
+ return lev;
+}
+
+
+int linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body){
+ return _linphone_event_send_publish(lev,body,TRUE);
+}
+
+int linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* body ) {
+ return linphone_event_send_publish(lev,body);
+}
+
+void linphone_event_set_user_data(LinphoneEvent *ev, void *up){
+ ev->userdata=up;
+}
+
+void *linphone_event_get_user_data(const LinphoneEvent *ev){
+ return ev->userdata;
+}
+
+void linphone_event_add_custom_header(LinphoneEvent *ev, const char *name, const char *value){
+ ev->send_custom_headers=sal_custom_header_append(ev->send_custom_headers, name, value);
+}
+
+const char* linphone_event_get_custom_header(LinphoneEvent* ev, const char* name){
+ const SalCustomHeader *ch=sal_op_get_recv_custom_header(ev->op);
+ return sal_custom_header_find(ch,name);
+}
+
+
+void linphone_event_terminate(LinphoneEvent *lev){
+ lev->terminating=TRUE;
+ if (lev->dir==LinphoneSubscriptionIncoming){
+ sal_notify_close(lev->op);
+ }else if (lev->dir==LinphoneSubscriptionOutgoing){
+ sal_unsubscribe(lev->op);
+ }
+
+ 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);
+ linphone_event_set_publish_state(lev,LinphonePublishCleared);
+ return;
+ }
+
+ if (lev->subscription_state!=LinphoneSubscriptionNone){
+ linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
+ return;
+ }
+}
+
+
+LinphoneEvent *linphone_event_ref(LinphoneEvent *lev){
+ lev->refcnt++;
+ return lev;
+}
+
+static void linphone_event_destroy(LinphoneEvent *lev){
+ if (lev->op)
+ sal_op_release(lev->op);
+ ms_free(lev->name);
+ ms_free(lev);
+}
+
+void linphone_event_unref(LinphoneEvent *lev){
+ lev->refcnt--;
+ if (lev->refcnt==0) linphone_event_destroy(lev);
+}
+
+LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev){
+ return lev->dir;
+}
+
+LinphoneSubscriptionState linphone_event_get_subscription_state(const LinphoneEvent *lev){
+ return lev->subscription_state;
+}
+
+const char *linphone_event_get_name(const LinphoneEvent *lev){
+ return lev->name;
+}
+
+const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){
+ if (lev->is_out_of_dialog_op){
+ return (LinphoneAddress*)sal_op_get_to_address(lev->op);
+ }else{
+ return (LinphoneAddress*)sal_op_get_from_address(lev->op);
+ }
+}
+
+const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){
+ if (lev->is_out_of_dialog_op){
+ return (LinphoneAddress*)sal_op_get_from_address(lev->op);
+ }else{
+ return (LinphoneAddress*)sal_op_get_to_address(lev->op);
+ }
+}
+
+LinphoneCore *linphone_event_get_core(const LinphoneEvent *lev){
+ return lev->lc;
+}
+
diff --git a/coreapi/event.h b/coreapi/event.h
new file mode 100644
index 000000000..fe3f984de
--- /dev/null
+++ b/coreapi/event.h
@@ -0,0 +1,307 @@
+/*
+linphone
+Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.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.
+*/
+#ifndef LINPHONEEVENT_H
+#define LINPHONEEVENT_H
+
+/**
+ * @addtogroup event_api
+ * @{
+**/
+
+struct _LinphoneEvent;
+
+/**
+ * Object representing an event state, which is subcribed or published.
+ * @see linphone_core_publish()
+ * @see linphone_core_subscribe()
+**/
+typedef struct _LinphoneEvent LinphoneEvent;
+
+/**
+ * Enum for subscription direction (incoming or outgoing).
+**/
+enum _LinphoneSubscriptionDir{
+ LinphoneSubscriptionIncoming, /**< Incoming subscription. */
+ LinphoneSubscriptionOutgoing, /**< Outgoing subscription. */
+ LinphoneSubscriptionInvalidDir /**< Invalid subscription direction. */
+};
+
+/**
+ * Typedef alias for _LinphoneSubscriptionDir
+**/
+typedef enum _LinphoneSubscriptionDir LinphoneSubscriptionDir;
+
+/**
+ * Enum for subscription states.
+**/
+enum _LinphoneSubscriptionState{
+ LinphoneSubscriptionNone, /**< Initial state, should not be used.**/
+ LinphoneSubscriptionOutgoingProgress, /**refresh_generic_subscribe property is set to 0.*/
+};
+/*typo compatibility*/
+#define LinphoneSubscriptionOutoingInit LinphoneSubscriptionOutgoingInit
+
+#define LinphoneSubscriptionOutgoingInit LinphoneSubscriptionOutgoingProgress
+/**
+ * Typedef for subscription state enum.
+**/
+typedef enum _LinphoneSubscriptionState LinphoneSubscriptionState;
+
+LINPHONE_PUBLIC const char *linphone_subscription_state_to_string(LinphoneSubscriptionState state);
+
+/**
+ * Enum for publish states.
+**/
+enum _LinphonePublishState{
+ LinphonePublishNone, /**< Initial state, do not use**/
+ LinphonePublishProgress, /**refresh_generic_publish property is set to 0.*/
+ LinphonePublishCleared /**uri;
LinphoneAddress *fb=((LinphoneFriend*)b)->uri;
- if (linphone_address_weak_equal (fa,fb)) return 0;
+ if (linphone_address_weak_equal(fa,fb)) return 0;
return 1;
}
-MSList *linphone_find_friend(MSList *fl, const LinphoneAddress *friend, LinphoneFriend **lf){
+MSList *linphone_find_friend_by_address(MSList *fl, const LinphoneAddress *addr, LinphoneFriend **lf){
MSList *res=NULL;
LinphoneFriend dummy;
if (lf!=NULL) *lf=NULL;
- dummy.uri=(LinphoneAddress*)friend;
+ dummy.uri=(LinphoneAddress*)addr;
res=ms_list_find_custom(fl,friend_compare,&dummy);
if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)res->data;
return res;
@@ -90,42 +92,27 @@ 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 (lf->insub==op) return lf;
+ 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){
- LinphoneFriend *lf=(LinphoneFriend*)elem->data;
+ lf=(LinphoneFriend*)elem->data;
if (lf->outsub==op) return lf;
}
return NULL;
}
void __linphone_friend_do_subscribe(LinphoneFriend *fr){
- char *friend=NULL;
- const char *route=NULL;
- const char *from=NULL;
- const char *fixed_contact=NULL;
- LinphoneProxyConfig *cfg;
-
- friend=linphone_address_as_string(fr->uri);
- cfg=linphone_core_lookup_known_proxy(fr->lc,linphone_friend_get_address(fr));
- if (cfg!=NULL){
- route=linphone_proxy_config_get_route(cfg);
- from=linphone_proxy_config_get_identity(cfg);
- if (cfg->op){
- fixed_contact=sal_op_get_contact(cfg->op);
- if (fixed_contact) {
- ms_message("Contact for subscribe has been fixed using proxy to %s",fixed_contact);
- }
- }
- }else from=linphone_core_get_primary_contact(fr->lc);
+ LinphoneCore *lc=fr->lc;
+
if (fr->outsub==NULL){
/* people for which we don't have yet an answer should appear as offline */
- fr->status=LinphoneStatusOffline;
+ fr->presence=NULL;
/*
if (fr->lc->vtable.notify_recv)
fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr);
@@ -134,23 +121,21 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){
sal_op_release(fr->outsub);
fr->outsub=NULL;
}
- fr->outsub=sal_op_new(fr->lc->sal);
- sal_op_set_route(fr->outsub,route);
- sal_op_set_contact(fr->outsub,fixed_contact);
- sal_subscribe_presence(fr->outsub,from,friend);
+ 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;
- ms_free(friend);
}
LinphoneFriend * linphone_friend_new(){
- LinphoneFriend *obj=ms_new0(LinphoneFriend,1);
+ LinphoneFriend *obj=belle_sip_object_new(LinphoneFriend);
obj->pol=LinphoneSPAccept;
- obj->status=LinphoneStatusOffline;
+ obj->presence=NULL;
obj->subscribe=TRUE;
- return obj;
+ return obj;
}
-LinphoneFriend *linphone_friend_new_with_addr(const char *addr){
+LinphoneFriend *linphone_friend_new_with_address(const char *addr){
LinphoneAddress* linphone_address = linphone_address_new(addr);
LinphoneFriend *fr;
@@ -159,13 +144,19 @@ LinphoneFriend *linphone_friend_new_with_addr(const char *addr){
return NULL;
}
fr=linphone_friend_new();
- if (linphone_friend_set_addr(fr,linphone_address)<0){
- linphone_friend_destroy(fr);
- return NULL;
- }
+ linphone_friend_set_address(fr,linphone_address);
+ linphone_address_destroy(linphone_address);
return fr;
}
+void linphone_friend_set_user_data(LinphoneFriend *lf, void *data){
+ lf->user_data=data;
+}
+
+void* linphone_friend_get_user_data(const LinphoneFriend *lf){
+ return lf->user_data;
+}
+
bool_t linphone_friend_in_list(const LinphoneFriend *lf){
return lf->lc!=NULL;
}
@@ -187,7 +178,7 @@ void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char
}else if (lc->default_proxy!=NULL){
/*try adding domain part from default current proxy*/
LinphoneAddress * id=linphone_address_new(linphone_core_get_identity(lc));
- if (id!=NULL){
+ if ((id!=NULL) && (uri[0] != '\0')){
linphone_address_set_display_name(id,NULL);
linphone_address_set_username(id,uri);
*result=linphone_address_as_string(id);
@@ -200,14 +191,16 @@ void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char
}else{
ms_warning("Fail to interpret friend uri %s",uri);
}
- }else *result=linphone_address_as_string(fr);
- linphone_address_destroy(fr);
+ }else {
+ *result=linphone_address_as_string(fr);
+ linphone_address_destroy(fr);
+ }
}
-int linphone_friend_set_addr(LinphoneFriend *lf, const LinphoneAddress *addr){
+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);
+ if (lf->uri!=NULL) linphone_address_destroy(lf->uri);
lf->uri=fr;
return 0;
}
@@ -233,55 +226,25 @@ int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscri
return 0;
}
-SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os){
- switch(os){
- case LinphoneStatusOffline:
- return SalPresenceOffline;
- break;
- case LinphoneStatusOnline:
- return SalPresenceOnline;
- break;
- case LinphoneStatusBusy:
- return SalPresenceBusy;
- break;
- case LinphoneStatusBeRightBack:
- return SalPresenceBerightback;
- break;
- case LinphoneStatusAway:
- return SalPresenceAway;
- break;
- case LinphoneStatusOnThePhone:
- return SalPresenceOnthephone;
- break;
- case LinphoneStatusOutToLunch:
- return SalPresenceOuttolunch;
- break;
- case LinphoneStatusDoNotDisturb:
- return SalPresenceDonotdisturb;
- break;
- case LinphoneStatusMoved:
- return SalPresenceMoved;
- break;
- case LinphoneStatusAltService:
- return SalPresenceAltService;
- break;
- case LinphoneStatusPending:
- return SalPresenceOffline;
- break;
- default:
- return SalPresenceOffline;
- break;
+void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){
+ MSList *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);
+ }
+ for(elem=lf->insubs; elem!=NULL; elem=elem->next){
+ SalOp *op = (SalOp*)elem->data;
+ sal_notify_presence(op,(SalPresenceModel *)presence);
}
- return SalPresenceOffline;
}
-void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os){
- char *addr=linphone_address_as_string(linphone_friend_get_address(lf));
- ms_message("Want to notify %s, insub=%p",addr,lf->insub);
- ms_free(addr);
- if (lf->insub!=NULL){
- sal_notify_presence(lf->insub,linphone_online_status_to_sal(os),NULL);
- }
+void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){
+ lf->insubs = ms_list_append(lf->insubs, op);
+}
+
+void linphone_friend_remove_incoming_subscription(LinphoneFriend *lf, SalOp *op){
+ lf->insubs = ms_list_remove(lf->insubs, op);
}
static void linphone_friend_unsubscribe(LinphoneFriend *lf){
@@ -291,32 +254,49 @@ static void linphone_friend_unsubscribe(LinphoneFriend *lf){
}
}
-void linphone_friend_close_subscriptions(LinphoneFriend *lf){
- linphone_friend_unsubscribe(lf);
- if (lf->insub){
- sal_notify_close(lf->insub);
-
+static void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
+ 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);
}
+ lf->initial_subscribes_sent=FALSE;
}
-void linphone_friend_destroy(LinphoneFriend *lf){
- if (lf->insub) {
- sal_op_release(lf->insub);
- lf->insub=NULL;
- }
+void linphone_friend_close_subscriptions(LinphoneFriend *lf){
+ linphone_friend_unsubscribe(lf);
+ ms_list_for_each(lf->insubs, (MSIterateFunc) sal_notify_presence_close);
+
+}
+
+static void _linphone_friend_destroy(LinphoneFriend *lf){
+ lf->insubs = ms_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);
- ms_free(lf);
}
const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf){
return lf->uri;
}
+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);
+}
+
bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){
return lf->subscribe;
}
@@ -326,43 +306,153 @@ LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneF
}
LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
- return lf->status;
+ LinphoneOnlineStatus online_status = LinphoneStatusOffline;
+ LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed;
+ LinphonePresenceActivity *activity = 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);
+ online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline;
+ if (nb_activities > 1) {
+ char *tmp = NULL;
+ const LinphoneAddress *addr = linphone_friend_get_address(lf);
+ if (addr) tmp = linphone_address_as_string(addr);
+ ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown");
+ if (tmp) ms_free(tmp);
+ nb_activities = 1;
+ }
+ if (nb_activities == 1) {
+ activity = linphone_presence_model_get_activity(lf->presence);
+ switch (linphone_presence_activity_get_type(activity)) {
+ case LinphonePresenceActivityBreakfast:
+ case LinphonePresenceActivityDinner:
+ case LinphonePresenceActivityLunch:
+ case LinphonePresenceActivityMeal:
+ online_status = LinphoneStatusOutToLunch;
+ break;
+ case LinphonePresenceActivityAppointment:
+ case LinphonePresenceActivityMeeting:
+ case LinphonePresenceActivityPerformance:
+ case LinphonePresenceActivityPresentation:
+ case LinphonePresenceActivitySpectator:
+ case LinphonePresenceActivityWorking:
+ case LinphonePresenceActivityWorship:
+ online_status = LinphoneStatusDoNotDisturb;
+ break;
+ case LinphonePresenceActivityAway:
+ case LinphonePresenceActivitySleeping:
+ online_status = LinphoneStatusAway;
+ break;
+ case LinphonePresenceActivityHoliday:
+ case LinphonePresenceActivityTravel:
+ case LinphonePresenceActivityVacation:
+ online_status = LinphoneStatusVacation;
+ break;
+ case LinphonePresenceActivityBusy:
+ case LinphonePresenceActivityLookingForWork:
+ case LinphonePresenceActivityPlaying:
+ case LinphonePresenceActivityShopping:
+ case LinphonePresenceActivityTV:
+ online_status = LinphoneStatusBusy;
+ break;
+ case LinphonePresenceActivityInTransit:
+ case LinphonePresenceActivitySteering:
+ online_status = LinphoneStatusBeRightBack;
+ break;
+ case LinphonePresenceActivityOnThePhone:
+ online_status = LinphoneStatusOnThePhone;
+ break;
+ case LinphonePresenceActivityOther:
+ case LinphonePresenceActivityPermanentAbsence:
+ online_status = LinphoneStatusMoved;
+ break;
+ case LinphonePresenceActivityUnknown:
+ /* Rely on the basic status information. */
+ break;
+ case LinphonePresenceActivityOnline:
+ /* Should not happen! */
+ ms_warning("LinphonePresenceActivityOnline should not happen here!");
+ break;
+ case LinphonePresenceActivityOffline:
+ online_status = LinphoneStatusOffline;
+ break;
+ }
+ }
+ }
+
+ return online_status;
+}
+
+const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend *lf) {
+ return lf->presence;
}
BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){
return lf->info;
}
+/*
+ * updates the 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){
+ 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;
+ }
+ }
+ if (can_subscribe && fr->subscribe && fr->subscribe_active==FALSE){
+ ms_message("Sending a new SUBSCRIBE");
+ __linphone_friend_do_subscribe(fr);
+ }else if (can_subscribe && fr->subscribe_active && !fr->subscribe){
+ linphone_friend_unsubscribe(fr);
+ }else if (!can_subscribe && fr->outsub){
+ fr->subscribe_active=FALSE;
+ sal_op_stop_refreshing(fr->outsub);
+ }
+}
+
void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){
+ LinphonePresenceModel *model;
+
if (fr->uri==NULL) {
ms_warning("No sip url defined.");
return;
}
- fr->lc=lc;
-
+
linphone_core_write_friends_config(lc);
if (fr->inc_subscribe_pending){
switch(fr->pol){
case LinphoneSPWait:
- linphone_friend_notify(fr,LinphoneStatusPending);
+ model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOther, "Waiting for user acceptance");
+ linphone_friend_notify(fr,model);
+ linphone_presence_model_unref(model);
break;
case LinphoneSPAccept:
if (fr->lc!=NULL)
- {
- linphone_friend_notify(fr,fr->lc->presence_mode);
- }
+ linphone_friend_notify(fr,fr->lc->presence_model);
break;
case LinphoneSPDeny:
- linphone_friend_notify(fr,LinphoneStatusOffline);
+ linphone_friend_notify(fr,NULL);
break;
}
fr->inc_subscribe_pending=FALSE;
}
- if (fr->subscribe && fr->subscribe_active==FALSE){
- ms_message("Sending a new SUBSCRIBE");
- __linphone_friend_do_subscribe(fr);
- }
+ 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.");
lc->bl_refresh=TRUE;
fr->commit=FALSE;
@@ -377,6 +467,14 @@ void linphone_friend_done(LinphoneFriend *fr){
linphone_friend_apply(fr,fr->lc);
}
+LinphoneFriend * linphone_core_create_friend(LinphoneCore *lc) {
+ return linphone_friend_new();
+}
+
+LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address) {
+ return linphone_friend_new_with_address(address);
+}
+
void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf)
{
ms_return_if_fail(lf->lc==NULL);
@@ -389,30 +487,51 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf)
if (tmp) ms_free(tmp);
return ;
}
- lc->friends=ms_list_append(lc->friends,lf);
+ 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,(void *)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_send_initial_subscribes(LinphoneCore *lc){
+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;
- if (f->commit)
- linphone_friend_apply(f,lc);
+ linphone_friend_update_subscribes(f,cfg,only_when_registered);
}
}
+bool_t linphone_core_should_subscribe_friends_only_when_registered(const LinphoneCore *lc){
+ return lp_config_get_int(lc->config,"sip","subscribe_presence_only_when_registered",1);
+}
+
+void linphone_core_send_initial_subscribes(LinphoneCore *lc){
+ if (lc->initial_subscribes_sent) return;
+ lc->initial_subscribes_sent=TRUE;
+ linphone_core_update_friends_subscriptions(lc,NULL,linphone_core_should_subscribe_friends_only_when_registered(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);
+ }
+ lc->initial_subscribes_sent=FALSE;
+}
+
void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key){
if (lf->refkey!=NULL){
ms_free(lf->refkey);
@@ -428,40 +547,22 @@ const char *linphone_friend_get_ref_key(const LinphoneFriend *lf){
return lf->refkey;
}
-static bool_t username_match(const char *u1, const char *u2){
- if (u1==NULL && u2==NULL) return TRUE;
- if (u1 && u2 && strcasecmp(u1,u2)==0) return TRUE;
- return FALSE;
+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;
+ }
+ return lf;
}
LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri){
LinphoneAddress *puri=linphone_address_new(uri);
- const MSList *elem;
- const char *username;
- const char *domain;
- const char *it_username;
- const char *it_host;
- LinphoneFriend *lf=NULL;
-
- if (puri==NULL){
- return NULL;
- }
- username=linphone_address_get_username(puri);
- domain=linphone_address_get_domain(puri);
- if (domain==NULL) {
- linphone_address_destroy(puri);
- return NULL;
- }
- for(elem=lc->friends;elem!=NULL;elem=ms_list_next(elem)){
- lf=(LinphoneFriend*)elem->data;
- it_username=linphone_address_get_username(lf->uri);
- it_host=linphone_address_get_domain(lf->uri);;
- if (strcasecmp(domain,it_host)==0 && username_match(username,it_username)){
- break;
- }
- lf=NULL;
- }
- linphone_address_destroy(puri);
+ LinphoneFriend *lf=puri ? linphone_core_find_friend(lc,puri) : NULL;
+ if (puri) linphone_address_unref(puri);
return lf;
}
@@ -504,18 +605,18 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde
int a;
LinphoneFriend *lf;
LpConfig *config=lc->config;
-
+
sprintf(item,"friend_%i",index);
-
+
if (!lp_config_has_section(config,item)){
return NULL;
}
-
+
tmp=lp_config_get_string(config,item,"url",NULL);
if (tmp==NULL) {
return NULL;
}
- lf=linphone_friend_new_with_addr(tmp);
+ lf=linphone_friend_new_with_address(tmp);
if (lf==NULL) {
return NULL;
}
@@ -526,7 +627,7 @@ 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);
-
+
linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL));
return lf;
}
@@ -551,9 +652,9 @@ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf,
char key[50];
char *tmp;
const char *refkey;
-
+
sprintf(key,"friend_%i",index);
-
+
if (lf==NULL){
lp_config_clean_section(config,key);
return;
@@ -586,3 +687,29 @@ void linphone_core_write_friends_config(LinphoneCore* lc)
linphone_friend_write_to_config_file(lc->config,NULL,i); /* set the end */
}
+LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr){
+ return fr->lc;
+}
+
+LinphoneFriend *linphone_friend_ref(LinphoneFriend *lf) {
+ belle_sip_object_ref(lf);
+ return lf;
+}
+
+void linphone_friend_unref(LinphoneFriend *lf) {
+ belle_sip_object_unref(lf);
+}
+
+/* DEPRECATED */
+void linphone_friend_destroy(LinphoneFriend *lf) {
+ linphone_friend_unref(lf);
+}
+
+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
+ FALSE
+);
\ No newline at end of file
diff --git a/coreapi/gitversion.cmake b/coreapi/gitversion.cmake
new file mode 100644
index 000000000..23fecb10c
--- /dev/null
+++ b/coreapi/gitversion.cmake
@@ -0,0 +1,39 @@
+############################################################################
+# gitversion.cmake
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+
+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
+ )
+endif()
diff --git a/coreapi/help/CMakeLists.txt b/coreapi/help/CMakeLists.txt
new file mode 100644
index 000000000..fa6915d8f
--- /dev/null
+++ b/coreapi/help/CMakeLists.txt
@@ -0,0 +1,45 @@
+############################################################################
+# CMakeLists.txt
+# Copyright (C) 2014 Belledonne Communications, Grenoble France
+#
+############################################################################
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+############################################################################
+
+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/.")
+ endif()
+endif()
diff --git a/coreapi/help/Doxyfile.in b/coreapi/help/Doxyfile.in
index 03dbf91a0..12facea9b 100644
--- a/coreapi/help/Doxyfile.in
+++ b/coreapi/help/Doxyfile.in
@@ -34,7 +34,7 @@ DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
DISTRIBUTE_GROUP_DOC = NO
TAB_SIZE = 8
-ALIASES =
+ALIASES = mslist{1}="A list of \ref \1 objects. \xmlonly \1 \endxmlonly"
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
SUBGROUPING = YES
@@ -165,7 +165,7 @@ MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
-GENERATE_XML = NO
+GENERATE_XML = YES
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
@@ -190,7 +190,7 @@ EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH = .
INCLUDE_FILE_PATTERNS = *.h
-PREDEFINED = DOXYGEN
+PREDEFINED = DOXYGEN MS2_PUBLIC= LINPHONE_PUBLIC=
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am
index 902d23c9c..479d2b31d 100644
--- a/coreapi/help/Makefile.am
+++ b/coreapi/help/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_DIST=Doxyfile.in doxygen.dox
-SOURCES=doxygen.dox $(top_srcdir)/coreapi/help/*.c $(top_srcdir)/coreapi/*.c $(top_srcdir)/coreapi/*.h
+SOURCES=doxygen.dox $(top_srcdir)/coreapi/help/*.c $(top_srcdir)/coreapi/*.c $(top_srcdir)/coreapi/*.h
# html doc
@@ -10,6 +10,7 @@ if HAVE_DOXYGEN
# docdir & pkgdocdir are not always defined by automake
pkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION)
doc_htmldir=$(pkgdocdir)/html
+doc_xmldir=$(pkgdocdir)/xml
doc_html_DATA = $(top_builddir)/coreapi/help/doc/html/html.tar
@@ -20,28 +21,40 @@ $(top_builddir)/coreapi/help/doc/html/index.html: $(SOURCES) Doxyfile Makefile.a
rm -rf doc
$(DOXYGEN) Doxyfile
+doc_xml_DATA = $(top_builddir)/coreapi/help/doc/xml/xml.tar
+
+$(doc_xml_DATA): $(top_builddir)/coreapi/help/doc/xml/index.xml
+ cd $(top_builddir)/coreapi/help/doc/xml/ && tar cf xml.tar *
+
+$(top_builddir)/coreapi/help/doc/xml/index.xml: $(top_builddir)/coreapi/help/doc/html/index.html
+
+
install-data-hook:
cd $(DESTDIR)$(doc_htmldir) && tar xf html.tar && rm -f html.tar
+ cd $(DESTDIR)$(doc_xmldir) && tar xf xml.tar && rm -f xml.tar
uninstall-hook:
cd $(DESTDIR)$(doc_htmldir) && rm -f *
+ cd $(DESTDIR)$(doc_xmldir) && rm -f *
endif
clean-local:
rm -rf doc
-if ENABLE_TESTS
#tutorials
-if BUILD_TESTS
-noinst_PROGRAMS=helloworld registration buddy_status chatroom
+
+if ENABLE_TUTORIALS
+
+noinst_PROGRAMS=helloworld registration buddy_status chatroom notify filetransfer
helloworld_SOURCES=helloworld.c
LINPHONE_TUTOS=$(helloworld_SOURCES)
helloworld_LDADD=$(top_builddir)/coreapi/liblinphone.la \
$(MEDIASTREAMER_LIBS) \
- $(ORTP_LIBS)
+ $(ORTP_LIBS) \
+ $(BELLESIP_LIBS)
registration_SOURCES=registration.c
LINPHONE_TUTOS+=$(registration_SOURCES)
@@ -57,24 +70,33 @@ chatroom_SOURCES=chatroom.c
LINPHONE_TUTOS+=$(chatroom_SOURCES)
chatroom_LDADD=$(helloworld_LDADD)
-endif
-
-endif
+
+notify_SOURCES=notify.c
+LINPHONE_TUTOS+=$(notify_SOURCES)
+
+notify_LDADD=$(helloworld_LDADD)
+
+filetransfer_SOURCES=filetransfer.c
+LINPHONE_TUTOS+=$(filetransfer_SOURCES)
+
+filetransfer_LDADD=$(helloworld_LDADD)
AM_CFLAGS=\
+ -I$(top_srcdir)/coreapi \
$(STRICT_OPTIONS) \
+ $(STRICT_OPTIONS_CC) \
-DIN_LINPHONE \
$(ORTP_CFLAGS) \
- $(OSIP_CFLAGS) \
$(MEDIASTREAMER_CFLAGS) \
- $(EXOSIP_CFLAGS) \
-DENABLE_TRACE \
-DLOG_DOMAIN=\"LinphoneCore\" \
$(IPV6_CFLAGS) \
-DORTP_INET6 \
- $(VIDEO_CFLAGS)
+ $(VIDEO_CFLAGS) \
+ $(BELLESIP_CFLAGS)
tutodir=$(datadir)/tutorials/linphone
tuto_DATA=$(LINPHONE_TUTOS)
+endif
diff --git a/coreapi/help/buddy_status.c b/coreapi/help/buddy_status.c
index 6fdb47f40..cd7f13d52 100644
--- a/coreapi/help/buddy_status.c
+++ b/coreapi/help/buddy_status.c
@@ -1,7 +1,7 @@
/*
buddy_status
-Copyright (C) 2010 Belledonne Communications SARL
+Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -51,12 +51,15 @@ static void stop(int signum){
* presence state change notification callback
*/
static void notify_presence_recv_updated (LinphoneCore *lc, LinphoneFriend *friend) {
+ const LinphonePresenceModel* model = linphone_friend_get_presence_model(friend);
const LinphoneAddress* friend_address = linphone_friend_get_address(friend);
+ LinphonePresenceActivity *activity = linphone_presence_model_get_activity(model);
+ char *activity_str = linphone_presence_activity_to_string(activity);
printf("New state state [%s] for user id [%s] \n"
- ,linphone_online_status_to_string(linphone_friend_get_status(friend))
+ ,activity_str
,linphone_address_as_string (friend_address));
}
-static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *friend, const char* url) {
+static void new_subscription_requested (LinphoneCore *lc, LinphoneFriend *friend, const char* url) {
const LinphoneAddress* friend_address = linphone_friend_get_address(friend);
printf(" [%s] wants to see your status, accepting\n"
,linphone_address_as_string (friend_address));
@@ -70,7 +73,7 @@ static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *friend,
* Registration state notification callback
*/
static void registration_state_changed(struct _LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message){
- printf("New registration state %s for user id [%s] at proxy [%s]\n"
+ printf("New registration state %s for user id [%s] at proxy [%s]"
,linphone_registration_state_to_string(cstate)
,linphone_proxy_config_get_identity(cfg)
,linphone_proxy_config_get_addr(cfg));
@@ -84,6 +87,8 @@ int main(int argc, char *argv[]){
char* identity=NULL;
char* password=NULL;
+ LinphoneFriend* my_friend=NULL;
+
/* takes sip uri identity from the command line arguments */
if (argc>1){
dest_friend=argv[1];
@@ -101,13 +106,13 @@ int main(int argc, char *argv[]){
#ifdef DEBUG
linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/
#endif
- /*
+ /*
Fill the LinphoneCoreVTable with application callbacks.
- All are optional. Here we only use the both notify_presence_recv and new_subscription_request callbacks
+ All are optional. Here we only use the both notify_presence_received and new_subscription_requested callbacks
in order to get notifications about friend status.
*/
- vtable.notify_presence_recv=notify_presence_recv_updated;
- vtable.new_subscription_request=new_subscription_request;
+ vtable.notify_presence_received=notify_presence_recv_updated;
+ vtable.new_subscription_requested=new_subscription_requested;
vtable.registration_state_changed=registration_state_changed; /*just in case sip proxy is used*/
/*
@@ -120,13 +125,13 @@ int main(int argc, char *argv[]){
LinphoneProxyConfig* proxy_cfg = linphone_proxy_config_new();
/*parse identity*/
LinphoneAddress *from = linphone_address_new(identity);
+ LinphoneAuthInfo *info;
if (from==NULL){
printf("%s not a valid sip uri, must be like sip:toto@sip.linphone.org \n",identity);
goto end;
}
- LinphoneAuthInfo *info;
if (password!=NULL){
- info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL); /*create authentication structure from identity*/
+ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL,NULL); /*create authentication structure from identity*/
linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/
}
@@ -149,10 +154,9 @@ int main(int argc, char *argv[]){
while( running && linphone_proxy_config_get_state(proxy_cfg) == LinphoneRegistrationProgress);
}
- LinphoneFriend* my_friend=NULL;
if (dest_friend) {
- my_friend = linphone_friend_new_with_addr(dest_friend); /*creates friend object from dest*/
+ my_friend = linphone_friend_new_with_address(dest_friend); /*creates friend object from dest*/
if (my_friend == NULL) {
printf("bad destination uri for friend [%s]\n",dest_friend);
goto end;
@@ -164,7 +168,8 @@ int main(int argc, char *argv[]){
}
- linphone_core_set_presence_info(lc,0,NULL,LinphoneStatusOnline); /*set my status to online*/
+ /*set my status to online*/
+ linphone_core_set_presence_model(lc, linphone_presence_model_new_with_activity(LinphonePresenceActivityOnline, NULL));
/* main loop for receiving notifications and doing background linphone core work: */
while(running){
@@ -172,7 +177,8 @@ int main(int argc, char *argv[]){
ms_usleep(50000);
}
- linphone_core_set_presence_info(lc,0,NULL,LinphoneStatusOffline); /* change my presence status to offline*/
+ /* change my presence status to offline*/
+ linphone_core_set_presence_model(lc, linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline, NULL));
linphone_core_iterate(lc); /* just to make sure new status is initiate message is issued */
linphone_friend_edit(my_friend); /* start editing friend */
diff --git a/coreapi/help/chatroom.c b/coreapi/help/chatroom.c
index 51fc4a237..62d75bab9 100644
--- a/coreapi/help/chatroom.c
+++ b/coreapi/help/chatroom.c
@@ -1,7 +1,7 @@
/*
linphone
-Copyright (C) 2010 Belledonne Communications SARL
+Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -55,7 +55,7 @@ int main(int argc, char *argv[]){
LinphoneCoreVTable vtable={0};
char* dest_friend=NULL;
-
+ LinphoneChatRoom* chat_room;
/* takes sip uri identity from the command line arguments */
if (argc>1){
@@ -67,7 +67,7 @@ int main(int argc, char *argv[]){
#ifdef DEBUG
linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/
#endif
- /*
+ /*
Fill the LinphoneCoreVTable with application callbacks.
All are optional. Here we only use the text_received callback
in order to get notifications about incoming message.
@@ -81,7 +81,7 @@ int main(int argc, char *argv[]){
/*Next step is to create a chat root*/
- LinphoneChatRoom* chat_room = linphone_core_create_chat_room(lc,dest_friend);
+ chat_room = linphone_core_get_chat_room_from_uri(lc,dest_friend);
linphone_chat_room_send_message(chat_room,"Hello world"); /*sending message*/
@@ -92,7 +92,6 @@ int main(int argc, char *argv[]){
}
printf("Shutting down...\n");
- linphone_chat_room_destroy(chat_room);
linphone_core_destroy(lc);
printf("Exited\n");
return 0;
diff --git a/coreapi/help/doxygen.dox b/coreapi/help/doxygen.dox
index 8b2e6b7b9..a8baa5138 100644
--- a/coreapi/help/doxygen.dox
+++ b/coreapi/help/doxygen.dox
@@ -53,6 +53,11 @@
/**
* @defgroup media_parameters Controlling media parameters
+ * Multicast
+ * Call using rtp multicast addresses are supported for both audio and video with some limitations. Limitations are, no stun, no ice, no encryption.
+ *
Incoming call with multicast address are automatically accepted. The called party switches in a media receive only mode.
+ *
Outgoing call willing to send media to a multicast address can activate multicast using \link linphone_core_enable_video_multicast\endlink or
+ *\link linphone_core_enable_audio_multicast\endlink . The calling party switches in a media listen send only mode.
**/
/**
@@ -61,7 +66,7 @@
*like \link linphone_proxy_config_set_server_addr() proxy address \endlink , \link linphone_proxy_config_set_identity() user id \endlink, \link linphone_proxy_config_expires() refresh period \endlink, and so on.
* A created proxy config using linphone_proxy_config_new(), once configured, must be added to #LinphoneCore using function linphone_core_add_proxy_config().
* It is recommended to set a default \link #LinphoneProxyConfig proxy config \endlink using function linphone_core_set_default_proxy(). Once done, if \link #LinphoneProxyConfig a proxy config \endlink has been configured with attribute \link linphone_proxy_config_enable_register() enable register \endlink , next call to linphone_core_iterate() triggers a SIP register.
- * Registration status is reported by #LinphoneRegistrationStateCb.
+ * Registration status is reported by LinphoneCoreRegistrationStateChangedCb.
*
* This pseudo code demonstrates basic registration operations:
* \code
@@ -96,7 +101,7 @@
}
\endcode
* Authentication:
- * Most of the time, registration requires \ref authentication "authentication" to succed. #LinphoneAuthInfo info must be either added to #LinphoneCore using function linphone_core_add_auth_info() before #LinphoneProxyConfig is added to Linphone core, or on demand from call back #AuthInfoRequested .
+ * Most of the time, registration requires \ref authentication "authentication" to succeed. #LinphoneAuthInfo info must be either added to #LinphoneCore using function linphone_core_add_auth_info() before #LinphoneProxyConfig is added to Linphone core, or on demand from call back #LinphoneCoreAuthInfoRequestedCb.
*
* Unregistration:
* Unregistration or any changes to #LinphoneProxyConfig must be first started by a call to function linphone_proxy_config_edit() and validated by function linphone_proxy_config_done()
@@ -152,7 +157,7 @@ linphone_friend_done(my_friend); /*commit changes triggering an UNSUBSCRIBE mess
Publishing presence status
- Local presence status can be changed using function linphone_core_set_presence_info() .New status is propagated to all friends \link linphone_core_add_friend() previously added \endlink to #LinphoneCore.
+ Local presence status can be changed using function linphone_core_set_presence_model() .New status is propagated to all friends \link linphone_core_add_friend() previously added \endlink to #LinphoneCore.
Handling incoming subscription request New incoming subscription requests are process according to \link linphone_friend_set_inc_subscribe_policy() the incoming subscription policy state \endlink for subscription initiated by \link linphone_core_add_friend() members of the buddy list. \endlink
@@ -203,7 +208,7 @@ void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddre
* While initiating the second call, the first one is automatically paused.
* Then, once the second call is established, the application has the possibility to merge the two calls to form a conference where each participant
* (the local participant, the remote destination of the first call, the remote destination of the second call) can talk together.
- * This must be done by adding the two calls to the conference using \link linphone_call_add_to_conference() \endlink
+ * This must be done by adding the two calls to the conference using \link linphone_core_add_to_conference() \endlink
*
* Once merged into a conference the LinphoneCall objects representing the calls that were established remain unchanged, except that
* they are tagged as part of the conference (see \link linphone_call_is_in_conference() \endlink ). The calls in a conference are in the LinphoneCallStreamsRunning state.
@@ -216,6 +221,12 @@ void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddre
*
**/
+
+/**
+ * @defgroup event_api Managing generic subscriptions and publishes
+ * The LinphoneEvent api allows application to control subscriptions, receive notifications and make publish to peers, in a generic manner.
+ */
+
/**
* @defgroup misc Miscenalleous: logs, version strings, config storage
**/
@@ -270,7 +281,7 @@ and register a keep-alive handler for periodically refreshing the registration.
}];
\endcode
Incoming call notification while in background mode
- Assuming application using liblinphone is well configured for multitasking, incoming calls arriving while liblinphone is in background mode will simply wakeup liblinphone thread but not resume GUI. To wakeup GUI, it is recommended to send a Local Notification to the user from the #LinphoneCallStateCb. Here under a speudo code for this operation:
+ Assuming application using liblinphone is well configured for multitasking, incoming calls arriving while liblinphone is in background mode will simply wakeup liblinphone thread but not resume GUI. To wakeup GUI, it is recommended to send a Local Notification to the user from the #LinphoneCoreCallStateChangedCb. Here under a speudo code for this operation:
\code
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
// Create a new notification
diff --git a/coreapi/help/filetransfer.c b/coreapi/help/filetransfer.c
new file mode 100644
index 000000000..21cb19115
--- /dev/null
+++ b/coreapi/help/filetransfer.c
@@ -0,0 +1,203 @@
+
+/*
+linphone
+Copyright (C) 2010 Belledonne Communications SARL
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+/**
+ * @defgroup chatroom_tuto Chat room and messaging
+ * @ingroup tutorials
+ *This program is a _very_ simple usage example of liblinphone,
+ *desmonstrating how to send/receive SIP MESSAGE from a sip uri identity passed from the command line.
+ * Argument must be like sip:jehan@sip.linphone.org .
+ *
+ *ex chatroom sip:jehan@sip.linphone.org
+ *
+ *@include chatroom.c
+
+ *
+ */
+
+#ifdef IN_LINPHONE
+#include "linphonecore.h"
+#else
+#include "linphone/linphonecore.h"
+#endif
+
+#include
+
+static bool_t running=TRUE;
+
+static void stop(int signum){
+ running=FALSE;
+}
+/**
+ * function invoked to report file transfer progress.
+ * */
+static void file_transfer_progress_indication(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) {
+ const LinphoneAddress* from_address = linphone_chat_message_get_from(message);
+ const LinphoneAddress* to_address = linphone_chat_message_get_to(message);
+ char *address = linphone_chat_message_is_outgoing(message)?linphone_address_as_string(to_address):linphone_address_as_string(from_address);
+ printf(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", (int)((offset *100)/total)
+ ,(linphone_chat_message_is_outgoing(message)?"sent":"received")
+ , linphone_content_get_type(content)
+ , linphone_content_get_subtype(content)
+ ,(linphone_chat_message_is_outgoing(message)?"to":"from")
+ , address);
+ free(address);
+}
+/**
+ * function invoked when a file transfer is received.
+ **/
+static void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const LinphoneBuffer *buffer){
+ FILE* file=NULL;
+ if (!linphone_chat_message_get_user_data(message)) {
+ /*first chunk, creating file*/
+ file = fopen("receive_file.dump","wb");
+ linphone_chat_message_set_user_data(message,(void*)file); /*store fd for next chunks*/
+ }
+
+ file = (FILE*)linphone_chat_message_get_user_data(message);
+ if (linphone_buffer_is_empty(buffer)) {
+ printf("File transfert completed\n");
+ linphone_chat_room_destroy(linphone_chat_message_get_chat_room(message));
+ linphone_chat_message_destroy(message);
+ fclose(file);
+ running=FALSE;
+ } else { /* store content on a file*/
+ if (fwrite(linphone_buffer_get_content(buffer),linphone_buffer_get_size(buffer),1,file)==-1){
+ ms_warning("file_transfer_received() write failed: %s",strerror(errno));
+ }
+ }
+}
+
+char big_file [128000];
+
+/*
+ * function called when the file transfer is initiated. file content should be feed into object LinphoneContent
+ * */
+static LinphoneBuffer * file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size){
+ size_t size_to_send = MIN(size, sizeof(big_file) - offset);
+ if (size == 0) return linphone_buffer_new(); /*end of file*/
+ return linphone_buffer_new_from_data((uint8_t *)big_file + offset, size_to_send);
+}
+
+/*
+ * Call back to get delivery status of a message
+ * */
+static void linphone_file_transfer_state_changed(LinphoneChatMessage* msg,LinphoneChatMessageState state,void* ud) {
+ const LinphoneAddress* to_address = linphone_chat_message_get_to(msg);
+ char *to = linphone_address_as_string(to_address);
+ printf("File transfer sent to [%s] delivery status is [%s] \n" , to
+ , linphone_chat_message_state_to_string(state));
+ free(to);
+}
+
+/*
+ * Call back called when a message is received
+ */
+static void message_received(LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+ const LinphoneContent *file_transfer_info = linphone_chat_message_get_file_transfer_information(msg);
+ printf ("Do you really want to download %s (size %ld)?[Y/n]\nOk, let's go\n", linphone_content_get_name(file_transfer_info), (long int)linphone_content_get_size(file_transfer_info));
+
+ linphone_chat_message_start_file_download(msg, linphone_file_transfer_state_changed, NULL);
+
+}
+
+LinphoneCore *lc;
+int main(int argc, char *argv[]){
+ LinphoneCoreVTable vtable={0};
+
+ const char* dest_friend=NULL;
+ int i;
+ const char* big_file_content="big file";
+ LinphoneChatRoom* chat_room;
+ LinphoneContent* content;
+ LinphoneChatMessage* chat_message;
+ LinphoneChatMessageCbs *cbs;
+
+ /*seting dummy file content to something*/
+ for (i=0;i
+ *ex registration sip:jehan@sip.linphone.org secret
+ * Registration is cleared on SIGINT
+ *
+ *@include registration.c
+
+ *
+ */
+
+#define DEBUG 1
+
+#ifdef IN_LINPHONE
+#include "linphonecore.h"
+#else
+#include "linphone/linphonecore.h"
+#endif
+
+#include
+
+static bool_t running=TRUE;
+
+static void stop(int signum){
+ running=FALSE;
+}
+
+typedef struct MyAppData{
+ LinphoneEvent *ev;
+}MyAppData;
+
+/**
+ * Registration state notification callback
+ */
+static void registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message){
+ printf("New registration state %s for user id [%s] at proxy [%s]\n"
+ ,linphone_registration_state_to_string(cstate)
+ ,linphone_proxy_config_get_identity(cfg)
+ ,linphone_proxy_config_get_addr(cfg));
+}
+
+static void subscription_state_changed(LinphoneCore *lc, LinphoneEvent *ev, LinphoneSubscriptionState state){
+ MyAppData *data=(MyAppData*)linphone_core_get_user_data(lc);
+ if (state==LinphoneSubscriptionIncomingReceived){
+ printf("Receiving new subscription for event %s\n",linphone_event_get_name(ev));
+ if (data->ev==NULL) {
+ linphone_event_accept_subscription(ev);
+ data->ev=linphone_event_ref(ev);
+ }else{
+ linphone_event_deny_subscription(ev,LinphoneReasonBusy);
+ }
+ }else if (state==LinphoneSubscriptionTerminated){
+ if (data->ev==ev){
+ linphone_event_unref(data->ev);
+ data->ev=NULL;
+ }
+ }
+}
+
+LinphoneCore *lc;
+int main(int argc, char *argv[]){
+ LinphoneCoreVTable vtable={0};
+ MyAppData *data=ms_new0(MyAppData,1);
+ char* identity=NULL;
+ char* password=NULL;
+ int i;
+ LinphoneProxyConfig* proxy_cfg;
+ LinphoneAddress *from;
+ LinphoneAuthInfo *info;
+ const char* server_addr;
+
+ /* takes sip uri identity from the command line arguments */
+ if (argc>1){
+ identity=argv[1];
+ }
+
+ /* takes password from the command line arguments */
+ if (argc>2){
+ password=argv[2];
+ }
+
+ signal(SIGINT,stop);
+
+#ifdef DEBUG
+ linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/
+#endif
+ /*
+ Fill the LinphoneCoreVTable with application callbacks.
+ All are optional. Here we only use the registration_state_changed callbacks
+ in order to get notifications about the progress of the registration.
+ */
+ vtable.registration_state_changed=registration_state_changed;
+ vtable.subscription_state_changed=subscription_state_changed;
+
+ /*
+ Instanciate a LinphoneCore object given the LinphoneCoreVTable
+ */
+ lc=linphone_core_new(&vtable,NULL,NULL,data);
+
+ /*create proxy config*/
+ proxy_cfg = linphone_proxy_config_new();
+ /*parse identity*/
+ from = linphone_address_new(identity);
+ if (from==NULL){
+ printf("%s not a valid sip uri, must be like sip:toto@sip.linphone.org \n",identity);
+ goto end;
+ }
+ if (password!=NULL){
+ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL,NULL); /*create authentication structure from identity*/
+ linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/
+ }
+
+ // configure proxy entries
+ linphone_proxy_config_set_identity(proxy_cfg,identity); /*set identity with user name and domain*/
+ server_addr = linphone_address_get_domain(from); /*extract domain address from identity*/
+ linphone_proxy_config_set_server_addr(proxy_cfg,server_addr); /* we assume domain = proxy server address*/
+ linphone_proxy_config_enable_register(proxy_cfg,TRUE); /*activate registration for this proxy config*/
+ linphone_address_destroy(from); /*release resource*/
+
+ linphone_core_add_proxy_config(lc,proxy_cfg); /*add proxy config to linphone core*/
+ linphone_core_set_default_proxy(lc,proxy_cfg); /*set to default proxy*/
+
+ i=0;
+ /* main loop for receiving notifications and doing background linphonecore work: */
+ while(running){
+ linphone_core_iterate(lc); /* first iterate initiates registration */
+ ms_usleep(50000);
+ ++i;
+ if (data->ev && i%100==0){
+ LinphoneContentPrivate content;
+ content.type="application";
+ content.subtype="goodxml";
+ content.data="really cool";
+ content.size=strlen((const char*)content.data);
+ linphone_event_notify(data->ev,LINPHONE_CONTENT(&content));
+ }
+ }
+
+ linphone_core_get_default_proxy(lc,&proxy_cfg); /* get default proxy config*/
+ linphone_proxy_config_edit(proxy_cfg); /*start editing proxy configuration*/
+ linphone_proxy_config_enable_register(proxy_cfg,FALSE); /*de-activate registration for this proxy config*/
+ linphone_proxy_config_done(proxy_cfg); /*initiate REGISTER with expire = 0*/
+
+ if (data->ev){
+ linphone_event_terminate(data->ev);
+ }
+
+ while(linphone_proxy_config_get_state(proxy_cfg) != LinphoneRegistrationCleared){
+ linphone_core_iterate(lc); /*to make sure we receive call backs before shutting down*/
+ ms_usleep(50000);
+ }
+
+end:
+ printf("Shutting down...\n");
+ linphone_core_destroy(lc);
+ ms_free(data);
+ printf("Exited\n");
+ return 0;
+}
+
diff --git a/coreapi/help/registration.c b/coreapi/help/registration.c
index 6ed93e70d..8dbab6440 100644
--- a/coreapi/help/registration.c
+++ b/coreapi/help/registration.c
@@ -1,7 +1,7 @@
/*
linphone
-Copyright (C) 2010 Belledonne Communications SARL
+Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -60,9 +60,13 @@ static void registration_state_changed(struct _LinphoneCore *lc, LinphoneProxyCo
LinphoneCore *lc;
int main(int argc, char *argv[]){
LinphoneCoreVTable vtable={0};
+ LinphoneProxyConfig* proxy_cfg;
+ LinphoneAddress *from;
+ LinphoneAuthInfo *info;
char* identity=NULL;
char* password=NULL;
+ const char* server_addr;
/* takes sip uri identity from the command line arguments */
if (argc>1){
@@ -79,7 +83,7 @@ int main(int argc, char *argv[]){
#ifdef DEBUG
linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/
#endif
- /*
+ /*
Fill the LinphoneCoreVTable with application callbacks.
All are optional. Here we only use the registration_state_changed callbacks
in order to get notifications about the progress of the registration.
@@ -91,30 +95,28 @@ int main(int argc, char *argv[]){
*/
lc=linphone_core_new(&vtable,NULL,NULL,NULL);
- LinphoneProxyConfig* proxy_cfg;
/*create proxy config*/
proxy_cfg = linphone_proxy_config_new();
/*parse identity*/
- LinphoneAddress *from = linphone_address_new(identity);
+ from = linphone_address_new(identity);
if (from==NULL){
printf("%s not a valid sip uri, must be like sip:toto@sip.linphone.org \n",identity);
goto end;
}
- LinphoneAuthInfo *info;
- if (password!=NULL){
- info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL); /*create authentication structure from identity*/
- linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/
- }
+ if (password!=NULL){
+ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL,NULL); /*create authentication structure from identity*/
+ linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/
+ }
- // configure proxy entries
- linphone_proxy_config_set_identity(proxy_cfg,identity); /*set identity with user name and domain*/
- const char* server_addr = linphone_address_get_domain(from); /*extract domain address from identity*/
- linphone_proxy_config_set_server_addr(proxy_cfg,server_addr); /* we assume domain = proxy server address*/
- linphone_proxy_config_enable_register(proxy_cfg,TRUE); /*activate registration for this proxy config*/
- linphone_address_destroy(from); /*release resource*/
+ // configure proxy entries
+ linphone_proxy_config_set_identity(proxy_cfg,identity); /*set identity with user name and domain*/
+ server_addr = linphone_address_get_domain(from); /*extract domain address from identity*/
+ linphone_proxy_config_set_server_addr(proxy_cfg,server_addr); /* we assume domain = proxy server address*/
+ linphone_proxy_config_enable_register(proxy_cfg,TRUE); /*activate registration for this proxy config*/
+ linphone_address_destroy(from); /*release resource*/
- linphone_core_add_proxy_config(lc,proxy_cfg); /*add proxy config to linphone core*/
- linphone_core_set_default_proxy(lc,proxy_cfg); /*set to default proxy*/
+ linphone_core_add_proxy_config(lc,proxy_cfg); /*add proxy config to linphone core*/
+ linphone_core_set_default_proxy(lc,proxy_cfg); /*set to default proxy*/
/* main loop for receiving notifications and doing background linphonecore work: */
diff --git a/coreapi/info.c b/coreapi/info.c
new file mode 100644
index 000000000..f33a16997
--- /dev/null
+++ b/coreapi/info.c
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * info.c
+ *
+ * Thu May 16 11:48:01 2013
+ * Copyright 2013 Belledonne Communications SARL
+ * 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 "linphonecore.h"
+#include "private.h"
+#include "lpconfig.h"
+
+
+struct _LinphoneInfoMessage{
+ LinphoneContent *content;
+ SalCustomHeader *headers;
+};
+
+
+/**
+ * Destroy a LinphoneInfoMessage
+**/
+void linphone_info_message_destroy(LinphoneInfoMessage *im){
+ if (im->content) linphone_content_unref(im->content);
+ if (im->headers) sal_custom_header_free(im->headers);
+ ms_free(im);
+}
+
+
+LinphoneInfoMessage *linphone_info_message_copy(const LinphoneInfoMessage *orig){
+ LinphoneInfoMessage *im=ms_new0(LinphoneInfoMessage,1);
+ if (orig->content) im->content=linphone_content_copy(orig->content);
+ if (orig->headers) im->headers=sal_custom_header_clone(orig->headers);
+ return im;
+}
+
+/**
+ * Creates an empty info message.
+ * @param lc the LinphoneCore
+ * @return a new LinphoneInfoMessage.
+ *
+ * The info message can later be filled with information using linphone_info_message_add_header() or linphone_info_message_set_content(),
+ * and finally sent with linphone_core_send_info_message().
+**/
+LinphoneInfoMessage *linphone_core_create_info_message(LinphoneCore *lc){
+ LinphoneInfoMessage *im=ms_new0(LinphoneInfoMessage,1);
+ return im;
+}
+
+/**
+ * Send a LinphoneInfoMessage through an established call
+ * @param call the call
+ * @param info the info message
+**/
+int linphone_call_send_info_message(LinphoneCall *call, const LinphoneInfoMessage *info){
+ SalBody body;
+ sal_op_set_sent_custom_header(call->op,info->headers);
+ return sal_send_info(call->op,NULL, NULL, sal_body_from_content(&body,info->content));
+}
+
+/**
+ * Add a header to an info message to be sent.
+ * @param im the info message
+ * @param name the header'name
+ * @param value the header's value
+**/
+void linphone_info_message_add_header(LinphoneInfoMessage *im, const char *name, const char *value){
+ im->headers=sal_custom_header_append(im->headers, name, value);
+}
+
+/**
+ * Obtain a header value from a received info message.
+ * @param im the info message
+ * @param name the header'name
+ * @return the corresponding header's value, or NULL if not exists.
+**/
+const char *linphone_info_message_get_header(const LinphoneInfoMessage *im, const char *name){
+ return sal_custom_header_find(im->headers,name);
+}
+
+/**
+ * Assign a content to the info message.
+ * @param im the linphone info message
+ * @param content the content described as a #LinphoneContent structure.
+ * All fields of the LinphoneContent are copied, thus the application can destroy/modify/recycloe the content object freely ater the function returns.
+**/
+void linphone_info_message_set_content(LinphoneInfoMessage *im, const LinphoneContent *content){
+ im->content=linphone_content_copy(content);
+}
+
+/**
+ * Returns the info message's content as a #LinphoneContent structure.
+**/
+const LinphoneContent * linphone_info_message_get_content(const LinphoneInfoMessage *im){
+ return (im->content && linphone_content_get_type(im->content)) ? im->content : NULL;
+}
+
+void linphone_core_notify_info_message(LinphoneCore* lc,SalOp *op, const SalBody *body){
+ LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+ if (call){
+ LinphoneInfoMessage *info=ms_new0(LinphoneInfoMessage,1);
+ info->headers=sal_custom_header_clone(sal_op_get_recv_custom_header(op));
+ if (body) info->content=linphone_content_from_sal_body(body);
+ linphone_core_notify_info_received(lc,call,info);
+ linphone_info_message_destroy(info);
+ }
+}
diff --git a/coreapi/ldap/ldapprovider.c b/coreapi/ldap/ldapprovider.c
new file mode 100644
index 000000000..75b6b19a1
--- /dev/null
+++ b/coreapi/ldap/ldapprovider.c
@@ -0,0 +1,830 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "ldapprovider.h"
+#include "private.h"
+#include "lpconfig.h"
+#include "contact_providers_priv.h"
+#include "mediastreamer2/mscommon.h"
+#include
+
+#ifdef BUILD_LDAP
+#include
+#include
+
+
+#define MAX_RUNNING_REQUESTS 10
+#define FILTER_MAX_SIZE 512
+
+struct LDAPFriendData {
+ char* name;
+ char* sip;
+};
+
+struct _LinphoneLDAPContactProvider
+{
+ LinphoneContactProvider base;
+ LinphoneDictionary* config;
+
+ LDAP* ld;
+ MSList* requests;
+ unsigned int req_count;
+
+ // bind transaction
+ bool_t connected;
+ ms_thread_t bind_thread;
+
+ // config
+ int use_tls;
+ const char* auth_method;
+ const char* username;
+ const char* password;
+ const char* server;
+ const char* bind_dn;
+
+ const char* sasl_authname;
+ const char* sasl_realm;
+
+ const char* base_object;
+ const char* sip_attr;
+ const char* name_attr;
+ const char* filter;
+
+ char** attributes;
+
+ int timeout;
+ int deref_aliases;
+ int max_results;
+
+};
+
+struct _LinphoneLDAPContactSearch
+{
+ LinphoneContactSearch base;
+ LDAP* ld;
+ int msgid;
+ char* filter;
+ bool_t complete;
+ MSList* found_entries;
+ unsigned int found_count;
+};
+
+
+/* *************************
+ * LinphoneLDAPContactSearch
+ * *************************/
+
+LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* cp, const char* predicate, ContactSearchCallback cb, void* cb_data)
+{
+ LinphoneLDAPContactSearch* search = belle_sip_object_new(LinphoneLDAPContactSearch);
+ LinphoneContactSearch* base = LINPHONE_CONTACT_SEARCH(search);
+
+ linphone_contact_search_init(base, predicate, cb, cb_data);
+
+ search->ld = cp->ld;
+
+ search->filter = ms_malloc(FILTER_MAX_SIZE);
+ snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
+ search->filter[FILTER_MAX_SIZE-1] = 0;
+
+ return search;
+}
+
+void linphone_ldap_contact_search_destroy_friend( void* entry )
+{
+ linphone_friend_destroy((LinphoneFriend*)entry);
+}
+
+unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj)
+{
+ return obj->found_count;
+}
+
+static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
+{
+ //ms_message("~LinphoneLDAPContactSearch(%p)", obj);
+ ms_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend);
+ obj->found_entries = ms_list_free(obj->found_entries);
+ if( obj->filter ) ms_free(obj->filter);
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
+ (belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
+ NULL,
+ NULL,
+ TRUE
+);
+
+
+/* ***************************
+ * LinphoneLDAPContactProvider
+ * ***************************/
+
+static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid );
+static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req);
+static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj );
+static bool_t linphone_ldap_contact_provider_iterate(void *data);
+static int linphone_ldap_contact_provider_bind_interact(LDAP *ld, unsigned flags, void *defaults, void *sasl_interact);
+static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req);
+
+static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
+{
+ belle_sip_object_unref(req);
+}
+
+static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
+{
+ //ms_message("linphone_ldap_contact_provider_destroy");
+ linphone_core_remove_iterate_hook(LINPHONE_CONTACT_PROVIDER(obj)->lc, linphone_ldap_contact_provider_iterate,obj);
+
+ // clean pending requests
+ ms_list_for_each(obj->requests, linphone_ldap_contact_provider_destroy_request_cb);
+
+ if (obj->ld) ldap_unbind_ext(obj->ld, NULL, NULL);
+ obj->ld = NULL;
+
+ if( obj->config ) linphone_dictionary_unref(obj->config);
+
+ linphone_ldap_contact_provider_conf_destroy(obj);
+}
+
+static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
+{
+ if( strcmp(attr_name, obj->name_attr ) == 0 ){
+ lf->name = ms_strdup(attr_value);
+ } else if( strcmp(attr_name, obj->sip_attr) == 0 ) {
+ lf->sip = ms_strdup(attr_value);
+ }
+
+ // return 1 if the structure has enough data to create a linphone friend
+ if( lf->name && lf->sip )
+ return 1;
+ else
+ return 0;
+
+}
+
+static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req, LDAPMessage* message )
+{
+ int msgtype = ldap_msgtype(message);
+
+ switch(msgtype){
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_EXTENDED:
+ {
+ LDAPMessage *entry = ldap_first_entry(obj->ld, message);
+ LinphoneCore* lc = LINPHONE_CONTACT_PROVIDER(obj)->lc;
+
+ while( entry != NULL ){
+
+ struct LDAPFriendData ldap_data = {0};
+ bool_t contact_complete = FALSE;
+ BerElement* ber = NULL;
+ char* attr = ldap_first_attribute(obj->ld, entry, &ber);
+
+ while( attr ){
+ struct berval** values = ldap_get_values_len(obj->ld, entry, attr);
+ struct berval** it = values;
+
+ while( values && *it && (*it)->bv_val && (*it)->bv_len )
+ {
+ contact_complete = linphone_ldap_contact_provider_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
+ if( contact_complete ) break;
+
+ it++;
+ }
+
+ if( values ) ldap_value_free_len(values);
+ ldap_memfree(attr);
+
+ if( contact_complete ) break;
+
+ attr = ldap_next_attribute(obj->ld, entry, ber);
+ }
+
+ if( contact_complete ) {
+ LinphoneAddress* la = linphone_core_interpret_url(lc, ldap_data.sip);
+ if( la ){
+ LinphoneFriend* lf = linphone_core_create_friend(lc);
+ linphone_friend_set_address(lf, la);
+ linphone_friend_set_name(lf, ldap_data.name);
+ req->found_entries = ms_list_append(req->found_entries, lf);
+ req->found_count++;
+ //ms_message("Added friend %s / %s", ldap_data.name, ldap_data.sip);
+ ms_free(ldap_data.sip);
+ ms_free(ldap_data.name);
+ linphone_address_destroy(la);
+ }
+ }
+
+ if( ber ) ber_free(ber, 0);
+
+ entry = ldap_next_entry(obj->ld, entry);
+ }
+ }
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ {
+ // this one is received when a request is finished
+ req->complete = TRUE;
+ linphone_contact_search_invoke_cb(LINPHONE_CONTACT_SEARCH(req), req->found_entries);
+ }
+ break;
+
+
+ default: ms_message("[LDAP] Unhandled message type %x", msgtype); break;
+ }
+}
+
+static bool_t linphone_ldap_contact_provider_iterate(void *data)
+{
+ LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
+ if( obj->ld && obj->connected && (obj->req_count > 0) ){
+
+ // never block
+ struct timeval timeout = {0,0};
+ LDAPMessage* results = NULL;
+
+ int ret = ldap_result(obj->ld, LDAP_RES_ANY, LDAP_MSG_ONE, &timeout, &results);
+
+ switch( ret ){
+ case -1:
+ {
+ ms_warning("Error in ldap_result : returned -1 (req_count %d): %s", obj->req_count, ldap_err2string(errno));
+ break;
+ }
+ case 0: break; // nothing to do
+
+ case LDAP_RES_BIND:
+ {
+ ms_error("iterate: unexpected LDAP_RES_BIND");
+ break;
+ }
+ case LDAP_RES_EXTENDED:
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ case LDAP_RES_SEARCH_RESULT:
+ {
+ LDAPMessage* message = ldap_first_message(obj->ld, results);
+ LinphoneLDAPContactSearch* req = linphone_ldap_contact_provider_request_search(obj, ldap_msgid(message));
+ while( message != NULL ){
+ linphone_ldap_contact_provider_handle_search_result(obj, req, message );
+ message = ldap_next_message(obj->ld, message);
+ }
+ if( req && ret == LDAP_RES_SEARCH_RESULT)
+ linphone_ldap_contact_provider_cancel_search(
+ LINPHONE_CONTACT_PROVIDER(obj),
+ LINPHONE_CONTACT_SEARCH(req));
+ break;
+ }
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODDN:
+ case LDAP_RES_COMPARE:
+ default:
+ ms_message("Unhandled LDAP result %x", ret);
+ break;
+ }
+
+ if( results )
+ ldap_msgfree(results);
+ }
+
+ if( obj->ld && obj->connected ){
+ // check for pending searches
+ unsigned int i;
+
+ for( i=0; ireq_count; i++){
+ LinphoneLDAPContactSearch* search = (LinphoneLDAPContactSearch*)ms_list_nth_data( obj->requests, i );
+ if( search && search->msgid == 0){
+ int ret;
+ ms_message("Found pending search %p (for %s), launching...", search, search->filter);
+ ret = linphone_ldap_contact_provider_perform_search(obj, search);
+ if( ret != LDAP_SUCCESS ){
+ linphone_ldap_contact_provider_cancel_search(
+ LINPHONE_CONTACT_PROVIDER(obj),
+ LINPHONE_CONTACT_SEARCH(search));
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj )
+{
+ if(obj->attributes){
+ int i=0;
+ for( ; obj->attributes[i]; i++){
+ ms_free(obj->attributes[i]);
+ }
+ ms_free(obj->attributes);
+ }
+}
+
+static char* required_config_keys[] = {
+ // connection
+ "server",
+ "use_tls",
+ "auth_method",
+ "username",
+ "password",
+ "bind_dn",
+ "sasl_authname",
+ "sasl_realm",
+
+ // search
+ "base_object",
+ "filter",
+ "name_attribute",
+ "sip_attribute",
+ "attributes",
+
+ // misc
+ "timeout",
+ "max_results",
+ "deref_aliases",
+ NULL
+};
+
+static bool_t linphone_ldap_contact_provider_valid_config(const LinphoneDictionary* dict)
+{
+ char** config_name = required_config_keys;
+
+ bool_t valid = TRUE;
+ bool_t has_key;
+
+ while(*config_name ){
+ has_key = linphone_dictionary_haskey(dict, *config_name);
+ if( !has_key ) ms_error("Missing LDAP config value for '%s'", *config_name);
+ valid &= has_key;
+ config_name++;
+ }
+ return valid;
+}
+
+static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, const LinphoneDictionary* dict)
+{
+ char* attributes_list, *saveptr, *attr;
+ unsigned int attr_count = 0, attr_idx = 0, i;
+
+ if( !linphone_ldap_contact_provider_valid_config(dict) ) return;
+
+ // free any pre-existing attributes values
+ linphone_ldap_contact_provider_conf_destroy(obj);
+ if( obj->config ) linphone_dictionary_unref(obj->config);
+
+ // clone new config into the dictionary
+ obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
+
+#if 0 // until sasl auth is set up, force anonymous auth.
+ linphone_dictionary_set_string(obj->config, "auth_method", "ANONYMOUS");
+#endif
+
+ obj->use_tls = linphone_dictionary_get_int(obj->config, "use_tls", 0);
+ obj->timeout = linphone_dictionary_get_int(obj->config, "timeout", 10);
+ obj->deref_aliases = linphone_dictionary_get_int(obj->config, "deref_aliases", 0);
+ obj->max_results = linphone_dictionary_get_int(obj->config, "max_results", 50);
+ obj->auth_method = linphone_dictionary_get_string(obj->config, "auth_method", "ANONYMOUS");
+ obj->username = linphone_dictionary_get_string(obj->config, "username", "");
+ obj->password = linphone_dictionary_get_string(obj->config, "password", "");
+ obj->bind_dn = linphone_dictionary_get_string(obj->config, "bind_dn", "");
+ obj->base_object = linphone_dictionary_get_string(obj->config, "base_object", "dc=example,dc=com");
+ obj->server = linphone_dictionary_get_string(obj->config, "server", "ldap://localhost");
+ obj->filter = linphone_dictionary_get_string(obj->config, "filter", "uid=*%s*");
+ obj->name_attr = linphone_dictionary_get_string(obj->config, "name_attribute", "givenName");
+ obj->sip_attr = linphone_dictionary_get_string(obj->config, "sip_attribute", "mobile");
+ obj->sasl_authname = linphone_dictionary_get_string(obj->config, "sasl_authname", "");
+ obj->sasl_realm = linphone_dictionary_get_string(obj->config, "sasl_realm", "");
+
+ /*
+ * parse the attributes list
+ */
+ attributes_list = ms_strdup(
+ linphone_dictionary_get_string(obj->config,
+ "attributes",
+ "telephoneNumber,givenName,sn,mobile,homePhone")
+ );
+
+ // count attributes:
+ for( i=0; attributes_list[i]; i++) {
+ if( attributes_list[i] == ',') attr_count++;
+ }
+
+ // 1 more for the first attr without ',', the other for the null-finished list
+ obj->attributes = ms_malloc0((attr_count+2) * sizeof(char*));
+
+ attr = strtok_r( attributes_list, ",", &saveptr );
+ while( attr != NULL ){
+ obj->attributes[attr_idx] = ms_strdup(attr);
+ attr_idx++;
+ attr = strtok_r(NULL, ",", &saveptr);
+ }
+ if( attr_idx != attr_count+1) ms_error("Invalid attribute number!!! %d expected, got %d", attr_count+1, attr_idx);
+
+ ms_free(attributes_list);
+}
+
+static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
+ unsigned flags,
+ void *defaults,
+ void *sasl_interact)
+{
+ sasl_interact_t *interact = (sasl_interact_t*)sasl_interact;
+ LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(defaults);
+ ms_message("bind_interact called: ld %p, flags %x, default %p, interact %p",
+ ld, flags, defaults, sasl_interact);
+
+ if( ld == NULL ) return LDAP_PARAM_ERROR;
+
+ while( interact->id != SASL_CB_LIST_END ) {
+
+ const char *dflt = interact->defresult;
+
+ switch( interact->id ) {
+ case SASL_CB_GETREALM:
+ ms_message("* SASL_CB_GETREALM -> %s", obj->sasl_realm);
+ dflt = obj->sasl_realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ ms_message("* SASL_CB_AUTHNAME -> %s", obj->sasl_authname);
+ dflt = obj->sasl_authname;
+ break;
+ case SASL_CB_USER:
+ ms_message("* SASL_CB_USER -> %s", obj->username);
+ dflt = obj->username;
+ break;
+ case SASL_CB_PASS:
+ ms_message("* SASL_CB_PASS (hidden)");
+ dflt = obj->password;
+ break;
+ default:
+ ms_message("SASL interact asked for unknown id %lx\n",interact->id);
+ }
+ interact->result = (dflt && *dflt) ? dflt : (const char*)"";
+ interact->len = strlen( (const char*)interact->result );
+
+ interact++;
+ }
+ return LDAP_SUCCESS;
+}
+
+static void* ldap_bind_thread_func( void*arg)
+{
+ LinphoneLDAPContactProvider* obj = linphone_ldap_contact_provider_ref(arg);
+ const char* auth_mechanism = obj->auth_method;
+ int ret;
+
+ if( (strcmp(auth_mechanism, "ANONYMOUS") == 0) || (strcmp(auth_mechanism, "SIMPLE") == 0) )
+ {
+ struct berval passwd = { strlen(obj->password), ms_strdup(obj->password)};
+ auth_mechanism = LDAP_SASL_SIMPLE;
+ ret = ldap_sasl_bind_s(obj->ld,
+ obj->bind_dn,
+ auth_mechanism,
+ &passwd,
+ NULL,
+ NULL,
+ NULL);
+
+ ms_free(passwd.bv_val);
+ }
+ else
+ {
+
+ ms_message("LDAP interactive bind");
+ ret = ldap_sasl_interactive_bind_s(obj->ld,
+ obj->bind_dn,
+ auth_mechanism,
+ NULL,NULL,
+ LDAP_SASL_QUIET,
+ linphone_ldap_contact_provider_bind_interact,
+ obj);
+ }
+
+ if( ret == LDAP_SUCCESS ) {
+ ms_message("LDAP bind OK");
+ obj->connected = 1;
+ } else {
+ int err;
+ ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err);
+ ms_error("ldap_sasl_bind error returned %x, err %x (%s), auth_method: %s",
+ ret, err, ldap_err2string(err), auth_mechanism );
+ }
+
+ obj->bind_thread = 0;
+ linphone_ldap_contact_provider_unref(obj);
+ return (void*)0;
+}
+
+static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
+{
+ // perform the bind in an alternate thread, so that we don't stall the main loop
+ ms_thread_create(&obj->bind_thread, NULL, ldap_bind_thread_func, obj);
+ return 0;
+}
+
+unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj)
+{
+ return obj->max_results;
+}
+
+static void linphone_ldap_contact_provider_config_dump_cb(const char*key, void* value, void* userdata)
+{
+ ms_message("- %s -> %s", key, (const char* )value);
+}
+
+LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config)
+{
+ LinphoneLDAPContactProvider* obj = belle_sip_object_new(LinphoneLDAPContactProvider);
+ int proto_version = LDAP_VERSION3;
+
+ linphone_contact_provider_init((LinphoneContactProvider*)obj, lc);
+
+ ms_message( "Constructed Contact provider '%s'", BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->name);
+
+ if( !linphone_ldap_contact_provider_valid_config(config) ) {
+ ms_error( "Invalid configuration for LDAP, aborting creation");
+ belle_sip_object_unref(obj);
+ obj = NULL;
+ } else {
+ int ret;
+ linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 );
+ linphone_ldap_contact_provider_loadconfig(obj, config);
+
+ ret = ldap_initialize(&(obj->ld),obj->server);
+
+ if( ret != LDAP_SUCCESS ){
+ ms_error( "Problem initializing ldap on url '%s': %s", obj->server, ldap_err2string(ret));
+ belle_sip_object_unref(obj);
+ obj = NULL;
+ } else if( (ret = ldap_set_option(obj->ld, LDAP_OPT_PROTOCOL_VERSION, &proto_version)) != LDAP_SUCCESS ){
+ ms_error( "Problem setting protocol version %d: %s", proto_version, ldap_err2string(ret));
+ belle_sip_object_unref(obj);
+ obj = NULL;
+ } else {
+ // prevents blocking calls to bind() when the server is invalid, but this is not working for now..
+ // see bug https://bugzilla.mozilla.org/show_bug.cgi?id=79509
+ //ldap_set_option( obj->ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
+
+ // register our hook into iterate so that LDAP can do its magic asynchronously.
+ linphone_core_add_iterate_hook(lc, linphone_ldap_contact_provider_iterate, obj);
+ }
+ }
+ return obj;
+}
+
+/**
+ * Search an LDAP request in the list of current LDAP requests to serve, only taking
+ * the msgid as a key to search.
+ */
+static int linphone_ldap_request_entry_compare_weak(const void*a, const void* b)
+{
+ const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
+ const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
+ return !(ra->msgid == rb->msgid); // 0 if equal
+}
+
+/**
+ * Search an LDAP request in the list of current LDAP requests to serve, with strong search
+ * comparing both msgid and request pointer
+ */
+static int linphone_ldap_request_entry_compare_strong(const void*a, const void* b)
+{
+ const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
+ const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
+ return !(ra->msgid == rb->msgid) && !(ra == rb);
+}
+
+static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid )
+{
+ LinphoneLDAPContactSearch dummy = {};
+ MSList* list_entry;
+ dummy.msgid = msgid;
+
+ list_entry = ms_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy);
+ if( list_entry ) return list_entry->data;
+ else return NULL;
+}
+
+static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
+{
+ LinphoneLDAPContactSearch* ldap_req = LINPHONE_LDAP_CONTACT_SEARCH(req);
+ LinphoneLDAPContactProvider* ldap_cp = LINPHONE_LDAP_CONTACT_PROVIDER(obj);
+ int ret = 1;
+
+ MSList* list_entry = ms_list_find_custom(ldap_cp->requests, linphone_ldap_request_entry_compare_strong, req);
+ if( list_entry ) {
+ ms_message("Delete search %p", req);
+ ldap_cp->requests = ms_list_remove_link(ldap_cp->requests, list_entry);
+ ldap_cp->req_count--;
+ ret = 0; // return OK if we found it in the monitored requests
+ } else {
+ ms_warning("Couldn't find ldap request %p (id %d) in monitoring.", ldap_req, ldap_req->msgid);
+ }
+ belle_sip_object_unref(req); // unref request even if not found
+ return ret;
+}
+
+static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req)
+{
+ int ret = -1;
+ struct timeval timeout = { obj->timeout, 0 };
+
+ if( req->msgid == 0 ){
+ ms_message ( "Calling ldap_search_ext with predicate '%s' on base '%s', ld %p, attrs '%s', maxres = %d", req->filter, obj->base_object, obj->ld, obj->attributes[0], obj->max_results );
+ ret = ldap_search_ext(obj->ld,
+ obj->base_object,// base from which to start
+ LDAP_SCOPE_SUBTREE,
+ req->filter, // search predicate
+ obj->attributes, // which attributes to get
+ 0, // 0 = get attrs AND value, 1 = get attrs only
+ NULL,
+ NULL,
+ &timeout, // server timeout for the search
+ obj->max_results,// max result number
+ &req->msgid );
+
+ if( ret != LDAP_SUCCESS ){
+ ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
+ } else {
+ ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", req, req->msgid);
+ }
+
+ } else {
+ ms_warning( "LDAP Search already performed for %s, msgid %d", req->filter, req->msgid);
+ }
+ return ret;
+}
+
+static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
+ const char* predicate,
+ ContactSearchCallback cb,
+ void* cb_data )
+{
+ bool_t connected = obj->connected;
+ LinphoneLDAPContactSearch* request;
+
+ // if we're not yet connected, bind
+ if( !connected ) {
+ if( !obj->bind_thread ) linphone_ldap_contact_provider_bind(obj);
+ }
+
+ request = linphone_ldap_contact_search_create( obj, predicate, cb, cb_data );
+
+ if( connected ){
+ int ret = linphone_ldap_contact_provider_perform_search(obj, request);
+ ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request );
+ if( ret != LDAP_SUCCESS ){
+ belle_sip_object_unref(request);
+ request = NULL;
+ }
+ } else {
+ ms_message("Delayed search, wait for connection");
+ }
+
+ if( request != NULL ) {
+ obj->requests = ms_list_append ( obj->requests, request );
+ obj->req_count++;
+ }
+
+ return request;
+}
+
+
+static int linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider* obj, char* buff, size_t buff_size, size_t *offset)
+{
+ belle_sip_error_code error = BELLE_SIP_OK;
+ char **attr;
+
+ error = belle_sip_snprintf(buff, buff_size, offset, "ld:%p,\n", obj->ld);
+ if(error!= BELLE_SIP_OK) return error;
+
+ error = belle_sip_snprintf(buff, buff_size, offset, "req_count:%d,\n", obj->req_count);
+ if(error!= BELLE_SIP_OK) return error;
+
+ error = belle_sip_snprintf(buff, buff_size, offset,
+ "CONFIG:\n"
+ "tls: %d \n"
+ "auth: %s \n"
+ "user: %s \n"
+ "pass: %s \n"
+ "server: %s \n"
+ "base: %s \n"
+ "filter: %s \n"
+ "timeout: %d \n"
+ "deref: %d \n"
+ "max_res: %d \n"
+ "sip_attr:%s \n"
+ "name_attr:%s \n"
+ "attrs:\n",
+ obj->use_tls, obj->auth_method,
+ obj->username, obj->password, obj->server,
+ obj->base_object, obj->filter,
+ obj->timeout, obj->deref_aliases,
+ obj->max_results,
+ obj->sip_attr, obj->name_attr);
+ if(error!= BELLE_SIP_OK) return error;
+
+ attr = obj->attributes;
+ while( *attr ){
+ error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
+ if(error!= BELLE_SIP_OK) return error;
+ else attr++;
+ }
+
+ return error;
+
+}
+
+LinphoneLDAPContactProvider*linphone_ldap_contact_provider_ref(void* obj)
+{
+ return linphone_ldap_contact_provider_cast(belle_sip_object_ref(obj));
+}
+
+
+void linphone_ldap_contact_provider_unref(void* obj)
+{
+ belle_sip_object_unref(obj);
+}
+
+inline LinphoneLDAPContactSearch*linphone_ldap_contact_search_cast(void* obj)
+{
+ return BELLE_SIP_CAST(obj, LinphoneLDAPContactSearch);
+}
+
+
+LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast(void* obj)
+{
+ return BELLE_SIP_CAST(obj, LinphoneLDAPContactProvider);
+}
+
+int linphone_ldap_contact_provider_available()
+{
+ return 1;
+}
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider);
+
+BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider)
+ {
+ {
+ BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE),
+ (belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy,
+ NULL,
+ (belle_sip_object_marshal_t)linphone_ldap_contact_provider_marshal
+ },
+ "LDAP",
+ (LinphoneContactProviderStartSearchMethod)linphone_ldap_contact_provider_begin_search,
+ (LinphoneContactProviderCancelSearchMethod)linphone_ldap_contact_provider_cancel_search
+ }
+BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END
+
+#else
+
+/* Stubbed implementation */
+
+LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* ld,
+ const char* predicate,
+ ContactSearchCallback cb,
+ void* cb_data)
+{
+ return NULL;
+}
+
+unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj){ return 0; }
+LinphoneLDAPContactSearch* linphone_ldap_contact_search_cast( void* obj ){ return NULL; }
+
+
+/* LinphoneLDAPContactProvider */
+
+LinphoneLDAPContactProvider* linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config){ return NULL; }
+unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj){ return 0; }
+LinphoneLDAPContactProvider* linphone_ldap_contact_provider_ref( void* obj ){ return NULL; }
+void linphone_ldap_contact_provider_unref( void* obj ){ }
+LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast( void* obj ){ return NULL; }
+
+int linphone_ldap_contact_provider_available(){ return 0; }
+
+
+#endif /* BUILD_LDAP */
+
diff --git a/coreapi/ldap/ldapprovider.h b/coreapi/ldap/ldapprovider.h
new file mode 100644
index 000000000..066ab7ba8
--- /dev/null
+++ b/coreapi/ldap/ldapprovider.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "contactprovider.h"
+
+
+typedef struct _LinphoneLDAPContactProvider LinphoneLDAPContactProvider;
+
+/* LinphoneLDAPContactSearch */
+typedef struct _LinphoneLDAPContactSearch LinphoneLDAPContactSearch;
+
+LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* ld,
+ const char* predicate,
+ ContactSearchCallback cb,
+ void* cb_data);
+
+LINPHONE_PUBLIC unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj);
+LINPHONE_PUBLIC LinphoneLDAPContactSearch* linphone_ldap_contact_search_cast( void* obj );
+
+
+/* LinphoneLDAPContactProvider */
+
+LINPHONE_PUBLIC LinphoneLDAPContactProvider* linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config);
+LINPHONE_PUBLIC unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj);
+LINPHONE_PUBLIC LinphoneLDAPContactProvider* linphone_ldap_contact_provider_ref( void* obj );
+void linphone_ldap_contact_provider_unref( void* obj );
+LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast( void* obj );
+LINPHONE_PUBLIC int linphone_ldap_contact_provider_available();
diff --git a/coreapi/lime.c b/coreapi/lime.c
new file mode 100644
index 000000000..190ea6be7
--- /dev/null
+++ b/coreapi/lime.c
@@ -0,0 +1,836 @@
+#include "lime.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIME
+
+#include "linphonecore.h"
+#include "ortp/b64.h"
+#include "polarssl/gcm.h"
+
+/* check polarssl version */
+#include
+
+#if POLARSSL_VERSION_NUMBER >= 0x01030000 /* for Polarssl version 1.3 */
+#include "polarssl/sha256.h"
+#else /* for Polarssl version 1.2 */
+#include "polarssl/sha2.h"
+#endif
+
+/**
+ * @brief check at runtime if LIME is available
+ *
+ * @return TRUE when Lime was fully compiled, FALSE when it wasn't
+ */
+bool_t lime_is_available() { return TRUE; }
+
+/**
+ * @brief convert an hexa char [0-9a-fA-F] into the corresponding unsigned integer value
+ * Any invalid char will be converted to zero without any warning
+ *
+ * @param[in] inputChar a char which shall be in range [0-9a-fA-F]
+ *
+ * @return the unsigned integer value in range [0-15]
+ */
+uint8_t lime_charToByte(uint8_t inputChar) {
+ /* 0-9 */
+ if (inputChar>0x29 && inputChar<0x3A) {
+ return inputChar - 0x30;
+ }
+
+ /* a-f */
+ if (inputChar>0x60 && inputChar<0x67) {
+ return inputChar - 0x57; /* 0x57 = 0x61(a) + 0x0A*/
+ }
+
+ /* A-F */
+ if (inputChar>0x40 && inputChar<0x47) {
+ return inputChar - 0x37; /* 0x37 = 0x41(a) + 0x0A*/
+ }
+
+ /* shall never arrive here, string is not Hex*/
+ return 0;
+
+}
+
+/**
+ * @brief convert a byte which value is in range [0-15] into an hexa char [0-9a-fA-F]
+ *
+ * @param[in] inputByte an integer which shall be in range [0-15]
+ *
+ * @return the hexa char [0-9a-f] corresponding to the input
+ */
+uint8_t lime_byteToChar(uint8_t inputByte) {
+ inputByte &=0x0F; /* restrict the input value to range [0-15] */
+ /* 0-9 */
+ if(inputByte<0x0A) {
+ return inputByte+0x30;
+ }
+ /* a-f */
+ return inputByte + 0x57;
+}
+
+
+/**
+ * @brief Convert an hexadecimal string into the corresponding byte buffer
+ *
+ * @param[out] outputBytes The output bytes buffer, must have a length of half the input string buffer
+ * @param[in] inputString The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0)
+ * @param[in] inputStringLength The lenght in chars of the string buffer, output is half this length
+ */
+void lime_strToUint8(uint8_t *outputBytes, uint8_t *inputString, uint16_t inputStringLength) {
+ int i;
+ for (i=0; i>4)&0x0F);
+ outputString[2*i+1] = lime_byteToChar(inputBytes[i]&0x0F);
+ }
+}
+
+
+
+/**
+ * @brief Retrieve selfZID from cache
+ *
+ * @param[in] cacheBuffer The xmlDoc containing current cache
+ * @param[out] selfZid The ZID found as a 24 hexa char string null terminated
+ *
+ * @return 0 on success, error code otherwise
+ */
+static int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]) {
+ xmlNodePtr cur;
+ xmlChar *selfZidHex;
+
+ if (cacheBuffer == NULL ) {
+ return LIME_INVALID_CACHE;
+ }
+
+ cur = xmlDocGetRootElement(cacheBuffer);
+ /* if we found a root element, parse its children node */
+ if (cur!=NULL)
+ {
+ cur = cur->xmlChildrenNode;
+ }
+ selfZidHex = NULL;
+ while (cur!=NULL) {
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */
+ selfZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode, 1);
+ /* copy it to the output buffer and add the null termination */
+ memcpy(selfZid, selfZidHex, 24);
+ selfZid[24]='\0';
+ break;
+ }
+ cur = cur->next;
+ }
+
+ /* did we found a ZID? */
+ if (selfZidHex == NULL) {
+ return LIME_INVALID_CACHE;
+ }
+
+ xmlFree(selfZidHex);
+ return 0;
+}
+
+int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys) {
+ xmlNodePtr cur;
+
+ /* parse the file to get all peer matching the sipURI given in associatedKeys*/
+ if (cacheBuffer == NULL ) { /* there is no cache return error */
+ return LIME_INVALID_CACHE;
+ }
+
+ /* reset number of associated keys and their buffer */
+ associatedKeys->associatedZIDNumber = 0;
+ associatedKeys->peerKeys = NULL;
+
+ cur = xmlDocGetRootElement(cacheBuffer);
+ /* if we found a root element, parse its children node */
+ if (cur!=NULL)
+ {
+ cur = cur->xmlChildrenNode;
+ }
+ while (cur!=NULL) { /* loop on all peer nodes */
+ uint8_t matchingURIFlag = 0; /* this flag is set to one if we found the requested sipURI in the current peer node */
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))) { /* found a peer node, check if there is a matching sipURI node in it */
+ xmlNodePtr peerNodeChildren = cur->xmlChildrenNode;
+ matchingURIFlag = 0;
+
+ /* loop on children nodes until the end or we found the matching sipURI */
+ while (peerNodeChildren!=NULL && matchingURIFlag==0) {
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"uri")) { /* found a peer an URI node, check the content */
+ xmlChar *uriNodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ if (!xmlStrcmp(uriNodeContent, (const xmlChar *)associatedKeys->peerURI)) { /* found a match with requested URI */
+ matchingURIFlag=1;
+ }
+ xmlFree(uriNodeContent);
+ }
+ peerNodeChildren = peerNodeChildren->next;
+ }
+
+ if (matchingURIFlag == 1) { /* we found a match for the URI in this peer node, extract the keys, session Id and index values */
+ /* allocate a new limeKey_t structure to hold the retreived keys */
+ limeKey_t *currentPeerKeys = (limeKey_t *)malloc(sizeof(limeKey_t));
+ uint8_t itemFound = 0; /* count the item found, we must get all of the requested infos: 5 nodes*/
+ uint8_t pvs = 0;
+
+ peerNodeChildren = cur->xmlChildrenNode; /* reset peerNodeChildren to the first child of node */
+ while (peerNodeChildren!=NULL && itemFound<5) {
+ xmlChar *nodeContent = NULL;
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(currentPeerKeys->peerZID, nodeContent, 24);
+ itemFound++;
+ }
+
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(currentPeerKeys->key, nodeContent, 64);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(currentPeerKeys->sessionId, nodeContent, 64);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
+ uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(sessionIndexBuffer, nodeContent, 8);
+ /* convert it back to a uint32_t (MSByte first)*/
+ currentPeerKeys->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
+ itemFound++;
+ }
+
+ xmlFree(nodeContent);
+ peerNodeChildren = peerNodeChildren->next;
+ }
+
+ /* check if we have all the requested information and the PVS flag is set to 1 */
+ if (itemFound == 5 && pvs == 1) {
+ associatedKeys->associatedZIDNumber +=1;
+ /* extend array of pointer to limeKey_t structures to add the one we found */
+ associatedKeys->peerKeys = (limeKey_t **)realloc(associatedKeys->peerKeys, (associatedKeys->associatedZIDNumber)*sizeof(limeKey_t *));
+
+ /* add the new entry at the end */
+ associatedKeys->peerKeys[associatedKeys->associatedZIDNumber-1] = currentPeerKeys;
+
+ } else {
+ free(currentPeerKeys);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ return 0;
+}
+
+int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) {
+ uint8_t peerZidHex[25];
+ /* to check we collect all the information needed from the cache and that pvs(boolean for previously verified Sas) is set in cache */
+ uint8_t itemFound = 0;
+ uint8_t pvs = 0;
+ xmlNodePtr cur;
+
+ if (cacheBuffer == NULL ) { /* there is no cache return error */
+ return LIME_INVALID_CACHE;
+ }
+
+ /* get the given ZID into hex format */
+ lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
+ peerZidHex[24]='\0'; /* must be a null terminated string */
+
+ cur = xmlDocGetRootElement(cacheBuffer);
+ /* if we found a root element, parse its children node */
+ if (cur!=NULL)
+ {
+ cur = cur->xmlChildrenNode;
+
+ }
+
+ while (cur!=NULL) { /* loop on all peer nodes */
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
+ xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
+ if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
+ xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
+ while (peerNodeChildren != NULL && itemFound<4) { /* look for the tag we want to read : rcvKey, rcvSId, rcvIndex and pvs*/
+ xmlChar *nodeContent = NULL;
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(associatedKey->key, nodeContent, 64);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(associatedKey->sessionId, nodeContent, 64);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
+ uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(sessionIndexBuffer, nodeContent, 8);
+ /* convert it back to a uint32_t (MSByte first)*/
+ associatedKey->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
+ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
+ lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
+ itemFound++;
+ }
+ xmlFree(nodeContent);
+ peerNodeChildren = peerNodeChildren->next;
+ }
+ xmlFree(currentZidHex);
+ break; /* we parsed the peer node we were looking for, get out of the main while */
+ }
+ xmlFree(currentZidHex);
+ }
+ cur = cur->next;
+ }
+
+ /* if we manage to find the correct key information and that pvs is set to 1, return 0 (success) */
+ if ((pvs == 1) && (itemFound == 4)) {
+ return 0;
+ }
+
+ /* otherwise, key wasn't found or is invalid */
+ return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
+}
+
+int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) {
+ xmlNodePtr cur;
+ uint8_t peerZidHex[25];
+ uint8_t keyHex[65]; /* key is 32 bytes long -> 64 bytes string + null termination */
+ uint8_t sessionIdHex[65]; /* sessionId is 32 bytes long -> 64 bytes string + null termination */
+ uint8_t sessionIndexHex[9]; /* sessionInedx is an uint32_t : 4 bytes long -> 8 bytes string + null termination */
+ uint8_t itemFound = 0;
+
+ if (cacheBuffer == NULL ) { /* there is no cache return error */
+ return LIME_INVALID_CACHE;
+ }
+
+ /* get the given ZID into hex format */
+ lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
+ peerZidHex[24]='\0'; /* must be a null terminated string */
+
+ cur = xmlDocGetRootElement(cacheBuffer);
+ /* if we found a root element, parse its children node */
+ if (cur!=NULL)
+ {
+ cur = cur->xmlChildrenNode;
+
+ }
+
+ /* convert the given tag content to null terminated Hexadecimal strings */
+ lime_int8ToStr(keyHex, associatedKey->key, 32);
+ keyHex[64] = '\0';
+ lime_int8ToStr(sessionIdHex, associatedKey->sessionId, 32);
+ sessionIdHex[64] = '\0';
+ sessionIndexHex[0] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>28)&0x0F));
+ sessionIndexHex[1] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>24)&0x0F));
+ sessionIndexHex[2] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>20)&0x0F));
+ sessionIndexHex[3] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>16)&0x0F));
+ sessionIndexHex[4] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>12)&0x0F));
+ sessionIndexHex[5] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>8)&0x0F));
+ sessionIndexHex[6] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>4)&0x0F));
+ sessionIndexHex[7] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex)&0x0F));
+ sessionIndexHex[8] = '\0';
+
+ while (cur!=NULL && itemFound<3) { /* loop on all peer nodes */
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
+ xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
+ if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
+ xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
+ while (peerNodeChildren != NULL && itemFound<3) { /* look for the tag we want to write */
+ if (role == LIME_RECEIVER) { /* writing receiver key */
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
+ itemFound++;
+ }
+ } else { /* writing sender key */
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
+ itemFound++;
+ }
+ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
+ xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
+ itemFound++;
+ }
+ }
+ peerNodeChildren = peerNodeChildren->next;
+ }
+ }
+ xmlFree(currentZidHex);
+ }
+ cur = cur->next;
+ }
+
+
+ return 0;
+}
+
+/**
+ * @brief Derive in place the key given in parameter and increment session index
+ * Derivation is made derived Key = HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||256)
+ *
+ * @param[in/out] key The structure containing the original key which will be overwritten, the sessionId and SessionIndex
+ *
+ * @return 0 on success, error code otherwise
+ */
+static int lime_deriveKey(limeKey_t *key) {
+ uint8_t inputData[55];
+ uint8_t derivedKey[32];
+
+ if (key == NULL) {
+ return LIME_UNABLE_TO_DERIVE_KEY;
+ }
+
+#if 0
+ /*not doing anything yet since key and sessionId are array, not pointers*/
+ if ((key->key == NULL) || (key->sessionId == NULL)) {
+ return LIME_UNABLE_TO_DERIVE_KEY;
+ }
+#endif
+
+ /* Derivation is made derived Key = HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||0x00000100)*/
+ /* total data to be hashed is 55 bytes : 4 + 10 + 1 + 32 + 4 + 4 */
+ inputData[0] = 0x00;
+ inputData[1] = 0x00;
+ inputData[2] = 0x00;
+ inputData[3] = 0x01;
+
+ memcpy(inputData+4, "MessageKey", 10);
+
+ inputData[14] = 0x00;
+
+ memcpy(inputData+15, key->sessionId, 32);
+
+ inputData[47] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
+ inputData[48] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
+ inputData[49] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
+ inputData[50] = (uint8_t)(key->sessionIndex&0x000000FF);
+
+ inputData[51] = 0x00;
+ inputData[52] = 0x00;
+ inputData[53] = 0x01;
+ inputData[54] = 0x00;
+
+ /* derive the key in a temp buffer */
+#if POLARSSL_VERSION_NUMBER >= 0x01030000 /* for Polarssl version 1.3 */
+ sha256_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
+#else /* for Polarssl version 1.2 */
+ sha2_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
+#endif /* POLARSSL_VERSION_NUMBER */
+
+ /* overwrite the old key with the derived one */
+ memcpy(key->key, derivedKey, 32);
+
+ /* increment the session Index */
+ key->sessionIndex += 1;
+ return 0;
+}
+
+void lime_freeKeys(limeURIKeys_t associatedKeys) {
+ int i;
+
+ /* free all associated keys */
+ for (i=0; i< associatedKeys.associatedZIDNumber; i++) {
+ if (associatedKeys.peerKeys[i] != NULL) {
+ free(associatedKeys.peerKeys[i]);
+ associatedKeys.peerKeys[i] = NULL;
+ }
+ }
+
+ free(associatedKeys.peerKeys);
+
+ /* free sipURI string */
+ free(associatedKeys.peerURI);
+}
+
+int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
+ uint8_t authenticatedData[28];
+ gcm_context gcmContext;
+ /* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
+ memcpy(authenticatedData, selfZID, 12);
+ memcpy(authenticatedData+12, key->peerZID, 12);
+ authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
+ authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
+ authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
+ authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);
+
+ /* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
+ /* tag is 16 bytes long and is set in the 16 first bytes of the encrypted message */
+ gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
+ gcm_crypt_and_tag(&gcmContext, GCM_ENCRYPT, messageLength, key->key+24, 8, authenticatedData, 28, plainMessage, encryptedMessage+16, 16, encryptedMessage);
+ gcm_free(&gcmContext);
+
+ return 0;
+}
+
+int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
+ gcm_context *gcmContext;
+
+ if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
+ gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
+ *cryptoContext = (void *)gcmContext;
+ gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
+ gcm_starts(gcmContext, GCM_ENCRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
+ } else { /* this is not the first call, get the context */
+ gcmContext = (gcm_context *)*cryptoContext;
+ }
+
+ if (length != 0) {
+ gcm_update(gcmContext, length, (const unsigned char *)plain, (unsigned char *)cipher);
+ } else { /* lenght is 0, finish the stream */
+ gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
+ gcm_free(gcmContext);
+ free(*cryptoContext);
+ *cryptoContext = NULL;
+ }
+
+ return 0;
+}
+
+int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
+ gcm_context *gcmContext;
+
+ if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
+ gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
+ *cryptoContext = (void *)gcmContext;
+ gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
+ gcm_starts(gcmContext, GCM_DECRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
+ } else { /* this is not the first call, get the context */
+ gcmContext = (gcm_context *)*cryptoContext;
+ }
+
+ if (length != 0) {
+ gcm_update(gcmContext, length, (const unsigned char *)cipher, (unsigned char *)plain);
+ } else { /* lenght is 0, finish the stream */
+ gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
+ gcm_free(gcmContext);
+ free(*cryptoContext);
+ *cryptoContext = NULL;
+ }
+
+ return 0;
+}
+
+
+int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) {
+ uint8_t authenticatedData[28];
+ gcm_context gcmContext;
+ int retval;
+
+ /* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
+ memcpy(authenticatedData, key->peerZID, 12);
+ memcpy(authenticatedData+12, selfZID, 12);
+ authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
+ authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
+ authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
+ authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);
+
+ /* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
+ /* tag is 16 bytes long and is the 16 first bytes of the encrypted message */
+ gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
+ /* messageLength-16 is the length of encrypted data, messageLength include the 16 bytes tag included at the begining of encryptedMessage */
+ retval = gcm_auth_decrypt(&gcmContext, messageLength-16, key->key+24, 8, authenticatedData, 28, encryptedMessage, 16, encryptedMessage+16, plainMessage);
+ gcm_free(&gcmContext);
+ /* add the null termination char */
+ plainMessage[messageLength-16] = '\0';
+
+ return retval;
+}
+
+int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
+ uint8_t selfZidHex[25];
+ uint8_t selfZid[12]; /* same data but in byte buffer */
+ uint32_t encryptedMessageLength;
+ limeURIKeys_t associatedKeys;
+ xmlDocPtr xmlOutputMessage;
+ xmlNodePtr rootNode;
+ int i;
+ int xmlStringLength;
+
+ /* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
+ if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
+ return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
+ }
+ lime_strToUint8(selfZid, selfZidHex, 24);
+
+ /* encrypted message length is plaintext + 16 for tag */
+ encryptedMessageLength = strlen((char *)message) + 16;
+
+ /* retrieve keys associated to the peer URI */
+ associatedKeys.peerURI = (uint8_t *)malloc(strlen((char *)peerURI)+1);
+ strcpy((char *)(associatedKeys.peerURI), (char *)peerURI);
+ associatedKeys.associatedZIDNumber = 0;
+ associatedKeys.peerKeys = NULL;
+
+ if (lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys) != 0) {
+ lime_freeKeys(associatedKeys);
+ return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
+ }
+
+ if (associatedKeys.associatedZIDNumber == 0) {
+ lime_freeKeys(associatedKeys);
+ return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
+ }
+
+ /* create an xml doc to hold the multipart message */
+ xmlOutputMessage = xmlNewDoc((const xmlChar *)"1.0");
+ /* root tag is "doc" */
+ rootNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"doc", NULL);
+ xmlDocSetRootElement(xmlOutputMessage, rootNode);
+ /* add the self ZID child */
+ xmlNewTextChild(rootNode, NULL, (const xmlChar *)"ZID", selfZidHex);
+
+ /* loop on all keys found */
+ for (i=0; i
+ * peerZID
+ * session index
+ * ciphertext
+ * */
+ msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
+ lime_int8ToStr(peerZidHex, currentKey->peerZID, 12);
+ peerZidHex[24] = '\0';
+ sessionIndexHex[0] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>28)&0x0F));
+ sessionIndexHex[1] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>24)&0x0F));
+ sessionIndexHex[2] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>20)&0x0F));
+ sessionIndexHex[3] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>16)&0x0F));
+ sessionIndexHex[4] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>12)&0x0F));
+ sessionIndexHex[5] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>8)&0x0F));
+ sessionIndexHex[6] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>4)&0x0F));
+ sessionIndexHex[7] = lime_byteToChar((uint8_t)((currentKey->sessionIndex)&0x0F));
+ sessionIndexHex[8] = '\0';
+
+ xmlNewTextChild(msgNode, NULL, (const xmlChar *)"pzid", peerZidHex);
+ xmlNewTextChild(msgNode, NULL, (const xmlChar *)"index", sessionIndexHex);
+
+ /* convert the cipherText to base 64 */
+ b64Size = b64_encode(NULL, encryptedMessageLength, NULL, 0);
+ encryptedMessageb64 = (char *)malloc(b64Size+1);
+ b64Size = b64_encode(encryptedMessage, encryptedMessageLength, encryptedMessageb64, b64Size);
+ encryptedMessageb64[b64Size] = '\0'; /* libxml need a null terminated string */
+ xmlNewTextChild(msgNode, NULL, (const xmlChar *)"text", (const xmlChar *)encryptedMessageb64);
+ free(encryptedMessage);
+ free(encryptedMessageb64);
+
+ /* add the message Node into the doc */
+ xmlAddChild(rootNode, msgNode);
+
+ /* update the key used */
+ lime_deriveKey(currentKey);
+ lime_setCachedKey(cacheBuffer, currentKey, LIME_SENDER);
+ }
+
+ /* dump the whole message doc into the output */
+ xmlDocDumpFormatMemoryEnc(xmlOutputMessage, output, &xmlStringLength, "UTF-8", 0);
+ xmlFreeDoc(xmlOutputMessage);
+
+ lime_freeKeys(associatedKeys);
+
+ return 0;
+}
+
+int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) {
+ int retval;
+ uint8_t selfZidHex[25];
+ uint8_t selfZid[12]; /* same data but in byte buffer */
+ limeKey_t associatedKey;
+ xmlChar *peerZidHex = NULL;
+ xmlNodePtr cur;
+ uint8_t *encryptedMessage = NULL;
+ uint32_t encryptedMessageLength = 0;
+ uint32_t usedSessionIndex = 0;
+ xmlDocPtr xmlEncryptedMessage;
+
+ if (cacheBuffer == NULL) {
+ return LIME_INVALID_CACHE;
+ }
+ /* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
+ if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
+ return LIME_UNABLE_TO_DECRYPT_MESSAGE;
+ }
+ lime_strToUint8(selfZid, selfZidHex, 24);
+
+ /* parse the message into an xml doc */
+ /* make sure we have a valid xml message before trying to parse it */
+ if (memcmp(message, "", 38) != 0 ) {
+ return LIME_INVALID_ENCRYPTED_MESSAGE;
+ }
+ xmlEncryptedMessage = xmlParseDoc((const xmlChar *)message);
+ if (xmlEncryptedMessage == NULL) {
+ return LIME_INVALID_ENCRYPTED_MESSAGE;
+ }
+
+ /* retrieve the sender ZID which is the first child of root */
+ cur = xmlDocGetRootElement(xmlEncryptedMessage);
+ if (cur != NULL) {
+ cur = cur->xmlChildrenNode;
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)"ZID"))){ /* sender ZID found, extract it */
+ peerZidHex = xmlNodeListGetString(xmlEncryptedMessage, cur->xmlChildrenNode, 1);
+ /* convert it from hexa string to bytes string and set the result in the associatedKey structure */
+ lime_strToUint8(associatedKey.peerZID, peerZidHex, strlen((char *)peerZidHex));
+ cur = cur->next;
+ }
+ }
+
+ if (peerZidHex != NULL) {
+ /* get from cache the matching key */
+ retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
+
+ if (retval != 0) {
+ xmlFree(peerZidHex);
+ xmlFreeDoc(xmlEncryptedMessage);
+ return retval;
+ }
+
+ /* retrieve the portion of message which is encrypted with our key */
+ while (cur != NULL) { /* loop on all "msg" node in the message */
+ xmlNodePtr msgChildrenNode = cur->xmlChildrenNode;
+ xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1); /* pZID is the first element of msg */
+ if (!xmlStrcmp(currentZidHex, (const xmlChar *)selfZidHex)) { /* we found the msg node we are looking for */
+ /* get the index (second node in the msg one) */
+ xmlChar *sessionIndexHex;
+ xmlChar *encryptedMessageb64;
+
+ msgChildrenNode = msgChildrenNode->next;
+ sessionIndexHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
+ usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
+ | (((uint32_t)lime_charToByte(sessionIndexHex[7])));
+ xmlFree(sessionIndexHex);
+ /* get the encrypted message */
+ msgChildrenNode = msgChildrenNode->next;
+ /* convert the cipherText from base 64 */
+ encryptedMessageb64 = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
+ encryptedMessageLength = b64_decode((char *)encryptedMessageb64, strlen((char *)encryptedMessageb64), NULL, 0);
+ encryptedMessage = (uint8_t *)malloc(encryptedMessageLength);
+ encryptedMessageLength = b64_decode((char *)encryptedMessageb64, strlen((char *)encryptedMessageb64), encryptedMessage, encryptedMessageLength);
+ xmlFree(encryptedMessageb64);
+ }
+
+ cur = cur->next;
+ xmlFree(currentZidHex);
+ }
+ }
+
+ xmlFree(peerZidHex);
+ xmlFreeDoc(xmlEncryptedMessage);
+
+ /* do we have retrieved correctly all the needed data */
+ if (encryptedMessage == NULL) {
+ return LIME_UNABLE_TO_DECRYPT_MESSAGE;
+ }
+
+ /* shall we derive our key before going for decryption */
+ if (usedSessionIndex < associatedKey.sessionIndex) {
+ /* something wen't wrong with the cache, this shall never happend */
+ free(encryptedMessage);
+ return LIME_UNABLE_TO_DECRYPT_MESSAGE;
+ }
+
+ if ((usedSessionIndex - associatedKey.sessionIndex > MAX_DERIVATION_NUMBER) ) {
+ /* we missed to many messages, ask for a cache reset via a ZRTP call */
+ free(encryptedMessage);
+ return LIME_UNABLE_TO_DECRYPT_MESSAGE;
+ }
+
+ while (usedSessionIndex>associatedKey.sessionIndex) {
+ lime_deriveKey(&associatedKey);
+ }
+
+ /* decrypt the message */
+ *output = (uint8_t *)malloc(encryptedMessageLength - 16 +1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
+ retval = lime_decryptMessage(&associatedKey, encryptedMessage, encryptedMessageLength, selfZid, *output);
+
+ free(encryptedMessage);
+
+ if (retval!=0 ) {
+ free(*output);
+ *output = NULL;
+ return LIME_UNABLE_TO_DECRYPT_MESSAGE;
+ }
+
+ /* update used key */
+ lime_deriveKey(&associatedKey);
+ lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);
+
+ return 0;
+}
+
+
+#else /* HAVE_LIME */
+
+bool_t lime_is_available() { return FALSE; }
+int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { return LIME_NOT_ENABLED;}
+int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) { return LIME_NOT_ENABLED;}
+int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
+int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {return LIME_NOT_ENABLED;}
+
+
+#endif /* HAVE_LIME */
+
+char *lime_error_code_to_string(int errorCode) {
+ switch (errorCode) {
+ case LIME_INVALID_CACHE: return "Invalid ZRTP cache";
+ case LIME_UNABLE_TO_DERIVE_KEY: return "Unable to derive Key";
+ case LIME_UNABLE_TO_ENCRYPT_MESSAGE: return "Unable to encrypt message";
+ case LIME_UNABLE_TO_DECRYPT_MESSAGE: return "Unable to decrypt message";
+ case LIME_NO_VALID_KEY_FOUND_FOR_PEER: return "No valid key found";
+ case LIME_INVALID_ENCRYPTED_MESSAGE: return "Invalid encrypted message";
+ case LIME_NOT_ENABLED: return "Lime not enabled at build";
+ }
+ return "Unknow error";
+
+}
diff --git a/coreapi/lime.h b/coreapi/lime.h
new file mode 100644
index 000000000..c196a6410
--- /dev/null
+++ b/coreapi/lime.h
@@ -0,0 +1,186 @@
+#ifndef LIME_H
+#define LIME_H
+
+#define LIME_INVALID_CACHE 0x1001
+#define LIME_UNABLE_TO_DERIVE_KEY 0x1002
+#define LIME_UNABLE_TO_ENCRYPT_MESSAGE 0x1004
+#define LIME_UNABLE_TO_DECRYPT_MESSAGE 0x1008
+#define LIME_NO_VALID_KEY_FOUND_FOR_PEER 0x1010
+#define LIME_INVALID_ENCRYPTED_MESSAGE 0x1020
+#define LIME_NOT_ENABLED 0x1100
+
+/* this define the maximum key derivation number allowed to get the caches back in sync in case of missed messages */
+#define MAX_DERIVATION_NUMBER 100
+
+#define LIME_SENDER 0x01
+#define LIME_RECEIVER 0x02
+#include
+#include
+#include
+#include
+
+#include
+
+#ifndef LINPHONE_PUBLIC
+#define LINPHONE_PUBLIC MS2_PUBLIC
+#endif
+
+/**
+ * @brief Structure holding all needed material to encrypt/decrypt Messages */
+typedef struct limeKey_struct {
+ uint8_t key[32]; /**< a 256 bit key used to encrypt/decrypt message */
+ uint8_t sessionId[32]; /**< a session id used to derive key */
+ uint32_t sessionIndex; /**< an index to count number of derivation */
+ uint8_t peerZID[12]; /**< the ZID associated to this key */
+} limeKey_t;
+
+/**
+ * @brief Store the differents keys associated to a sipURI */
+typedef struct limeURIKeys_struct {
+ limeKey_t **peerKeys; /**< an array of all the key material associated to each ZID matching the specified URI */
+ uint16_t associatedZIDNumber; /**< previous array length */
+ uint8_t *peerURI; /**< the sip URI associated to all the keys, must be a null terminated string */
+} limeURIKeys_t;
+
+/**
+ * @brief Get from cache all the senders keys associated to the given URI
+ * peerKeys field from associatedKeys param must be NULL when calling this function.
+ * Structure content must then be freed using lime_freeKeys function
+ *
+ * @param[in] cacheBuffer The xmlDoc containing current cache
+ * @param[in/out] associatedKeys Structure containing the peerURI. After this call contains all key material associated to the given URI. Must be then freed through lime_freeKeys function
+ *
+ * @return 0 on success, error code otherwise
+ */
+LINPHONE_PUBLIC int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys);
+
+/**
+ * @brief Get the receiver key associated to the ZID given in the associatedKey parameter
+ *
+ * @param[in] cacheBuffer The xmlDoc containing current cache
+ * @param[in/out] associatedKey Structure containing the peerZID and will store the retrieved key
+ *
+ * @return 0 on success, error code otherwise
+ */
+LINPHONE_PUBLIC int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey);
+
+/**
+ * @brief Set in cache the given key material, association is made by ZID contained in the associatedKey parameter
+ *
+ * @param[out] cacheBuffer The xmlDoc containing current cache to be updated
+ * @param[in/out] associatedKey Structure containing the key and ZID to identify the peer node to be updated
+ * @param[in] role Can be LIME_SENDER or LIME_RECEIVER, specify which key we want to update
+ *
+ * @return 0 on success, error code otherwise
+ */
+
+LINPHONE_PUBLIC int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role);
+
+/**
+ * @brief Free all allocated data in the associated keys structure
+ * Note, this will also free the peerURI string which then must have been allocated
+ *
+ * @param[in/out] associatedKeys The structure to be cleaned
+ *
+ */
+LINPHONE_PUBLIC void lime_freeKeys(limeURIKeys_t associatedKeys);
+
+/**
+ * @brief encrypt a message with the given key
+ *
+ * @param[in] key Key to use: first 192 bits are used as key, last 64 bits as init vector
+ * @param[in] message The string to be encrypted
+ * @param[in] messageLength The length in bytes of the message to be encrypted
+ * @param[in] selfZID The self ZID is use in authentication tag computation
+ * @param[out] encryptedMessage A buffer to hold the output, ouput length is input's one + 16 for the authentication tag
+ * Authentication tag is set at the begining of the encrypted Message
+ *
+ * @return 0 on success, error code otherwise
+ *
+ */
+LINPHONE_PUBLIC int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
+
+/**
+ * @brief Encrypt a file before transfering it to the server, encryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
+ *
+ * @param[in/out] cryptoContext The context used to encrypt the file using AES-GCM. Is created at first call(if null)
+ * @param[in] key 256 bits : 192 bits of key || 64 bits of Initial Vector
+ * @param[in] length Length of data to be encrypted, if 0 it will conclude the encryption
+ * @param[in] plain Plain data to be encrypted (length bytes)
+ * @param[out] cipher Output to a buffer allocated by caller, at least length bytes available
+ *
+ * @return 0 on success, error code otherwise
+ *
+ */
+LINPHONE_PUBLIC int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher);
+
+/**
+ * @brief Decrypt a file retrieved from server, decryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
+ *
+ * @param[in/out] cryptoContext The context used to decrypt the file using AES-GCM. Is created at first call(if null)
+ * @param[in] key 256 bits : 192 bits of key || 64 bits of Initial Vector
+ * @param[in] length Length of data to be decrypted, if 0 it will conclude the decryption
+ * @param[out] plain Output to a buffer allocated by caller, at least length bytes available
+ * @param[in] cipher Cipher text to be decrypted(length bytes)
+ *
+ * @return 0 on success, error code otherwise
+ *
+ */
+LINPHONE_PUBLIC int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher);
+
+/**
+ * @brief decrypt and authentify a message with the given key
+ *
+ * @param[in] key Key to use: first 192 bits are used as key, last 64 bits as init vector
+ * @param[in] message The string to be decrypted
+ * @param[in] messageLength The length in bytes of the message to be decrypted (this include the 16 bytes tag at the begining of the message)
+ * @param[in] selfZID The self ZID is use in authentication tag computation
+ * @param[out] plainMessage A buffer to hold the output, ouput length is input's one - 16 for the authentication tag + 1 for null termination char
+ * Authentication tag is retrieved at the begining of the encrypted Message
+ *
+ * @return 0 on success, error code otherwise
+ *
+ */
+
+LINPHONE_PUBLIC int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage);
+
+/**
+ * @brief create the encrypted multipart xml message from plain text and destination URI
+ * Retrieve in cache the needed keys which are then updated. Output buffer is allocated and must be freed by caller
+ *
+ * @param[in/out] cacheBuffer The xmlDoc containing current cache, get the keys and selfZID from it, updated by this function with derivated keys
+ * @param[in] message The plain text message to be encrypted
+ * @param[in] peerURI The destination URI, associated keys will be found in cache
+ * @param[out] output The output buffer, allocated and set with the encrypted message xml body(null terminated string). Must be freed by caller
+ *
+ * @return 0 on success, error code otherwise
+ */
+LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output);
+
+/**
+ * @brief decrypt a multipart xml message
+ * Retrieve in cache the needed key which is then updated. Output buffer is allocated and must be freed by caller
+ *
+ * @param[in/out] cacheBuffer The xmlDoc containing current cache, get the key and selfZID from it, updated by this function with derivated keys
+ * @param[in] message The multipart message, contain one or several part identified by destination ZID, one shall match the self ZID retrieved from cache
+ * @param[out] output The output buffer, allocated and set with the decrypted message(null terminated string). Must be freed by caller
+ *
+ * @return 0 on success, error code otherwise
+ */
+
+LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output);
+
+/**
+ * @brief given a readable version of error code generated by Lime functions
+ * @param[in] errorCode The error code
+ * @return a string containing the error description
+ */
+LINPHONE_PUBLIC char *lime_error_code_to_string(int errorCode);
+
+/**
+ * @brief Check if Lime was enabled at build time
+ *
+ * @return TRUE if Lime is available, FALSE if not
+ */
+LINPHONE_PUBLIC bool_t lime_is_available();
+#endif /* LIME_H */
diff --git a/coreapi/linphone_proxy_config.h b/coreapi/linphone_proxy_config.h
new file mode 100644
index 000000000..127564504
--- /dev/null
+++ b/coreapi/linphone_proxy_config.h
@@ -0,0 +1,523 @@
+/*
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef LINPHONE_PROXY_CONFIG_H
+#define LINPHONE_PROXY_CONFIG_H
+
+/**
+ * @addtogroup proxies
+ * @{
+**/
+
+/**
+ * Creates an empty proxy config.
+ * @deprecated, use #linphone_core_create_proxy_config instead
+**/
+LINPHONE_PUBLIC LinphoneProxyConfig *linphone_proxy_config_new(void);
+
+/**
+ * Acquire a reference to the proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The same proxy config.
+**/
+LINPHONE_PUBLIC LinphoneProxyConfig *linphone_proxy_config_ref(LinphoneProxyConfig *cfg);
+
+/**
+ * Release reference to the proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_unref(LinphoneProxyConfig *cfg);
+
+/**
+ * Retrieve the user pointer associated with the proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The user pointer associated with the proxy config.
+**/
+LINPHONE_PUBLIC void *linphone_proxy_config_get_user_data(const LinphoneProxyConfig *cfg);
+
+/**
+ * Assign a user pointer to the proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] ud The user pointer to associate with the proxy config.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_user_data(LinphoneProxyConfig *cfg, void *ud);
+
+/**
+ * Sets the proxy address
+ *
+ * Examples of valid sip proxy address are:
+ * - IP address: sip:87.98.157.38
+ * - IP address with port: sip:87.98.157.38:5062
+ * - hostnames : sip:sip.example.net
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *cfg, const char *server_addr);
+
+/**
+ * @deprecated Use linphone_proxy_config_set_identity_address()
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_set_identity(LinphoneProxyConfig *cfg, const char *identity);
+
+/**
+ * Sets the user identity as a SIP address.
+ *
+ * This identity is normally formed with display name, username and domain, such
+ * as:
+ * Alice
+ * The REGISTER messages will have from and to set to this identity.
+ *
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_set_identity_address(LinphoneProxyConfig *cfg, const LinphoneAddress *identity);
+
+/**
+ * Sets a SIP route.
+ * When a route is set, all outgoing calls will go to the route's destination if this proxy
+ * is the default one (see linphone_core_set_default_proxy() ).
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_set_route(LinphoneProxyConfig *cfg, const char *route);
+
+/**
+ * Sets the registration expiration time in seconds.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_expires(LinphoneProxyConfig *cfg, int expires);
+
+#define linphone_proxy_config_expires linphone_proxy_config_set_expires
+
+/**
+ * Indicates either or not, REGISTRATION must be issued for this #LinphoneProxyConfig .
+ * In case this #LinphoneProxyConfig has been added to #LinphoneCore, follows the linphone_proxy_config_edit() rule.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param val if true, registration will be engaged
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_enable_register(LinphoneProxyConfig *cfg, bool_t val);
+
+#define linphone_proxy_config_enableregister linphone_proxy_config_enable_register
+
+/**
+ * Starts editing a proxy configuration.
+ *
+ * Because proxy configuration must be consistent, applications MUST
+ * call linphone_proxy_config_edit() before doing any attempts to modify
+ * proxy configuration (such as identity, proxy address and so on).
+ * Once the modifications are done, then the application must call
+ * linphone_proxy_config_done() to commit the changes.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_edit(LinphoneProxyConfig *cfg);
+
+/**
+ * Commits modification made to the proxy configuration.
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_done(LinphoneProxyConfig *cfg);
+
+/**
+ * Indicates either or not, PUBLISH must be issued for this #LinphoneProxyConfig .
+ * In case this #LinphoneProxyConfig has been added to #LinphoneCore, follows the linphone_proxy_config_edit() rule.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param val if true, publish will be engaged
+ *
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_enable_publish(LinphoneProxyConfig *cfg, bool_t val);
+
+/**
+ * Set the publish expiration time in second.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param expires in second
+ * */
+LINPHONE_PUBLIC void linphone_proxy_config_set_publish_expires(LinphoneProxyConfig *cfg, int expires);
+
+/**
+ * get the publish expiration time in second. Default value is the registration expiration value.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return expires in second
+ * */
+LINPHONE_PUBLIC int linphone_proxy_config_get_publish_expires(const LinphoneProxyConfig *cfg);
+
+/**
+ * Sets whether liblinphone should replace "+" by international calling prefix in dialed numbers (passed to
+ * #linphone_core_invite ).
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val);
+
+/**
+ * Sets a dialing prefix to be automatically prepended when inviting a number with
+ * linphone_core_invite();
+ * This dialing prefix shall usually be the country code of the country where the user is living, without "+".
+ *
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix);
+
+ /**
+ * Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] enable True to sotre quality statistics and sent them to the collector, false to disable it.
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t enable);
+
+/**
+ * Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return True if quality repotring is enabled, false otherwise.
+ */
+LINPHONE_PUBLIC bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg);
+
+ /**
+ * Set the route of the collector end-point when using quality reporting. This SIP address
+ * should be used on server-side to process packets directly before discarding packets. Collector address
+ * should be a non existing account and will not receive any messages.
+ * If NULL, reports will be send to the proxy domain.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] collector route of the collector end-point, if NULL PUBLISH will be sent to the proxy domain.
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_set_quality_reporting_collector(LinphoneProxyConfig *cfg, const char *collector);
+
+ /**
+ * Get the route of the collector end-point when using quality reporting. This SIP address
+ * should be used on server-side to process packets directly before discarding packets. Collector address
+ * should be a non existing account and will not receive any messages.
+ * If NULL, reports will be send to the proxy domain.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The SIP address of the collector end-point.
+ */
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *cfg);
+
+/**
+ * Set the interval between 2 interval reports sending when using quality reporting. If call exceed interval size, an
+ * interval report will be sent to the collector. On call termination, a session report will be sent
+ * for the remaining period. Value must be 0 (disabled) or positive.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] interval The interval in seconds, 0 means interval reports are disabled.
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, uint8_t interval);
+
+/**
+ * Get the interval between interval reports when using quality reporting.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The interval in seconds, 0 means interval reports are disabled.
+ */
+
+LINPHONE_PUBLIC int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg);
+
+/**
+ * Get the registration state of the given proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The registration state of the proxy config.
+**/
+LINPHONE_PUBLIC LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return a boolean indicating that the user is sucessfully registered on the proxy.
+ * @deprecated Use linphone_proxy_config_get_state() instead.
+**/
+LINPHONE_PUBLIC bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get the domain name of the given proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The domain name of the proxy config.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get the realm of the given proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The realm of the proxy config.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_realm(const LinphoneProxyConfig *cfg);
+
+/**
+ * Set the realm of the given proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] realm New realm value.
+ * @return The realm of the proxy config.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_realm(LinphoneProxyConfig *cfg, const char * realm);
+
+/**
+ * @return the route set for this proxy configuration.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return the SIP identity that belongs to this proxy configuration.
+**/
+LINPHONE_PUBLIC const LinphoneAddress *linphone_proxy_config_get_identity_address(const LinphoneProxyConfig *cfg);
+
+/**
+ * @deprecated use linphone_proxy_config_get_identity_address()
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_identity(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return TRUE if PUBLISH request is enabled for this proxy.
+**/
+LINPHONE_PUBLIC bool_t linphone_proxy_config_publish_enabled(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return the proxy's SIP address.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_server_addr(const LinphoneProxyConfig *cfg);
+
+#define linphone_proxy_config_get_addr linphone_proxy_config_get_server_addr
+
+/**
+ * @return the duration of registration.
+**/
+LINPHONE_PUBLIC int linphone_proxy_config_get_expires(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return TRUE if registration to the proxy is enabled.
+**/
+LINPHONE_PUBLIC bool_t linphone_proxy_config_register_enabled(const LinphoneProxyConfig *cfg);
+
+/**
+ * Refresh a proxy registration.
+ * This is useful if for example you resuming from suspend, thus IP address may have changed.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_refresh_register(LinphoneProxyConfig *cfg);
+
+/**
+ * Prevent a proxy config from refreshing its registration.
+ * This is useful to let registrations to expire naturally (or) when the application wants to keep control on when
+ * refreshes are sent.
+ * However, linphone_core_set_network_reachable(lc,TRUE) will always request the proxy configs to refresh their registrations.
+ * The refreshing operations can be resumed with linphone_proxy_config_refresh_register().
+ * @param[in] cfg #LinphoneProxyConfig object.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_pause_register(LinphoneProxyConfig *cfg);
+
+LINPHONE_PUBLIC const LinphoneAddress* linphone_proxy_config_get_contact(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return previously set contact parameters.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_contact_parameters(const LinphoneProxyConfig *cfg);
+
+/**
+ * Set optional contact parameters that will be added to the contact information sent in the registration.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param contact_params a string contaning the additional parameters in text form, like "myparam=something;myparam2=something_else"
+ *
+ * The main use case for this function is provide the proxy additional information regarding the user agent, like for example unique identifier or apple push id.
+ * As an example, the contact address in the SIP register sent will look like ;apple-push-id=43143-DFE23F-2323-FA2232.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_contact_parameters(LinphoneProxyConfig *cfg, const char *contact_params);
+
+/**
+ * Set optional contact parameters that will be added to the contact information sent in the registration, inside the URI.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param contact_uri_params a string containing the additional parameters in text form, like "myparam=something;myparam2=something_else"
+ *
+ * The main use case for this function is provide the proxy additional information regarding the user agent, like for example unique identifier or apple push id.
+ * As an example, the contact address in the SIP register sent will look like .
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_contact_uri_parameters(LinphoneProxyConfig *cfg, const char *contact_uri_params);
+
+/**
+ * @return previously set contact URI parameters.
+**/
+LINPHONE_PUBLIC const char* linphone_proxy_config_get_contact_uri_parameters(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get the #LinphoneCore object to which is associated the #LinphoneProxyConfig.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The #LinphoneCore object to which is associated the #LinphoneProxyConfig.
+**/
+LINPHONE_PUBLIC LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return whether liblinphone should replace "+" by "00" in dialed numbers (passed to
+ * #linphone_core_invite ).
+ *
+**/
+LINPHONE_PUBLIC bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg);
+
+/**
+ * @return dialing prefix.
+**/
+LINPHONE_PUBLIC const char * linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get the reason why registration failed when the proxy config state is LinphoneRegistrationFailed.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The reason why registration failed for this proxy config.
+**/
+LINPHONE_PUBLIC LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get detailed information why registration failed when the proxy config state is LinphoneRegistrationFailed.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The details why registration failed for this proxy config.
+**/
+LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_proxy_config_get_error_info(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get the transport from either service route, route or addr.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The transport as a string (I.E udp, tcp, tls, dtls)
+**/
+LINPHONE_PUBLIC const char* linphone_proxy_config_get_transport(const LinphoneProxyConfig *cfg);
+
+/**
+ * Destroys a proxy config.
+ * @deprecated
+ *
+ * @note: LinphoneProxyConfig that have been removed from LinphoneCore with
+ * linphone_core_remove_proxy_config() must not be freed.
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_destroy(LinphoneProxyConfig *cfg);
+
+LINPHONE_PUBLIC void linphone_proxy_config_set_sip_setup(LinphoneProxyConfig *cfg, const char *type);
+LINPHONE_PUBLIC SipSetupContext *linphone_proxy_config_get_sip_setup_context(LinphoneProxyConfig *cfg);
+LINPHONE_PUBLIC SipSetup *linphone_proxy_config_get_sip_setup(LinphoneProxyConfig *cfg);
+
+/**
+ * Detect if the given input is a phone number or not.
+ * @param proxy #LinphoneProxyConfig argument, unused yet but may contain useful data. Can be NULL.
+ * @param username string to parse.
+ * @return TRUE if input is a phone number, FALSE otherwise.
+**/
+LINPHONE_PUBLIC bool_t linphone_proxy_config_is_phone_number(LinphoneProxyConfig *proxy, const char *username);
+
+/**
+ * Normalize a human readable phone number into a basic string. 888-444-222 becomes 888444222
+ * or +33888444222 depending on the #LinphoneProxyConfig object. However this argument is OPTIONNAL
+ * and if not provided, a default one will be used.
+ * This function will always generate a normalized username; if input is not a phone number, output will be a copy of input.
+ * @param proxy #LinphoneProxyConfig object containing country code and/or escape symbol. If NULL passed, will use default configuration.
+ * @param username the string to parse
+ * @param result the newly normalized number
+ * @param result_len the size of the normalized number \a result
+ * @return TRUE if a phone number was recognized, FALSE otherwise.
+ */
+LINPHONE_PUBLIC bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len);
+
+/**
+ * Same objective as linphone_proxy_config_normalize_number but allocates a new string
+ * @param proxy #LinphoneProxyConfig object containing country code and/or escape symbol. If NULL passed, will use default configuration.
+ * @param username the string to parse
+ * @return NULL if invalid phone number, normalized phone number from username input otherwise.
+*/
+LINPHONE_PUBLIC char* linphone_proxy_config_normalize_phone_number(LinphoneProxyConfig *proxy, const char *username);
+
+/**
+ * Normalize a human readable sip uri into a fully qualified LinphoneAddress.
+ * A sip address should look like DisplayName \ .
+ * Basically this function performs the following tasks
+ * - if a phone number is entered, prepend country prefix and eventually escape the '+' by 00 of the proxy config.
+ * - if no domain part is supplied, append the domain name of the proxy config. Returns NULL if no proxy is provided at this point.
+ * - if no sip: is present, prepend it.
+ *
+ * The result is a syntactically correct SIP address.
+ * @param proxy #LinphoneProxyConfig object containing country code, escape symbol and/or domain name. Can be NULL if domain is already provided.
+ * @param username the string to parse
+ * @return NULL if invalid input, normalized sip address otherwise.
+*/
+LINPHONE_PUBLIC LinphoneAddress* linphone_proxy_config_normalize_sip_uri(LinphoneProxyConfig *proxy, const char *username);
+
+/**
+ * Set default privacy policy for all calls routed through this proxy.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param privacy LinphonePrivacy to configure privacy
+ * */
+LINPHONE_PUBLIC void linphone_proxy_config_set_privacy(LinphoneProxyConfig *cfg, LinphonePrivacyMask privacy);
+
+/**
+ * Get default privacy policy for all calls routed through this proxy.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return Privacy mode
+ * */
+LINPHONE_PUBLIC LinphonePrivacyMask linphone_proxy_config_get_privacy(const LinphoneProxyConfig *cfg);
+
+/**
+ * Set the http file transfer server to be used for content type application/vnd.gsma.rcs-ft-http+xml
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param server_url URL of the file server like https://file.linphone.org/upload.php
+ * */
+LINPHONE_PUBLIC void linphone_proxy_config_set_file_transfer_server(LinphoneProxyConfig *cfg, const char * server_url);
+
+/**
+ * Get the http file transfer server to be used for content type application/vnd.gsma.rcs-ft-http+xml
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return URL of the file server like https://file.linphone.org/upload.php
+ * */
+LINPHONE_PUBLIC const char* linphone_proxy_config_get_file_transfer_server(const LinphoneProxyConfig *cfg);
+
+/**
+ * Indicates whether AVPF/SAVPF must be used for calls using this proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] enable True to enable AVPF/SAVF, false to disable it.
+ * @deprecated use linphone_proxy_config_set_avpf_mode()
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_enable_avpf(LinphoneProxyConfig *cfg, bool_t enable);
+
+/**
+ * Indicates whether AVPF/SAVPF is being used for calls using this proxy config.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return True if AVPF/SAVPF is enabled, false otherwise.
+ * @deprecated use linphone_proxy_config_set_avpf_mode()
+ */
+LINPHONE_PUBLIC bool_t linphone_proxy_config_avpf_enabled(LinphoneProxyConfig *cfg);
+
+/**
+ * Set the interval between regular RTCP reports when using AVPF/SAVPF.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] interval The interval in seconds (between 0 and 5 seconds).
+ */
+LINPHONE_PUBLIC void linphone_proxy_config_set_avpf_rr_interval(LinphoneProxyConfig *cfg, uint8_t interval);
+
+/**
+ * Get the interval between regular RTCP reports when using AVPF/SAVPF.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return The interval in seconds.
+ */
+LINPHONE_PUBLIC uint8_t linphone_proxy_config_get_avpf_rr_interval(const LinphoneProxyConfig *cfg);
+
+/**
+ * Get enablement status of RTCP feedback (also known as AVPF profile).
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @return the enablement mode, which can be LinphoneAVPFDefault (use LinphoneCore's mode), LinphoneAVPFEnabled (avpf is enabled), or LinphoneAVPFDisabled (disabled).
+**/
+LINPHONE_PUBLIC LinphoneAVPFMode linphone_proxy_config_get_avpf_mode(const LinphoneProxyConfig *cfg);
+
+/**
+ * Enable the use of RTCP feedback (also known as AVPF profile).
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param[in] mode the enablement mode, which can be LinphoneAVPFDefault (use LinphoneCore's mode), LinphoneAVPFEnabled (avpf is enabled), or LinphoneAVPFDisabled (disabled).
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_avpf_mode(LinphoneProxyConfig *cfg, LinphoneAVPFMode mode);
+
+/**
+ * Obtain the value of a header sent by the server in last answer to REGISTER.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param header_name the header name for which to fetch corresponding value
+ * @return the value of the queried header.
+**/
+LINPHONE_PUBLIC const char *linphone_proxy_config_get_custom_header(LinphoneProxyConfig *cfg, const char *header_name);
+
+/**
+ * Set the value of a custom header sent to the server in REGISTERs request.
+ * @param[in] cfg #LinphoneProxyConfig object.
+ * @param header_name the header name
+ * @param header_value the header's value
+**/
+LINPHONE_PUBLIC void linphone_proxy_config_set_custom_header(LinphoneProxyConfig *cfg, const char *header_name, const char *header_value);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/coreapi/linphone_tunnel.cc b/coreapi/linphone_tunnel.cc
index f0de56694..7dd27d890 100644
--- a/coreapi/linphone_tunnel.cc
+++ b/coreapi/linphone_tunnel.cc
@@ -29,7 +29,7 @@
#include "private.h"
#include "lpconfig.h"
-LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){
+LinphoneTunnel* linphone_core_get_tunnel(const LinphoneCore *lc){
return lc->tunnel;
}
@@ -44,31 +44,37 @@ extern "C" LinphoneTunnel* linphone_core_tunnel_new(LinphoneCore *lc){
return tunnel;
}
-static inline belledonnecomm::TunnelManager *bcTunnel(LinphoneTunnel *tunnel){
+belledonnecomm::TunnelManager *bcTunnel(const LinphoneTunnel *tunnel){
return tunnel->manager;
}
-static inline _LpConfig *config(LinphoneTunnel *tunnel){
+static inline _LpConfig *config(const LinphoneTunnel *tunnel){
return tunnel->manager->getLinphoneCore()->config;
}
void linphone_tunnel_destroy(LinphoneTunnel *tunnel){
delete tunnel->manager;
+
+ ms_list_free_with_data(tunnel->config_list, (void (*)(void *))linphone_tunnel_config_destroy);
+
ms_free(tunnel);
}
static char *linphone_tunnel_config_to_string(const LinphoneTunnelConfig *tunnel_config) {
char *str = NULL;
- if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) != -1) {
- str = ms_strdup_printf("%s:%d:%d:%d",
- linphone_tunnel_config_get_host(tunnel_config),
- linphone_tunnel_config_get_port(tunnel_config),
- linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
- linphone_tunnel_config_get_delay(tunnel_config));
- } else {
- str = ms_strdup_printf("%s:%d",
- linphone_tunnel_config_get_host(tunnel_config),
- linphone_tunnel_config_get_port(tunnel_config));
+ const char *host = linphone_tunnel_config_get_host(tunnel_config);
+ if(host != NULL) {
+ if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) != -1) {
+ str = ms_strdup_printf("%s:%d:%d:%d",
+ linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config),
+ linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
+ linphone_tunnel_config_get_delay(tunnel_config));
+ } else {
+ str = ms_strdup_printf("%s:%d",
+ linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config));
+ }
}
return str;
}
@@ -96,12 +102,12 @@ static LinphoneTunnelConfig *linphone_tunnel_config_from_string(const char *str)
break;
case 3:
delay = atoi(pch);
- break;
+ break;
default:
// Abort
pos = 0;
break;
-
+
}
++pos;
pch = strtok(NULL, ":");
@@ -117,26 +123,27 @@ static LinphoneTunnelConfig *linphone_tunnel_config_from_string(const char *str)
if(pos == 4) {
linphone_tunnel_config_set_delay(tunnel_config, delay);
}
- ms_free(dstr);
+ ms_free(dstr);
return tunnel_config;
}
-static void linphone_tunnel_save_config(LinphoneTunnel *tunnel) {
- MSList *elem = tunnel->config_list;
+static void linphone_tunnel_save_config(const LinphoneTunnel *tunnel) {
+ MSList *elem = NULL;
char *tmp = NULL, *old_tmp = NULL, *tc_str = NULL;
- while(elem != NULL) {
+ for(elem = tunnel->config_list; elem != NULL; elem = elem->next) {
LinphoneTunnelConfig *tunnel_config = (LinphoneTunnelConfig *)elem->data;
tc_str = linphone_tunnel_config_to_string(tunnel_config);
- if(tmp != NULL) {
- old_tmp = tmp;
- tmp = ms_strdup_printf("%s %s", old_tmp, tc_str);
- ms_free(old_tmp);
- ms_free(tc_str);
- } else {
- tmp = tc_str;
+ if(tc_str != NULL) {
+ if(tmp != NULL) {
+ old_tmp = tmp;
+ tmp = ms_strdup_printf("%s %s", old_tmp, tc_str);
+ ms_free(old_tmp);
+ ms_free(tc_str);
+ } else {
+ tmp = tc_str;
+ }
}
- elem = elem->next;
}
lp_config_set_string(config(tunnel), "tunnel", "server_addresses", tmp);
if(tmp != NULL) {
@@ -147,12 +154,12 @@ static void linphone_tunnel_save_config(LinphoneTunnel *tunnel) {
static void linphone_tunnel_add_server_intern(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config) {
if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) == -1) {
- bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
+ bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
linphone_tunnel_config_get_port(tunnel_config));
} else {
- bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
- linphone_tunnel_config_get_port(tunnel_config),
- linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
+ bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config),
+ linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
linphone_tunnel_config_get_delay(tunnel_config));
}
tunnel->config_list = ms_list_append(tunnel->config_list, tunnel_config);
@@ -204,33 +211,41 @@ void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig
MSList *elem = ms_list_find(tunnel->config_list, tunnel_config);
if(elem != NULL) {
tunnel->config_list = ms_list_remove(tunnel->config_list, tunnel_config);
- linphone_tunnel_config_destroy(tunnel_config);
+ linphone_tunnel_config_destroy(tunnel_config);
linphone_tunnel_refresh_config(tunnel);
linphone_tunnel_save_config(tunnel);
- }
+ }
}
-const MSList *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){
+const MSList *linphone_tunnel_get_servers(const LinphoneTunnel *tunnel){
return tunnel->config_list;
}
void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){
bcTunnel(tunnel)->cleanServers();
-
+
/* Free the list */
- ms_list_for_each(tunnel->config_list, (void (*)(void *))linphone_tunnel_config_destroy);
- tunnel->config_list = ms_list_free(tunnel->config_list);
-
+ ms_list_free_with_data(tunnel->config_list, (void (*)(void *))linphone_tunnel_config_destroy);
+ tunnel->config_list = NULL;
+
linphone_tunnel_save_config(tunnel);
}
-void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){
- lp_config_set_int(config(tunnel),"tunnel","enabled",(int)enabled);
- bcTunnel(tunnel)->enable(enabled);
+void linphone_tunnel_set_mode(LinphoneTunnel *tunnel, LinphoneTunnelMode mode){
+ lp_config_set_string(config(tunnel),"tunnel","mode", linphone_tunnel_mode_to_string(mode));
+ bcTunnel(tunnel)->setMode(mode);
}
-bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){
- return bcTunnel(tunnel)->isEnabled();
+LinphoneTunnelMode linphone_tunnel_get_mode(const LinphoneTunnel *tunnel){
+ return bcTunnel(tunnel)->getMode();
+}
+
+bool_t linphone_tunnel_connected(const LinphoneTunnel *tunnel){
+ return bcTunnel(tunnel)->isConnected();
+}
+
+bool_t linphone_tunnel_get_activated(const LinphoneTunnel *tunnel){
+ return bcTunnel(tunnel)->isActivated();
}
static OrtpLogFunc tunnelOrtpLogHandler=NULL;
@@ -306,22 +321,47 @@ void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){
bcTunnel(tunnel)->reconnect();
}
-void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){
- bcTunnel(tunnel)->autoDetect();
+void linphone_tunnel_enable_sip(LinphoneTunnel *tunnel, bool_t enable) {
+ bcTunnel(tunnel)->tunnelizeSipPackets(enable);
+ lp_config_set_int(config(tunnel), "tunnel", "sip", (enable ? TRUE : FALSE));
+}
+
+bool_t linphone_tunnel_sip_enabled(const LinphoneTunnel *tunnel) {
+ return bcTunnel(tunnel)->tunnelizeSipPacketsEnabled() ? TRUE : FALSE;
}
static void my_ortp_logv(OrtpLogLevel level, const char *fmt, va_list args){
ortp_logv(level,fmt,args);
}
+
/**
* Startup tunnel using configuration.
* Called internally from linphonecore at startup.
*/
void linphone_tunnel_configure(LinphoneTunnel *tunnel){
- bool_t enabled=(bool_t)lp_config_get_int(config(tunnel),"tunnel","enabled",FALSE);
+ LinphoneTunnelMode mode = linphone_tunnel_mode_from_string(lp_config_get_string(config(tunnel), "tunnel", "mode", NULL));
+ bool_t tunnelizeSIPPackets = (bool_t)lp_config_get_int(config(tunnel), "tunnel", "sip", TRUE);
linphone_tunnel_enable_logs_with_handler(tunnel,TRUE,my_ortp_logv);
linphone_tunnel_load_config(tunnel);
- linphone_tunnel_enable(tunnel, enabled);
+ linphone_tunnel_enable_sip(tunnel, tunnelizeSIPPackets);
+ linphone_tunnel_set_mode(tunnel, mode);
}
+/* Deprecated functions */
+void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled) {
+ if(enabled) linphone_tunnel_set_mode(tunnel, LinphoneTunnelModeEnable);
+ else linphone_tunnel_set_mode(tunnel, LinphoneTunnelModeDisable);
+}
+
+bool_t linphone_tunnel_enabled(const LinphoneTunnel *tunnel) {
+ return linphone_tunnel_get_mode(tunnel) == LinphoneTunnelModeEnable;
+}
+
+void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel) {
+ linphone_tunnel_set_mode(tunnel, LinphoneTunnelModeAuto);
+}
+
+bool_t linphone_tunnel_auto_detect_enabled(LinphoneTunnel *tunnel) {
+ return linphone_tunnel_get_mode(tunnel) == LinphoneTunnelModeAuto;
+}
diff --git a/coreapi/linphone_tunnel.h b/coreapi/linphone_tunnel.h
index e42a054dc..ecaaf2227 100644
--- a/coreapi/linphone_tunnel.h
+++ b/coreapi/linphone_tunnel.h
@@ -22,26 +22,65 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-
+
#ifndef LINPHONETUNNEL_H
#define LINPHONETUNNEL_H
#include "linphonecore.h"
+
/**
* @addtogroup tunnel
* @{
**/
/**
- * This set of methods enhance LinphoneCore functionalities in order to provide an easy to use API to
- * - provision tunnel servers ip addresses and ports. This functionality is an option not implemented under GPL.
- * - start/stop the tunneling service
- * - perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets.
+ * Linphone tunnel aims is to bypass IP traffic blocking due to aggressive firewalls which typically only authorize TCP traffic with destination port 443.
+ * Its principle is tunneling all SIP and/or RTP traffic through a single secure https connection up to a detunnelizer server.
+ * This set of methods enhance LinphoneCore functionalities in order to provide an easy to use API to
+ * \li provision tunnel servers IP addresses and ports. This functionality is an option not implemented under GPL. Availability can be check at runtime using function #linphone_core_tunnel_available
+ * \li start/stop the tunneling service
+ * \li perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets.
*
* It takes in charge automatically the SIP registration procedure when connecting or disconnecting to a tunnel server.
* No other action on LinphoneCore is required to enable full operation in tunnel mode.
- **/
+ *
+ * Provision is done using object #LinphoneTunnelConfig created by function #linphone_tunnel_config_new(). Functions #linphone_tunnel_config_set_host
+ * and #linphone_tunnel_config_set_port allow to point to tunnel server IP/port. Once set, use function #linphone_tunnel_add_server to provision a tunnel server.
+ * Finally tunnel mode configuration is achieved by function #linphone_tunnel_set_mode.
+ * Tunnel connection status can be checked using function #linphone_tunnel_connected.
+ *
+ * Bellow pseudo code that can be use to configure, enable, check state and disable tunnel functionality:
+ *
+ * \code
+ LinphoneTunnel *tunnel = linphone_core_get_tunnel(linphone_core);
+ LinphoneTunnelConfig *config=linphone_tunnel_config_new(); //instantiate tunnel configuration
+ linphone_tunnel_config_set_host(config, "tunnel.linphone.org"); //set tunnel server host address
+ linphone_tunnel_config_set_port(config, 443); //set tunnel server port
+ linphone_tunnel_add_server(tunnel, config); //provision tunnel config
+ linphone_tunnel_set_mode(tunnel, LinphoneTunnelModeEnable); //activate the tunnel unconditional
+
+ while (!linphone_tunnel_connected(tunnel)) { //wait for tunnel to be ready
+ linphone_core_iterate(linphone_core); //schedule core main loop
+ ms_sleep(100); //wait 100ms
+ }
+
+ LinphoneCall *call = linphone_core_invite(linphone_core,"sip:foo@example.org"); //place an outgoing call
+ linphone_call_ref(call); //acquire a reference on the call to avoid deletion after completion
+ //...
+ linphone_core_terminate_call(linphone_core,call);
+
+ while (linphone_call_get_state(call) != LinphoneCallReleased) { //wait for call to be in release state
+ linphone_core_iterate(linphone_core); //schedule core main loop
+ ms_sleep(100); //wait 100ms
+ }
+
+ linphone_tunnel_set_mode(tunnel, LinphoneTunnelModeDisable); //deactivate tunnel
+ linphone_call_unref(call); //release reference on the call
+
+ \endcode
+*
+* **/
#ifdef __cplusplus
extern "C"
@@ -50,161 +89,245 @@ extern "C"
typedef struct _LinphoneTunnelConfig LinphoneTunnelConfig;
+/**
+ * Enum describing the tunnel modes.
+**/
+typedef enum _LinphoneTunnelMode {
+ LinphoneTunnelModeDisable, /**< The tunnel is disabled. */
+ LinphoneTunnelModeEnable, /**< The tunnel is enabled. */
+ LinphoneTunnelModeAuto /**< The tunnel is enabled automatically if it is required. */
+} LinphoneTunnelMode;
+
+/**
+ * Convert a string into LinphoneTunnelMode enum
+ * @param string String to convert
+ * @return An LinphoneTunnelMode enum. If the passed string is NULL or
+ * does not match with any mode, the LinphoneTunnelModeDisable is returned.
+ */
+LINPHONE_PUBLIC LinphoneTunnelMode linphone_tunnel_mode_from_string(const char *string);
+
+/**
+ * Convert a tunnel mode enum into string
+ * @param mode Enum to convert
+ * @return "disable", "enable" or "auto"
+ */
+LINPHONE_PUBLIC const char *linphone_tunnel_mode_to_string(LinphoneTunnelMode mode);
+
/**
* Create a new tunnel configuration
*/
-LinphoneTunnelConfig *linphone_tunnel_config_new();
+LINPHONE_PUBLIC LinphoneTunnelConfig *linphone_tunnel_config_new(void);
/**
- * Set address of server.
- *
- * @param tunnel configuration object
- * @param host tunnel server ip address
+ * Set the IP address or hostname of the tunnel server.
+ * @param tunnel LinphoneTunnelConfig object
+ * @param host The tunnel server IP address or hostname
*/
-void linphone_tunnel_config_set_host(LinphoneTunnelConfig *tunnel, const char *host);
+LINPHONE_PUBLIC void linphone_tunnel_config_set_host(LinphoneTunnelConfig *tunnel, const char *host);
/**
- * Get address of server.
- *
- * @param tunnel configuration object
+ * Get the IP address or hostname of the tunnel server.
+ * @param tunnel LinphoneTunnelConfig object
+ * @return The tunnel server IP address or hostname
*/
-const char *linphone_tunnel_config_get_host(const LinphoneTunnelConfig *tunnel);
+LINPHONE_PUBLIC const char *linphone_tunnel_config_get_host(const LinphoneTunnelConfig *tunnel);
/**
* Set tls port of server.
- *
- * @param tunnel configuration object
- * @param port tunnel server tls port, recommended value is 443
+ * @param tunnel LinphoneTunnelConfig object
+ * @param port The tunnel server TLS port, recommended value is 443
*/
-void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunnel, int port);
+LINPHONE_PUBLIC void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunnel, int port);
/**
- * Get tls port of server.
- *
- * @param tunnel configuration object
+ * Get the TLS port of the tunnel server.
+ * @param tunnel LinphoneTunnelConfig object
+ * @return The TLS port of the tunnel server
*/
-int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel);
+LINPHONE_PUBLIC int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel);
/**
- * Set the remote port on the tunnel server side used to test udp reachability.
- *
- * @param tunnel configuration object
- * @param remote_udp_mirror_port remote port on the tunnel server side used to test udp reachability, set to -1 to disable the feature
+ * Set the remote port on the tunnel server side used to test UDP reachability.
+ * This is used when the mode is set auto, to detect whether the tunnel has to be enabled or not.
+ * @param tunnel LinphoneTunnelConfig object
+ * @param remote_udp_mirror_port The remote port on the tunnel server side used to test UDP reachability, set to -1 to disable the feature
*/
-void linphone_tunnel_config_set_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel, int remote_udp_mirror_port);
+LINPHONE_PUBLIC void linphone_tunnel_config_set_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel, int remote_udp_mirror_port);
/**
- * Get the remote port on the tunnel server side used to test udp reachability.
- *
- * @param tunnel configuration object
+ * Get the remote port on the tunnel server side used to test UDP reachability.
+ * This is used when the mode is set auto, to detect whether the tunnel has to be enabled or not.
+ * @param tunnel LinphoneTunnelConfig object
+ * @return The remote port on the tunnel server side used to test UDP reachability
*/
-int linphone_tunnel_config_get_remote_udp_mirror_port(const LinphoneTunnelConfig *tunnel);
+LINPHONE_PUBLIC int linphone_tunnel_config_get_remote_udp_mirror_port(const LinphoneTunnelConfig *tunnel);
/**
- * Set the udp packet round trip delay in ms for a tunnel configuration.
- *
- * @param tunnel configuration object
- * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms.
+ * Set the UDP packet round trip delay in ms for a tunnel configuration.
+ * @param tunnel LinphoneTunnelConfig object
+ * @param delay The UDP packet round trip delay in ms considered as acceptable (recommended value is 1000 ms).
*/
-void linphone_tunnel_config_set_delay(LinphoneTunnelConfig *tunnel, int delay);
+LINPHONE_PUBLIC void linphone_tunnel_config_set_delay(LinphoneTunnelConfig *tunnel, int delay);
/**
- * Get the udp packet round trip delay in ms for a tunnel configuration.
- *
- * @param tunnel configuration object
+ * Get the UDP packet round trip delay in ms for a tunnel configuration.
+ * @param tunnel LinphoneTunnelConfig object
+ * @return The UDP packet round trip delay in ms.
*/
-int linphone_tunnel_config_get_delay(const LinphoneTunnelConfig *tunnel);
+LINPHONE_PUBLIC int linphone_tunnel_config_get_delay(const LinphoneTunnelConfig *tunnel);
/**
* Destroy a tunnel configuration
- *
- * @param tunnel configuration object
+ * @param tunnel LinphoneTunnelConfig object
*/
-void linphone_tunnel_config_destroy(LinphoneTunnelConfig *tunnel);
+LINPHONE_PUBLIC void linphone_tunnel_config_destroy(LinphoneTunnelConfig *tunnel);
/**
- * Add tunnel server configuration
- *
- * @param tunnel object
- * @param tunnel_config object
+ * Add a tunnel server configuration.
+ * @param tunnel LinphoneTunnel object
+ * @param tunnel_config LinphoneTunnelConfig object
*/
-void linphone_tunnel_add_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config);
+LINPHONE_PUBLIC void linphone_tunnel_add_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config);
/**
- * Remove tunnel server configuration
- *
- * @param tunnel object
- * @param tunnel_config object
+ * Remove a tunnel server configuration.
+ * @param tunnel LinphoneTunnel object
+ * @param tunnel_config LinphoneTunnelConfig object
*/
-void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config);
+LINPHONE_PUBLIC void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config);
/**
- * @param tunnel object
- * returns a string of space separated list of host:port of tunnel server addresses
- * */
-const MSList *linphone_tunnel_get_servers(LinphoneTunnel *tunnel);
+ * Get added servers
+ * @param tunnel LinphoneTunnel object
+ * @return \mslist{LinphoneTunnelConfig}
+ */
+LINPHONE_PUBLIC const MSList *linphone_tunnel_get_servers(const LinphoneTunnel *tunnel);
/**
- * @param tunnel object
- * Removes all tunnel server address previously entered with addServer()
+ * Remove all tunnel server addresses previously entered with linphone_tunnel_add_server()
+ * @param tunnel LinphoneTunnel object
**/
-void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel);
+LINPHONE_PUBLIC void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel);
/**
- * Sets whether tunneling of SIP and RTP is required.
- * @param tunnel object
- * @param enabled If true enter in tunneled mode, if false exits from tunneled mode.
- * The TunnelManager takes care of refreshing SIP registration when switching on or off the tunneled mode.
- *
+ * Set the tunnel mode.
+ * The tunnel mode can be 'enable', 'disable' or 'auto'
+ * If the mode is set to 'auto', the tunnel manager will try to established an RTP session
+ * with the tunnel server on the UdpMirrorPort. If the connection fail, the tunnel is automatically
+ * activated whereas the tunnel is automatically disabled if the connection succeed.
+ * @param tunnel LinphoneTunnel object
+ * @param mode The desired LinphoneTunnelMode
**/
-void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled);
+LINPHONE_PUBLIC void linphone_tunnel_set_mode(LinphoneTunnel *tunnel, LinphoneTunnelMode mode);
/**
- * @param tunnel object
- * Returns a boolean indicating whether tunneled operation is enabled.
+ * Get the tunnel mode
+ * @param tunnel LinphoneTunnel object
+ * @return The current LinphoneTunnelMode
**/
-bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel);
+LINPHONE_PUBLIC LinphoneTunnelMode linphone_tunnel_get_mode(const LinphoneTunnel *tunnel);
/**
- * @param tunnel object
- * Forces reconnection to the tunnel server.
+ * Returns whether the tunnel is activated. If mode is set to auto, this gives indication whether the automatic detection determined
+ * that tunnel was necessary or not.
+ * @param tunnel the tunnel
+ * @return TRUE if tunnel is in use, FALSE otherwise.
+**/
+LINPHONE_PUBLIC bool_t linphone_tunnel_get_activated(const LinphoneTunnel *tunnel);
+
+
+/**
+ * Check whether the tunnel is connected
+ * @param tunnel LinphoneTunnel object
+ * @return A boolean value telling if the tunnel is connected
+**/
+LINPHONE_PUBLIC bool_t linphone_tunnel_connected(const LinphoneTunnel *tunnel);
+
+/**
+ * Force reconnection to the tunnel server.
* This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket
* won't be notified promptly that its connection is now zombie, so it is recommended to call this method that will cause
* the lost connection to be closed and new connection to be issued.
+ * @param tunnel LinphoneTunnel object
**/
-void linphone_tunnel_reconnect(LinphoneTunnel *tunnel);
+LINPHONE_PUBLIC void linphone_tunnel_reconnect(LinphoneTunnel *tunnel);
/**
- * Start tunnel need detection.
- * @param tunnel object
- * In auto detect mode, the tunnel manager try to establish a real time rtp cummunication with the tunnel server on specified port.
- * In case of success, the tunnel is automatically turned off. Otherwise, if no udp commmunication is feasible, tunnel mode is turned on.
- * Call this method each time to run the auto detection algorithm
+ * Set whether SIP packets must be directly sent to a UA or pass through the tunnel
+ * @param tunnel LinphoneTunnel object
+ * @param enable If true, SIP packets shall pass through the tunnel
*/
-void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel);
+LINPHONE_PUBLIC void linphone_tunnel_enable_sip(LinphoneTunnel *tunnel, bool_t enable);
+
+/**
+ * Check whether tunnel is set to transport SIP packets
+ * @param tunnel LinphoneTunnel object
+ * @return A boolean value telling whether SIP packets shall pass through the tunnel
+ */
+LINPHONE_PUBLIC bool_t linphone_tunnel_sip_enabled(const LinphoneTunnel *tunnel);
/**
* Set an optional http proxy to go through when connecting to tunnel server.
* @param tunnel LinphoneTunnel object
- * @param host Http proxy host.
- * @param port http proxy port.
- * @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
- * @param password optional http proxy password. Use NULL if not needed.
+ * @param host http proxy host
+ * @param port http proxy port
+ * @param username Optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
+ * @param passwd Optional http proxy password. Use NULL if not needed.
**/
-void linphone_tunnel_set_http_proxy(LinphoneTunnel *tunnel, const char *host, int port, const char* username,const char* passwd);
+LINPHONE_PUBLIC void linphone_tunnel_set_http_proxy(LinphoneTunnel *tunnel, const char *host, int port, const char* username,const char* passwd);
/**
* Retrieve optional http proxy configuration previously set with linphone_tunnel_set_http_proxy().
* @param tunnel LinphoneTunnel object
- * @param host Http proxy host.
- * @param port http proxy port.
- * @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
- * @param password optional http proxy password. Use NULL if not needed.
+ * @param host http proxy host
+ * @param port http proxy port
+ * @param username Optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
+ * @param passwd Optional http proxy password. Use NULL if not needed.
**/
-void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd);
+LINPHONE_PUBLIC void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd);
-void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel*tunnel, const char* username,const char* passwd);
+/**
+ * Set authentication info for the http proxy
+ * @param tunnel LinphoneTunnel object
+ * @param username User name
+ * @param passwd Password
+ */
+LINPHONE_PUBLIC void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel*tunnel, const char* username,const char* passwd);
+/**
+ * Sets whether tunneling of SIP and RTP is required.
+ * @param tunnel object
+ * @param enabled If true enter in tunneled mode, if false exits from tunneled mode.
+ * The TunnelManager takes care of refreshing SIP registration when switching on or off the tunneled mode.
+ * @deprecated Replaced by linphone_tunnel_set_mode()
+**/
+LINPHONE_PUBLIC void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled);
+
+/**
+ * Check whether tunnel is enabled
+ * @param tunnel Tunnel object
+ * @return Returns a boolean indicating whether tunneled operation is enabled.
+ * @deprecated Replaced by linphone_tunnel_get_mode()
+**/
+LINPHONE_PUBLIC bool_t linphone_tunnel_enabled(const LinphoneTunnel *tunnel);
+
+/**
+ * Start tunnel need detection.
+ * @param tunnel object
+ * In auto detect mode, the tunnel manager try to establish a real time rtp communication with the tunnel server on specified port.
+ * In case of success, the tunnel is automatically turned off. Otherwise, if no udp communication is feasible, tunnel mode is turned on.
+ * Call this method each time to run the auto detection algorithm
+ * @deprecated Replaced by linphone_tunnel_set_mode(LinphoneTunnelModeAuto)
+ */
+LINPHONE_PUBLIC void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel);
+
+/**
+ * Tell whether tunnel auto detection is enabled.
+ * @param[in] tunnel LinphoneTunnel object.
+ * @return TRUE if auto detection is enabled, FALSE otherwise.
+ * @deprecated Replaced by linphone_tunnel_get_mode()
+ */
+LINPHONE_PUBLIC bool_t linphone_tunnel_auto_detect_enabled(LinphoneTunnel *tunnel);
/**
* @}
diff --git a/coreapi/linphone_tunnel_stubs.c b/coreapi/linphone_tunnel_stubs.c
index 0560b3956..bfe793be6 100644
--- a/coreapi/linphone_tunnel_stubs.c
+++ b/coreapi/linphone_tunnel_stubs.c
@@ -29,7 +29,7 @@
#include "lpconfig.h"
-LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){
+LinphoneTunnel* linphone_core_get_tunnel(const LinphoneCore *lc){
return lc->tunnel;
}
@@ -45,20 +45,27 @@ void linphone_tunnel_add_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tu
void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config){
}
-const MSList *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){
- return NULL;
+const MSList *linphone_tunnel_get_servers(const LinphoneTunnel *tunnel){
+ return NULL;
}
void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){
}
-void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){
+void linphone_tunnel_set_mode(LinphoneTunnel *tunnel, LinphoneTunnelMode mode) {
}
-bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){
- return FALSE;
+LinphoneTunnelMode linphone_tunnel_get_mode(const LinphoneTunnel *tunnel){
+ return LinphoneTunnelModeDisable;
}
+bool_t linphone_tunnel_connected(const LinphoneTunnel *tunnel){
+ return FALSE;
+}
+
+bool_t linphone_tunnel_get_activated(const LinphoneTunnel *tunnel){
+ return FALSE;
+}
void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){
}
@@ -75,9 +82,14 @@ void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int
void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){
}
-void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){
-}
-
void linphone_tunnel_configure(LinphoneTunnel *tunnel){
}
+void linphone_tunnel_enable_sip(LinphoneTunnel *tunnel, bool_t enable) {}
+bool_t linphone_tunnel_sip_enabled(const LinphoneTunnel *tunnel) { return FALSE; }
+
+/* Deprecated functions */
+void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled) {}
+bool_t linphone_tunnel_enabled(const LinphoneTunnel *tunnel) { return FALSE; }
+void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel) {}
+bool_t linphone_tunnel_auto_detect_enabled(LinphoneTunnel *tunnel) { return FALSE; }
diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c
index 9872c0f5d..45a78f47f 100644
--- a/coreapi/linphonecall.c
+++ b/coreapi/linphonecall.c
@@ -18,7 +18,7 @@ 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.
*/
-#ifdef WIN32
+#ifdef _WIN32
#include
#endif
#include "linphonecore.h"
@@ -37,30 +37,50 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/mseventqueue.h"
#include "mediastreamer2/mssndcard.h"
-#ifdef VIDEO_ENABLED
-static MSWebCam *get_nowebcam_device(){
- return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
-}
-#endif
+static const char *EC_STATE_STORE = ".linphone.ecstate";
+#define EC_STATE_MAX_LEN 1048576 // 1Mo
-static bool_t generate_b64_crypto_key(int key_length, char* key_out) {
+static void linphone_call_stats_uninit(LinphoneCallStats *stats);
+static void linphone_call_get_local_ip(LinphoneCall *call, const LinphoneAddress *remote_addr);
+
+
+MSWebCam *get_nowebcam_device(){
+#ifdef VIDEO_ENABLED
+ return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
+#else
+ return NULL;
+#endif
+}
+
+
+static bool_t generate_b64_crypto_key(int key_length, char* key_out, size_t key_out_size) {
int b64_size;
- uint8_t* tmp = (uint8_t*) malloc(key_length);
- if (ortp_crypto_get_random(tmp, key_length)!=0) {
+ uint8_t* tmp = (uint8_t*) ms_malloc0(key_length);
+ if (sal_get_random_bytes(tmp, key_length)==NULL) {
ms_error("Failed to generate random key");
- free(tmp);
+ ms_free(tmp);
return FALSE;
}
-
+
b64_size = b64_encode((const char*)tmp, key_length, NULL, 0);
+ if (b64_size == 0) {
+ ms_error("Failed to get b64 result size");
+ ms_free(tmp);
+ return FALSE;
+ }
+ if (b64_size>=key_out_size){
+ ms_error("Insufficient room for writing base64 SRTP key");
+ ms_free(tmp);
+ return FALSE;
+ }
+ b64_size=b64_encode((const char*)tmp, key_length, key_out, key_out_size);
if (b64_size == 0) {
ms_error("Failed to b64 encode key");
- free(tmp);
+ ms_free(tmp);
return FALSE;
}
key_out[b64_size] = '\0';
- b64_encode((const char*)tmp, key_length, key_out, 40);
- free(tmp);
+ ms_free(tmp);
return TRUE;
}
@@ -72,63 +92,99 @@ const char* linphone_call_get_authentication_token(LinphoneCall *call){
return call->auth_token;
}
+/**
+ * Returns whether ZRTP authentication token is verified.
+ * If not, it must be verified by users as described in ZRTP procedure.
+ * Once done, the application must inform of the results with linphone_call_set_authentication_token_verified().
+ * @param call the LinphoneCall
+ * @return TRUE if authentication token is verifed, false otherwise.
+ * @ingroup call_control
+**/
bool_t linphone_call_get_authentication_token_verified(LinphoneCall *call){
return call->auth_token_verified;
}
-static bool_t linphone_call_are_all_streams_encrypted(LinphoneCall *call) {
- // Check ZRTP encryption in audiostream
- if (!call->audiostream_encrypted) {
- return FALSE;
+static bool_t linphone_call_all_streams_encrypted(const LinphoneCall *call) {
+ int number_of_encrypted_stream = 0;
+ int number_of_active_stream = 0;
+ if (call) {
+ if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
+ number_of_active_stream++;
+ if(media_stream_secured((MediaStream *)call->audiostream))
+ number_of_encrypted_stream++;
+ }
+ if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
+ number_of_active_stream++;
+ if (media_stream_secured((MediaStream *)call->videostream))
+ number_of_encrypted_stream++;
+ }
}
-
-#ifdef VIDEO_ENABLED
- // If video enabled, check ZRTP encryption in videostream
- const LinphoneCallParams *params=linphone_call_get_current_params(call);
- if (params->has_video && !call->videostream_encrypted) {
- return FALSE;
- }
-#endif
-
- return TRUE;
+ return number_of_active_stream>0 && number_of_active_stream==number_of_encrypted_stream;
}
-void propagate_encryption_changed(LinphoneCall *call){
- LinphoneCore *lc=call->core;
- if (!linphone_call_are_all_streams_encrypted(call)) {
- ms_message("Some streams are not encrypted");
- call->current_params.media_encryption=LinphoneMediaEncryptionNone;
- if (lc->vtable.call_encryption_changed)
- lc->vtable.call_encryption_changed(call->core, call, FALSE, call->auth_token);
+static bool_t linphone_call_all_streams_avpf_enabled(const LinphoneCall *call) {
+ int nb_active_streams = 0;
+ int nb_avpf_enabled_streams = 0;
+ if (call) {
+ if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
+ nb_active_streams++;
+ if (media_stream_avpf_enabled((MediaStream *)call->audiostream))
+ nb_avpf_enabled_streams++;
+ }
+ if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
+ nb_active_streams++;
+ if (media_stream_avpf_enabled((MediaStream *)call->videostream))
+ nb_avpf_enabled_streams++;
+ }
+ }
+ return ((nb_active_streams > 0) && (nb_active_streams == nb_avpf_enabled_streams));
+}
+
+static uint16_t linphone_call_get_avpf_rr_interval(const LinphoneCall *call) {
+ uint16_t rr_interval = 0;
+ uint16_t stream_rr_interval;
+ if (call) {
+ if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
+ stream_rr_interval = media_stream_get_avpf_rr_interval((MediaStream *)call->audiostream);
+ if (stream_rr_interval > rr_interval) rr_interval = stream_rr_interval;
+ }
+ if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
+ stream_rr_interval = media_stream_get_avpf_rr_interval((MediaStream *)call->videostream);
+ if (stream_rr_interval > rr_interval) rr_interval = stream_rr_interval;
+ }
} else {
- ms_message("All streams are encrypted");
- call->current_params.media_encryption=LinphoneMediaEncryptionZRTP;
- if (lc->vtable.call_encryption_changed)
- lc->vtable.call_encryption_changed(call->core, call, TRUE, call->auth_token);
+ rr_interval = 5000;
+ }
+ return rr_interval;
+}
+
+static void propagate_encryption_changed(LinphoneCall *call){
+ if (!linphone_call_all_streams_encrypted(call)) {
+ ms_message("Some streams are not encrypted");
+ call->current_params->media_encryption=LinphoneMediaEncryptionNone;
+ linphone_core_notify_call_encryption_changed(call->core, call, FALSE, call->auth_token);
+ } else {
+ if (call->auth_token) {/* ZRTP only is using auth_token */
+ call->current_params->media_encryption=LinphoneMediaEncryptionZRTP;
+ } else { /* otherwise it must be DTLS as SDES doesn't go through this function */
+ call->current_params->media_encryption=LinphoneMediaEncryptionDTLS;
+ }
+ ms_message("All streams are encrypted key exchanged using %s", call->current_params->media_encryption==LinphoneMediaEncryptionZRTP?"ZRTP":call->current_params->media_encryption==LinphoneMediaEncryptionDTLS?"DTLS":"Unknown mechanism");
+ linphone_core_notify_call_encryption_changed(call->core, call, TRUE, call->auth_token);
}
}
-#ifdef VIDEO_ENABLED
-static void linphone_call_videostream_encryption_changed(void *data, bool_t encrypted){
- ms_message("Video stream is %s", encrypted ? "encrypted" : "not encrypted");
-
- LinphoneCall *call = (LinphoneCall *)data;
- call->videostream_encrypted=encrypted;
- propagate_encryption_changed(call);
-}
-#endif
-
static void linphone_call_audiostream_encryption_changed(void *data, bool_t encrypted) {
char status[255]={0};
LinphoneCall *call;
- ms_message("Audio stream is %s ", encrypted ? "encrypted" : "not encrypted");
call = (LinphoneCall *)data;
- call->audiostream_encrypted=encrypted;
-
- if (encrypted && call->core->vtable.display_status != NULL) {
- snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token);
- call->core->vtable.display_status(call->core, status);
+
+ if (encrypted) {
+ if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { /* if encryption is DTLS, no status to be displayed */
+ snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token);
+ linphone_core_notify_display_status(call->core, status);
+ }
}
propagate_encryption_changed(call);
@@ -136,12 +192,14 @@ static void linphone_call_audiostream_encryption_changed(void *data, bool_t encr
#ifdef VIDEO_ENABLED
// Enable video encryption
- const LinphoneCallParams *params=linphone_call_get_current_params(call);
- if (params->has_video) {
- ms_message("Trying to enable encryption on video stream");
- OrtpZrtpParams params;
- params.zid_file=NULL; //unused
- video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms);
+ {
+ const LinphoneCallParams *params=linphone_call_get_current_params(call);
+ if (params->has_video) {
+ MSZrtpParams params;
+ ms_message("Trying to enable encryption on video stream");
+ params.zid_file=NULL; //unused
+ video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms);
+ }
}
#endif
}
@@ -158,52 +216,199 @@ static void linphone_call_audiostream_auth_token_ready(void *data, const char* a
ms_message("Authentication token is %s (%s)", auth_token, verified?"verified":"unverified");
}
+/**
+ * Set the result of ZRTP short code verification by user.
+ * If remote party also does the same, it will update the ZRTP cache so that user's verification will not be required for the two users.
+ * @param call the LinphoneCall
+ * @param verified whether the ZRTP SAS is verified.
+ * @ingroup call_control
+**/
void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t verified){
if (call->audiostream==NULL){
ms_error("linphone_call_set_authentication_token_verified(): No audio stream");
}
- if (call->audiostream->ms.zrtp_context==NULL){
+ if (call->audiostream->ms.sessions.zrtp_context==NULL){
ms_error("linphone_call_set_authentication_token_verified(): No zrtp context.");
}
if (!call->auth_token_verified && verified){
- ortp_zrtp_sas_verified(call->audiostream->ms.zrtp_context);
+ ms_zrtp_sas_verified(call->audiostream->ms.sessions.zrtp_context);
}else if (call->auth_token_verified && !verified){
- ortp_zrtp_sas_reset_verified(call->audiostream->ms.zrtp_context);
+ ms_zrtp_sas_reset_verified(call->audiostream->ms.sessions.zrtp_context);
}
call->auth_token_verified=verified;
propagate_encryption_changed(call);
}
-static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate, int nb_codecs_limit){
+static int get_max_codec_sample_rate(const MSList *codecs){
+ int max_sample_rate=0;
+ const MSList *it;
+ for(it=codecs;it!=NULL;it=it->next){
+ PayloadType *pt=(PayloadType*)it->data;
+ int sample_rate;
+
+ if( strcasecmp("G722",pt->mime_type) == 0 ){
+ /* G722 spec says 8000 but the codec actually requires 16000 */
+ sample_rate = 16000;
+ }else sample_rate=pt->clock_rate;
+ if (sample_rate>max_sample_rate) max_sample_rate=sample_rate;
+ }
+ return max_sample_rate;
+}
+
+static int find_payload_type_number(const MSList *assigned, const PayloadType *pt){
+ const MSList *elem;
+ const PayloadType *candidate=NULL;
+ for(elem=assigned;elem!=NULL;elem=elem->next){
+ const PayloadType *it=(const PayloadType*)elem->data;
+ if ((strcasecmp(pt->mime_type, payload_type_get_mime(it)) == 0)
+ && (it->clock_rate==pt->clock_rate)
+ && (it->channels==pt->channels || pt->channels<=0)) {
+ candidate=it;
+ if ((it->recv_fmtp!=NULL && pt->recv_fmtp!=NULL && strcasecmp(it->recv_fmtp, pt->recv_fmtp)==0)
+ || (it->recv_fmtp==NULL && pt->recv_fmtp==NULL)){
+ break;/*exact match*/
+ }
+ }
+ }
+ return candidate ? payload_type_get_number(candidate) : -1;
+}
+
+bool_t is_payload_type_number_available(const MSList *l, int number, const PayloadType *ignore){
+ const MSList *elem;
+ for (elem=l; elem!=NULL; elem=elem->next){
+ const PayloadType *pt=(PayloadType*)elem->data;
+ if (pt!=ignore && payload_type_get_number(pt)==number) return FALSE;
+ }
+ return TRUE;
+}
+
+static void linphone_core_assign_payload_type_numbers(LinphoneCore *lc, MSList *codecs){
+ MSList *elem;
+ int dyn_number=lc->codecs_conf.dyn_pt;
+ for (elem=codecs; elem!=NULL; elem=elem->next){
+ PayloadType *pt=(PayloadType*)elem->data;
+ int number=payload_type_get_number(pt);
+
+ /*check if number is duplicated: it could be the case if the remote forced us to use a mapping with a previous offer*/
+ if (number!=-1 && !(pt->flags & PAYLOAD_TYPE_FROZEN_NUMBER)){
+ if (!is_payload_type_number_available(codecs, number, pt)){
+ ms_message("Reassigning payload type %i %s/%i because already offered.", number, pt->mime_type, pt->clock_rate);
+ number=-1; /*need to be re-assigned*/
+ }
+ }
+
+ if (number==-1){
+ while(dyn_number<127){
+ if (is_payload_type_number_available(codecs, dyn_number, NULL)){
+ payload_type_set_number(pt, dyn_number);
+ dyn_number++;
+ break;
+ }
+ dyn_number++;
+ }
+ if (dyn_number==127){
+ ms_error("Too many payload types configured ! codec %s/%i is disabled.", pt->mime_type, pt->clock_rate);
+ payload_type_set_enable(pt, FALSE);
+ }
+ }
+ }
+}
+
+static bool_t has_telephone_event_at_rate(const MSList *tev, int rate){
+ const MSList *it;
+ for(it=tev;it!=NULL;it=it->next){
+ const PayloadType *pt=(PayloadType*)it->data;
+ if (pt->clock_rate==rate) return TRUE;
+ }
+ return FALSE;
+}
+
+static MSList * create_telephone_events(LinphoneCore *lc, const MSList *codecs){
+ const MSList *it;
+ MSList *ret=NULL;
+ for(it=codecs;it!=NULL;it=it->next){
+ const PayloadType *pt=(PayloadType*)it->data;
+ if (!has_telephone_event_at_rate(ret,pt->clock_rate)){
+ PayloadType *tev=payload_type_clone(&payload_type_telephone_event);
+ tev->clock_rate=pt->clock_rate;
+ /*let it choose the number dynamically as for normal codecs*/
+ payload_type_set_number(tev, -1);
+ if (ret==NULL){
+ /*But for first telephone-event, prefer the number that was configured in the core*/
+ if (is_payload_type_number_available(codecs, lc->codecs_conf.telephone_event_pt, NULL)){
+ payload_type_set_number(tev, lc->codecs_conf.telephone_event_pt);
+ }
+ }
+ ret=ms_list_append(ret,tev);
+ }
+ }
+ return ret;
+}
+
+static MSList *create_special_payload_types(LinphoneCore *lc, const MSList *codecs){
+ MSList *ret=create_telephone_events(lc, codecs);
+ if (linphone_core_generic_confort_noise_enabled(lc)){
+ PayloadType *cn=payload_type_clone(&payload_type_cn);
+ payload_type_set_number(cn, 13);
+ ret=ms_list_append(ret, cn);
+ }
+ return ret;
+}
+
+typedef struct _CodecConstraints{
+ int bandwidth_limit;
+ int max_codecs;
+ MSList *previously_used;
+}CodecConstraints;
+
+static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, SalStreamType stype, const MSList *codecs){
MSList *l=NULL;
const MSList *it;
int nb = 0;
- if (max_sample_rate) *max_sample_rate=0;
+
for(it=codecs;it!=NULL;it=it->next){
PayloadType *pt=(PayloadType*)it->data;
- if (pt->flags & PAYLOAD_TYPE_ENABLED){
- if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){
- ms_message("Codec %s/%i eliminated because of audio bandwidth constraint.",pt->mime_type,pt->clock_rate);
- continue;
- }
- if (linphone_core_check_payload_type_usability(lc,pt)){
- l=ms_list_append(l,payload_type_clone(pt));
- nb++;
- if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt);
- }
+ int num;
+
+ if (!(pt->flags & PAYLOAD_TYPE_ENABLED))
+ continue;
+ if (hints->bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,hints->bandwidth_limit)){
+ ms_message("Codec %s/%i eliminated because of audio bandwidth constraint of %i kbit/s",
+ pt->mime_type,pt->clock_rate,hints->bandwidth_limit);
+ continue;
}
- if ((nb_codecs_limit > 0) && (nb >= nb_codecs_limit)) break;
+ if (!linphone_core_check_payload_type_usability(lc,pt)){
+ continue;
+ }
+ pt=payload_type_clone(pt);
+
+ /*look for a previously assigned number for this codec*/
+ num=find_payload_type_number(hints->previously_used, pt);
+ if (num!=-1){
+ payload_type_set_number(pt,num);
+ payload_type_set_flag(pt, PAYLOAD_TYPE_FROZEN_NUMBER);
+ }
+
+ l=ms_list_append(l, pt);
+ nb++;
+ if ((hints->max_codecs > 0) && (nb >= hints->max_codecs)) break;
}
+ if (stype==SalAudio){
+ MSList *specials=create_special_payload_types(lc,l);
+ l=ms_list_concat(l,specials);
+ }
+ linphone_core_assign_payload_type_numbers(lc, l);
return l;
}
static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){
int i;
- for (i = 0; i < md->n_active_streams; i++) {
+ for (i = 0; i < md->nb_streams; i++) {
+ if (!sal_stream_description_active(&md->streams[i])) continue;
if ((md->streams[i].type == SalAudio) && (ac->port != 0)) {
strcpy(md->streams[0].rtp_addr,ac->addr);
md->streams[0].rtp_port=ac->port;
- if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->n_active_streams==1){
+ if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || sal_media_description_get_nb_active_streams(md)==1){
strcpy(md->addr,ac->addr);
}
}
@@ -214,134 +419,343 @@ static void update_media_description_from_stun(SalMediaDescription *md, const St
}
}
-void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){
- MSList *l;
- PayloadType *pt;
- SalMediaDescription *old_md=call->localdesc;
+static int setup_encryption_key(SalSrtpCryptoAlgo *crypto, MSCryptoSuite suite, unsigned int tag){
+ int keylen=0;
+ crypto->tag=tag;
+ crypto->algo=suite;
+ switch(suite){
+ case MS_AES_128_SHA1_80:
+ case MS_AES_128_SHA1_32:
+ case MS_AES_128_NO_AUTH:
+ case MS_NO_CIPHER_SHA1_80: /*not sure for this one*/
+ keylen=30;
+ break;
+ case MS_AES_256_SHA1_80:
+ case MS_AES_256_SHA1_32:
+ keylen=46;
+ break;
+ case MS_CRYPTO_SUITE_INVALID:
+ break;
+ }
+ if (keylen==0 || !generate_b64_crypto_key(30, crypto->master_key, SAL_SRTP_KEY_SIZE)){
+ ms_error("Could not generate SRTP key.");
+ crypto->algo = 0;
+ return -1;
+ }
+ return 0;
+}
+static void setup_dtls_keys(LinphoneCall *call, SalMediaDescription *md){
int i;
- const char *me=linphone_core_get_identity(lc);
- LinphoneAddress *addr=linphone_address_new(me);
- const char *username=linphone_address_get_username (addr);
- SalMediaDescription *md=sal_media_description_new();
- bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",0);
+ for(i=0; inb_streams; i++) {
+ if (!sal_stream_description_active(&md->streams[i])) continue;
+ /* if media encryption is set to DTLS check presence of fingerprint in the call which shall have been set at stream init but it may have failed when retrieving certificate resulting in no fingerprint present and then DTLS not usable */
+ if (sal_stream_description_has_dtls(&md->streams[i]) == TRUE) {
+ strncpy(md->streams[i].dtls_fingerprint, call->dtls_certificate_fingerprint, sizeof(md->streams[i].dtls_fingerprint)); /* get the self fingerprint from call(it's computed at stream init) */
+ md->streams[i].dtls_role = SalDtlsRoleUnset; /* if we are offering, SDP will have actpass setup attribute when role is unset, if we are responding the result mediadescription will be set to SalDtlsRoleIsClient */
+ } else {
+ md->streams[i].dtls_fingerprint[0] = '\0';
+ md->streams[i].dtls_role = SalDtlsRoleInvalid;
- linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
-
- md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff));
- md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff));
- md->n_total_streams=(old_md ? old_md->n_total_streams : 1);
- md->n_active_streams=1;
- strncpy(md->addr,call->localip,sizeof(md->addr));
- strncpy(md->username,username,sizeof(md->username));
-
- if (call->params.down_bw)
- md->bandwidth=call->params.down_bw;
- else md->bandwidth=linphone_core_get_download_bandwidth(lc);
-
- /*set audio capabilities */
- strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr));
- strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr));
- md->streams[0].rtp_port=call->audio_port;
- md->streams[0].rtcp_port=call->audio_port+1;
- md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ?
- SalProtoRtpSavp : SalProtoRtpAvp;
- md->streams[0].type=SalAudio;
- if (call->params.down_ptime)
- md->streams[0].ptime=call->params.down_ptime;
- else
- md->streams[0].ptime=linphone_core_get_download_ptime(lc);
- l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate,-1);
- pt=payload_type_clone(rtp_profile_get_payload_from_mime(lc->default_profile,"telephone-event"));
- l=ms_list_append(l,pt);
- md->streams[0].payloads=l;
-
- if (call->params.has_video){
- md->n_active_streams++;
- md->streams[1].rtp_port=call->video_port;
- md->streams[1].rtcp_port=call->video_port+1;
- md->streams[1].proto=md->streams[0].proto;
- md->streams[1].type=SalVideo;
- l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL,-1);
- md->streams[1].payloads=l;
- }
- if (md->n_total_streams < md->n_active_streams)
- md->n_total_streams = md->n_active_streams;
-
- /* Deactivate inactive streams. */
- for (i = md->n_active_streams; i < md->n_total_streams; i++) {
- md->streams[i].rtp_port = 0;
- md->streams[i].rtcp_port = 0;
- md->streams[i].proto = SalProtoRtpAvp;
- md->streams[i].type = old_md->streams[i].type;
- md->streams[i].dir = SalStreamInactive;
- l = make_codec_list(lc, lc->codecs_conf.video_codecs, 0, NULL, 1);
- md->streams[i].payloads = l;
+ }
}
- for(i=0; in_active_streams; i++) {
- if (md->streams[i].proto == SalProtoRtpSavp) {
- if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){
+}
+static void setup_encryption_keys(LinphoneCall *call, SalMediaDescription *md){
+ LinphoneCore *lc=call->core;
+ int i,j;
+ SalMediaDescription *old_md=call->localdesc;
+ bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",1);
+
+ for(i=0; inb_streams; i++) {
+ if (!sal_stream_description_active(&md->streams[i])) continue;
+ if (sal_stream_description_has_srtp(&md->streams[i]) == TRUE) {
+ if (keep_srtp_keys && old_md && (sal_stream_description_active(&old_md->streams[i]) == TRUE) && (sal_stream_description_has_srtp(&old_md->streams[i]) == TRUE)) {
int j;
+ ms_message("Keeping same crypto keys.");
for(j=0;jstreams[i].crypto[j],&old_md->streams[i].crypto[j],sizeof(SalSrtpCryptoAlgo));
}
}else{
- md->streams[i].crypto[0].tag = 1;
- md->streams[i].crypto[0].algo = AES_128_SHA1_80;
- if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
- md->streams[i].crypto[0].algo = 0;
- md->streams[i].crypto[1].tag = 2;
- md->streams[i].crypto[1].algo = AES_128_SHA1_32;
- if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
- md->streams[i].crypto[1].algo = 0;
- md->streams[i].crypto[2].algo = 0;
+ const MSCryptoSuite *suites=linphone_core_get_srtp_crypto_suites(lc);
+ for(j=0;suites!=NULL && suites[j]!=MS_CRYPTO_SUITE_INVALID && jstreams[i].crypto[j],suites[j],j+1);
+ }
}
}
}
- update_media_description_from_stun(md,&call->ac,&call->vc);
+}
+
+static void setup_rtcp_fb(LinphoneCall *call, SalMediaDescription *md) {
+ MSList *pt_it;
+ PayloadType *pt;
+ PayloadTypeAvpfParams avpf_params;
+ LinphoneCore *lc = call->core;
+ int i;
+
+ for (i = 0; i < md->nb_streams; i++) {
+ if (!sal_stream_description_active(&md->streams[i])) continue;
+ md->streams[i].rtcp_fb.generic_nack_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_fb_generic_nack_enabled", 0);
+ md->streams[i].rtcp_fb.tmmbr_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_fb_tmmbr_enabled", 0);
+ for (pt_it = md->streams[i].payloads; pt_it != NULL; pt_it = pt_it->next) {
+ pt = (PayloadType *)pt_it->data;
+ if (call->params->avpf_enabled == TRUE) {
+ payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
+ avpf_params = payload_type_get_avpf_params(pt);
+ avpf_params.trr_interval = call->params->avpf_rr_interval;
+ } else {
+ payload_type_unset_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
+ memset(&avpf_params, 0, sizeof(avpf_params));
+ }
+ payload_type_set_avpf_params(pt, avpf_params);
+ }
+ }
+}
+
+static void setup_rtcp_xr(LinphoneCall *call, SalMediaDescription *md) {
+ LinphoneCore *lc = call->core;
+ int i;
+
+ md->rtcp_xr.enabled = lp_config_get_int(lc->config, "rtp", "rtcp_xr_enabled", 1);
+ if (md->rtcp_xr.enabled == TRUE) {
+ const char *rcvr_rtt_mode = lp_config_get_string(lc->config, "rtp", "rtcp_xr_rcvr_rtt_mode", "all");
+ if (strcasecmp(rcvr_rtt_mode, "all") == 0) md->rtcp_xr.rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll;
+ else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) md->rtcp_xr.rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender;
+ else md->rtcp_xr.rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone;
+ if (md->rtcp_xr.rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
+ md->rtcp_xr.rcvr_rtt_max_size = lp_config_get_int(lc->config, "rtp", "rtcp_xr_rcvr_rtt_max_size", 10000);
+ }
+ md->rtcp_xr.stat_summary_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_xr_stat_summary_enabled", 1);
+ if (md->rtcp_xr.stat_summary_enabled == TRUE) {
+ md->rtcp_xr.stat_summary_flags = OrtpRtcpXrStatSummaryLoss | OrtpRtcpXrStatSummaryDup | OrtpRtcpXrStatSummaryJitt | OrtpRtcpXrStatSummaryTTL;
+ }
+ md->rtcp_xr.voip_metrics_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_xr_voip_metrics_enabled", 1);
+ }
+ for (i = 0; i < md->nb_streams; i++) {
+ if (!sal_stream_description_active(&md->streams[i])) continue;
+ memcpy(&md->streams[i].rtcp_xr, &md->rtcp_xr, sizeof(md->streams[i].rtcp_xr));
+ }
+}
+
+void linphone_call_increment_local_media_description(LinphoneCall *call){
+ SalMediaDescription *md=call->localdesc;
+ md->session_ver++;
+}
+
+void linphone_call_update_local_media_description_from_ice_or_upnp(LinphoneCall *call){
if (call->ice_session != NULL) {
- linphone_core_update_local_media_description_from_ice(md, call->ice_session);
+ _update_local_media_description_from_ice(call->localdesc, call->ice_session);
linphone_core_update_ice_state_in_call_stats(call);
}
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
- linphone_core_update_local_media_description_from_upnp(md, call->upnp_session);
+ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
linphone_core_update_upnp_state_in_call_stats(call);
}
#endif //BUILD_UPNP
- linphone_address_destroy(addr);
- call->localdesc=md;
- if (old_md) sal_media_description_unref(old_md);
}
-static int find_port_offset(LinphoneCore *lc, SalStreamType type){
+static void transfer_already_assigned_payload_types(SalMediaDescription *old, SalMediaDescription *md){
+ int i;
+ for(i=0;inb_streams;++i){
+ md->streams[i].already_assigned_payloads=old->streams[i].already_assigned_payloads;
+ old->streams[i].already_assigned_payloads=NULL;
+ }
+}
+
+static const char *linphone_call_get_bind_ip_for_stream(LinphoneCall *call, int stream_index){
+ const char *bind_ip = lp_config_get_string(call->core->config,"rtp","bind_address",call->af==AF_INET6 ? "::0" : "0.0.0.0");
+
+ if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0'){
+ if (call->dir==LinphoneCallOutgoing){
+ /*as multicast sender, we must decide a local interface to use to send multicast, and bind to it*/
+ bind_ip=call->media_localip;
+ }
+ }
+ return bind_ip;
+}
+
+static const char *linphone_call_get_public_ip_for_stream(LinphoneCall *call, int stream_index){
+ const char *public_ip=call->media_localip;
+
+ if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0')
+ public_ip=call->media_ports[stream_index].multicast_ip;
+ return public_ip;
+}
+
+static void force_streams_dir_according_to_state(LinphoneCall *call, SalMediaDescription *md){
+ int i;
+
+ switch (call->state){
+ case LinphoneCallPausing:
+ case LinphoneCallPaused:
+ break;
+ default:
+ return;
+ break;
+ }
+
+ for (i=0; i<2; ++i){
+ SalStreamDescription *sd = &md->streams[i];
+ sd->dir = SalStreamSendOnly;
+ if (sd->type == SalVideo){
+ if (lp_config_get_int(call->core->config, "sip", "inactive_video_on_pause", 0)) {
+ sd->dir = SalStreamInactive;
+ }
+ }
+ }
+}
+
+void linphone_call_make_local_media_description(LinphoneCall *call) {
+ MSList *l;
+ SalMediaDescription *old_md=call->localdesc;
+ int i;
+ int nb_active_streams = 0;
+ SalMediaDescription *md=sal_media_description_new();
+ LinphoneAddress *addr;
+ const char *subject;
+ CodecConstraints codec_hints={0};
+ LinphoneCallParams *params = call->params;
+ LinphoneCore *lc = call->core;
+
+
+ /*multicast is only set in case of outgoing call*/
+ if (call->dir == LinphoneCallOutgoing && linphone_call_params_audio_multicast_enabled(params)) {
+ md->streams[0].ttl=linphone_core_get_audio_multicast_ttl(lc);
+ md->streams[0].multicast_role = SalMulticastSender;
+ }
+
+ if (call->dir == LinphoneCallOutgoing && linphone_call_params_video_multicast_enabled(params)) {
+ md->streams[1].ttl=linphone_core_get_video_multicast_ttl(lc);
+ md->streams[1].multicast_role = SalMulticastSender;
+ }
+
+ subject=linphone_call_params_get_session_name(params);
+
+ linphone_core_adapt_to_network(lc,call->ping_time,params);
+
+ if (call->dest_proxy) {
+ addr=linphone_address_clone(linphone_proxy_config_get_identity_address(call->dest_proxy));
+ } else {
+ addr=linphone_address_new(linphone_core_get_identity(lc));
+ }
+
+ md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff));
+ md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff));
+ md->nb_streams=(call->biggestdesc ? call->biggestdesc->nb_streams : 1);
+
+ /*re-check local ip address each time we make a new offer, because it may change in case of network reconnection*/
+ linphone_call_get_local_ip(call, call->dir == LinphoneCallOutgoing ? call->log->to : call->log->from);
+ strncpy(md->addr,call->media_localip,sizeof(md->addr));
+ if (linphone_address_get_username(addr)) /*might be null in case of identity without userinfo*/
+ strncpy(md->username,linphone_address_get_username(addr),sizeof(md->username));
+ if (subject) strncpy(md->name,subject,sizeof(md->name));
+
+ if (params->down_bw)
+ md->bandwidth=params->down_bw;
+ else md->bandwidth=linphone_core_get_download_bandwidth(lc);
+
+ /*set audio capabilities */
+ strncpy(md->streams[0].rtp_addr,linphone_call_get_public_ip_for_stream(call,0),sizeof(md->streams[0].rtp_addr));
+ strncpy(md->streams[0].rtcp_addr,linphone_call_get_public_ip_for_stream(call,0),sizeof(md->streams[0].rtcp_addr));
+ strncpy(md->streams[0].name,"Audio",sizeof(md->streams[0].name)-1);
+ md->streams[0].rtp_port=call->media_ports[0].rtp_port;
+ md->streams[0].rtcp_port=call->media_ports[0].rtcp_port;
+ md->streams[0].proto=get_proto_from_call_params(params);
+ md->streams[0].dir=get_audio_dir_from_call_params(params);
+ md->streams[0].type=SalAudio;
+ if (params->down_ptime)
+ md->streams[0].ptime=params->down_ptime;
+ else
+ md->streams[0].ptime=linphone_core_get_download_ptime(lc);
+ codec_hints.bandwidth_limit=params->audio_bw;
+ codec_hints.max_codecs=-1;
+ codec_hints.previously_used=old_md ? old_md->streams[0].already_assigned_payloads : NULL;
+ l=make_codec_list(lc, &codec_hints, SalAudio, lc->codecs_conf.audio_codecs);
+ md->streams[0].max_rate=get_max_codec_sample_rate(l);
+ md->streams[0].payloads=l;
+ if (call->audiostream && call->audiostream->ms.sessions.rtp_session) {
+ char* me = linphone_address_as_string_uri_only(call->me);
+ md->streams[0].rtp_ssrc=rtp_session_get_send_ssrc(call->audiostream->ms.sessions.rtp_session);
+ strncpy(md->streams[0].rtcp_cname,me,sizeof(md->streams[0].rtcp_cname));
+ ms_free(me);
+ }
+ else
+ ms_warning("Cannot get audio local ssrc for call [%p]",call);
+ nb_active_streams++;
+
+ if (params->has_video && (!params->internal_call_update || !call->current_params->video_declined)){
+ strncpy(md->streams[1].rtp_addr,linphone_call_get_public_ip_for_stream(call,1),sizeof(md->streams[1].rtp_addr));
+ strncpy(md->streams[1].rtcp_addr,linphone_call_get_public_ip_for_stream(call,1),sizeof(md->streams[1].rtcp_addr));
+ strncpy(md->streams[1].name,"Video",sizeof(md->streams[1].name)-1);
+ md->streams[1].rtp_port=call->media_ports[1].rtp_port;
+ md->streams[1].rtcp_port=call->media_ports[1].rtcp_port;
+ md->streams[1].proto=md->streams[0].proto;
+ md->streams[1].dir=get_video_dir_from_call_params(params);
+ md->streams[1].type=SalVideo;
+ codec_hints.bandwidth_limit=0;
+ codec_hints.max_codecs=-1;
+ codec_hints.previously_used=old_md ? old_md->streams[1].already_assigned_payloads : NULL;
+ l=make_codec_list(lc, &codec_hints, SalVideo, lc->codecs_conf.video_codecs);
+ md->streams[1].payloads=l;
+ if (call->videostream && call->videostream->ms.sessions.rtp_session) {
+ char* me = linphone_address_as_string_uri_only(call->me);
+ md->streams[1].rtp_ssrc=rtp_session_get_send_ssrc(call->videostream->ms.sessions.rtp_session);
+ strncpy(md->streams[1].rtcp_cname,me,sizeof(md->streams[1].rtcp_cname));
+ ms_free(me);
+ }
+ else
+ ms_warning("Cannot get video local ssrc for call [%p]",call);
+ nb_active_streams++;
+ } else {
+ ms_message("Don't put video stream on local offer for call [%p]",call);
+ }
+
+ if (md->nb_streams < nb_active_streams)
+ md->nb_streams = nb_active_streams;
+
+ /* Deactivate inactive streams. */
+ for (i = nb_active_streams; i < md->nb_streams; i++) {
+ md->streams[i].rtp_port = 0;
+ md->streams[i].rtcp_port = 0;
+ md->streams[i].proto = call->biggestdesc->streams[i].proto;
+ md->streams[i].type = call->biggestdesc->streams[i].type;
+ md->streams[i].dir = SalStreamInactive;
+ codec_hints.bandwidth_limit=0;
+ codec_hints.max_codecs=1;
+ codec_hints.previously_used=NULL;
+ l = make_codec_list(lc, &codec_hints, SalVideo, lc->codecs_conf.video_codecs);
+ md->streams[i].payloads = l;
+ }
+ setup_encryption_keys(call,md);
+ setup_dtls_keys(call,md);
+ setup_rtcp_fb(call, md);
+ setup_rtcp_xr(call, md);
+
+ update_media_description_from_stun(md,&call->ac,&call->vc);
+ call->localdesc=md;
+ linphone_call_update_local_media_description_from_ice_or_upnp(call);
+ linphone_address_destroy(addr);
+ if (old_md){
+ transfer_already_assigned_payload_types(old_md,md);
+ call->localdesc_changed=sal_media_description_equals(md,old_md);
+ sal_media_description_unref(old_md);
+ }
+ force_streams_dir_according_to_state(call, md);
+}
+
+static int find_port_offset(LinphoneCore *lc, int stream_index, int base_port){
int offset;
MSList *elem;
int tried_port;
int existing_port;
bool_t already_used=FALSE;
+
for(offset=0;offset<100;offset+=2){
- switch (type) {
- default:
- case SalAudio:
- tried_port=linphone_core_get_audio_port (lc)+offset;
- break;
- case SalVideo:
- tried_port=linphone_core_get_video_port (lc)+offset;
- break;
- }
+ tried_port=base_port+offset;
already_used=FALSE;
for(elem=lc->calls;elem!=NULL;elem=elem->next){
LinphoneCall *call=(LinphoneCall*)elem->data;
- switch (type) {
- default:
- case SalAudio:
- existing_port = call->audio_port;
- break;
- case SalVideo:
- existing_port = call->video_port;
- break;
- }
+ existing_port=call->media_ports[stream_index].rtp_port;
if (existing_port==tried_port) {
already_used=TRUE;
break;
@@ -356,37 +770,19 @@ static int find_port_offset(LinphoneCore *lc, SalStreamType type){
return offset;
}
-static int select_random_port(LinphoneCore *lc, SalStreamType type) {
+static int select_random_port(LinphoneCore *lc, int stream_index, int min_port, int max_port) {
MSList *elem;
int nb_tries;
int tried_port = 0;
int existing_port = 0;
- int min_port = 0, max_port = 0;
bool_t already_used = FALSE;
- switch (type) {
- default:
- case SalAudio:
- linphone_core_get_audio_port_range(lc, &min_port, &max_port);
- break;
- case SalVideo:
- linphone_core_get_video_port_range(lc, &min_port, &max_port);
- break;
- }
- tried_port = (rand() % (max_port - min_port) + min_port) & ~0x1;
+ tried_port = (ortp_random() % (max_port - min_port) + min_port) & ~0x1;
if (tried_port < min_port) tried_port = min_port + 2;
for (nb_tries = 0; nb_tries < 100; nb_tries++) {
for (elem = lc->calls; elem != NULL; elem = elem->next) {
LinphoneCall *call = (LinphoneCall *)elem->data;
- switch (type) {
- default:
- case SalAudio:
- existing_port = call->audio_port;
- break;
- case SalVideo:
- existing_port = call->video_port;
- break;
- }
+ existing_port=call->media_ports[stream_index].rtp_port;
if (existing_port == tried_port) {
already_used = TRUE;
break;
@@ -401,38 +797,52 @@ static int select_random_port(LinphoneCore *lc, SalStreamType type) {
return tried_port;
}
-static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
+static void port_config_set_random(LinphoneCall *call, int stream_index){
+ call->media_ports[stream_index].rtp_port=-1;
+ call->media_ports[stream_index].rtcp_port=-1;
+}
+
+static void port_config_set(LinphoneCall *call, int stream_index, int min_port, int max_port){
int port_offset;
+ if (min_port>0 && max_port>0){
+ if (min_port == max_port) {
+ /* Used fixed RTP audio port. */
+ port_offset=find_port_offset(call->core, stream_index, min_port);
+ if (port_offset==-1) {
+ port_config_set_random(call, stream_index);
+ return;
+ }
+ call->media_ports[stream_index].rtp_port=min_port+port_offset;
+ } else {
+ /* Select random RTP audio port in the specified range. */
+ call->media_ports[stream_index].rtp_port = select_random_port(call->core, stream_index, min_port, max_port);
+ }
+ call->media_ports[stream_index].rtcp_port=call->media_ports[stream_index].rtp_port+1;
+ }else port_config_set_random(call,stream_index);
+}
+
+static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
int min_port, max_port;
- call->magic=linphone_call_magic;
- call->refcnt=1;
+ ms_message("New LinphoneCall [%p] initialized (LinphoneCore version: %s)",call,linphone_core_get_version());
call->state=LinphoneCallIdle;
call->transfer_state = LinphoneCallIdle;
- call->start_time=time(NULL);
- call->media_start_time=0;
- call->log=linphone_call_log_new(call, from, to);
- call->owns_call_log=TRUE;
- linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
+ call->log=linphone_call_log_new(call->dir, from, to);
+ call->camera_enabled=TRUE;
+ call->current_params = linphone_call_params_new();
+ call->current_params->media_encryption=LinphoneMediaEncryptionNone;
+ call->dtls_certificate_fingerprint = NULL;
+ if (call->dir == LinphoneCallIncoming)
+ call->me=to;
+ else
+ call->me=from;
+ linphone_address_ref(call->me);
+
linphone_core_get_audio_port_range(call->core, &min_port, &max_port);
- if (min_port == max_port) {
- /* Used fixed RTP audio port. */
- port_offset=find_port_offset (call->core, SalAudio);
- if (port_offset==-1) return;
- call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
- } else {
- /* Select random RTP audio port in the specified range. */
- call->audio_port = select_random_port(call->core, SalAudio);
- }
+ port_config_set(call,0,min_port,max_port);
+
linphone_core_get_video_port_range(call->core, &min_port, &max_port);
- if (min_port == max_port) {
- /* Used fixed RTP video port. */
- port_offset=find_port_offset (call->core, SalVideo);
- if (port_offset==-1) return;
- call->video_port=linphone_core_get_video_port(call->core)+port_offset;
- } else {
- /* Select random RTP video port in the specified range. */
- call->video_port = select_random_port(call->core, SalVideo);
- }
+ port_config_set(call,1,min_port,max_port);
+
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
}
@@ -463,21 +873,122 @@ static void discover_mtu(LinphoneCore *lc, const char *remote){
}
}
-LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
-{
- LinphoneCall *call=ms_new0(LinphoneCall,1);
- call->dir=LinphoneCallOutgoing;
- call->op=sal_op_new(lc->sal);
+void linphone_call_create_op(LinphoneCall *call){
+ if (call->op) sal_op_release(call->op);
+ call->op=sal_op_new(call->core->sal);
sal_op_set_user_pointer(call->op,call);
+ if (call->params->referer)
+ sal_call_set_referer(call->op,call->params->referer->op);
+ linphone_configure_op(call->core,call->op,call->log->to,call->params->custom_headers,FALSE);
+ if (call->params->privacy != LinphonePrivacyDefault)
+ sal_op_set_privacy(call->op,(SalPrivacyMask)call->params->privacy);
+ /*else privacy might be set by proxy */
+}
+
+/*
+ * Choose IP version we are going to use for RTP socket.
+ * The algorithm is as follows:
+ * - if ipv6 is disabled at the core level, it is always AF_INET
+ * - Otherwise, if the destination address for the call is an IPv6 address, use IPv6.
+ * - Otherwise, if the call is done through a known proxy config, then use the information obtained during REGISTER
+ * to know if IPv6 is supported by the server.
+**/
+static void linphone_call_outgoing_select_ip_version(LinphoneCall *call, LinphoneAddress *to, LinphoneProxyConfig *cfg){
+ if (linphone_core_ipv6_enabled(call->core)){
+ call->af=AF_INET;
+ if (sal_address_is_ipv6((SalAddress*)to)){
+ call->af=AF_INET6;
+ }else if (cfg && cfg->op){
+ call->af=sal_op_is_ipv6(cfg->op) ? AF_INET6 : AF_INET;
+ }
+ }else call->af=AF_INET;
+}
+
+/**
+ * Fill the local ip that routes to the internet according to the destination, or guess it by other special means (upnp).
+ */
+static void linphone_call_get_local_ip(LinphoneCall *call, const LinphoneAddress *remote_addr){
+ const char *ip;
+ int af = call->af;
+ const char *dest = NULL;
+ if (call->dest_proxy == NULL) {
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
+ int err;
+ const char *domain = linphone_address_get_domain(remote_addr);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ err = getaddrinfo(domain, NULL, &hints, &res);
+ if (err == 0) {
+ dest = domain;
+ }
+ if (res != NULL) freeaddrinfo(res);
+ }
+ if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseNatAddress
+ && (ip=linphone_core_get_nat_address_resolved(call->core))!=NULL){
+ strncpy(call->media_localip,ip,LINPHONE_IPADDR_SIZE);
+ return;
+ }
+#ifdef BUILD_UPNP
+ else if (call->core->upnp != NULL && linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseUpnp &&
+ linphone_upnp_context_get_state(call->core->upnp) == LinphoneUpnpStateOk) {
+ ip = linphone_upnp_context_get_external_ipaddress(call->core->upnp);
+ strncpy(call->media_localip,ip,LINPHONE_IPADDR_SIZE);
+ return;
+ }
+#endif //BUILD_UPNP
+ /*first nominal use case*/
+ linphone_core_get_local_ip(call->core, af, dest, call->media_localip);
+
+ /*next, sometime, override from config*/
+ if ((ip=lp_config_get_string(call->core->config,"rtp","bind_address",NULL)))
+ strncpy(call->media_localip,ip,LINPHONE_IPADDR_SIZE);
+
+ return;
+}
+
+static void linphone_call_destroy(LinphoneCall *obj);
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCall);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneCall, belle_sip_object_t,
+ (belle_sip_object_destroy_t)linphone_call_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE
+);
+void linphone_call_fill_media_multicast_addr(LinphoneCall *call) {
+ if (linphone_call_params_audio_multicast_enabled(call->params)){
+ strncpy(call->media_ports[0].multicast_ip,
+ linphone_core_get_audio_multicast_addr(call->core), sizeof(call->media_ports[0].multicast_ip));
+ } else
+ call->media_ports[0].multicast_ip[0]='\0';
+
+ if (linphone_call_params_video_multicast_enabled(call->params)){
+ strncpy(call->media_ports[1].multicast_ip,
+ linphone_core_get_video_multicast_addr(call->core), sizeof(call->media_ports[1].multicast_ip));
+ } else
+ call->media_ports[1].multicast_ip[0]='\0';
+}
+
+LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg){
+ LinphoneCall *call = belle_sip_object_new(LinphoneCall);
+
+ call->dir=LinphoneCallOutgoing;
call->core=lc;
- linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
- linphone_call_init_common(call,from,to);
- _linphone_call_params_copy(&call->params,params);
- sal_op_set_custom_header(call->op,call->params.custom_headers);
- call->params.custom_headers=NULL;
-
+ linphone_call_outgoing_select_ip_version(call,to,cfg);
+ linphone_call_get_local_ip(call, to);
+ linphone_call_init_common(call, from, to);
+ call->params = linphone_call_params_copy(params);
+
+ linphone_call_fill_media_multicast_addr(call);
+
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
call->ice_session = ice_session_new();
+ /*for backward compatibility purposes, shall be enabled by default in futur*/
+ ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0));
ice_session_set_role(call->ice_session, IR_Controlling);
}
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
@@ -485,29 +996,65 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr
}
#ifdef BUILD_UPNP
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
- call->upnp_session = linphone_upnp_session_new(call);
+ if(!lc->rtp_conf.disable_upnp) {
+ call->upnp_session = linphone_upnp_session_new(call);
+ }
}
#endif //BUILD_UPNP
- call->camera_active=params->has_video;
-
+
discover_mtu(lc,linphone_address_get_domain (to));
if (params->referer){
- sal_call_set_referer(call->op,params->referer->op);
call->referer=linphone_call_ref(params->referer);
}
+ call->dest_proxy=cfg;
+ linphone_call_create_op(call);
return call;
}
+static void linphone_call_incoming_select_ip_version(LinphoneCall *call){
+ if (linphone_core_ipv6_enabled(call->core)){
+ call->af=sal_op_is_ipv6(call->op) ? AF_INET6 : AF_INET;
+ }else call->af=AF_INET;
+}
+
+/**
+ * Fix call parameters on incoming call to eg. enable AVPF if the incoming call propose it and it is not enabled locally.
+ */
+void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, const SalMediaDescription *md) {
+ /* Handle AVPF, SRTP and DTLS. */
+ call->params->avpf_enabled = sal_media_description_has_avpf(md);
+ if (call->params->avpf_enabled == TRUE) {
+ if (call->dest_proxy != NULL) {
+ call->params->avpf_rr_interval = linphone_proxy_config_get_avpf_rr_interval(call->dest_proxy) * 1000;
+ } else {
+ call->params->avpf_rr_interval = linphone_core_get_avpf_rr_interval(call->core)*1000;
+ }
+ }
+ if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) {
+ call->params->media_encryption = LinphoneMediaEncryptionDTLS;
+ }else if ((sal_media_description_has_srtp(md) == TRUE) && (ms_srtp_supported() == TRUE)) {
+ call->params->media_encryption = LinphoneMediaEncryptionSRTP;
+ }else if (call->params->media_encryption != LinphoneMediaEncryptionZRTP){
+ call->params->media_encryption = LinphoneMediaEncryptionNone;
+ }
+
+}
+
LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
- LinphoneCall *call=ms_new0(LinphoneCall,1);
- char *from_str;
- const SalMediaDescription *md;
+ LinphoneCall *call = belle_sip_object_new(LinphoneCall);
+ SalMediaDescription *md;
+ LinphoneFirewallPolicy fpol;
+ int i;
call->dir=LinphoneCallIncoming;
sal_op_set_user_pointer(op,call);
call->op=op;
call->core=lc;
+ linphone_call_incoming_select_ip_version(call);
+
+ sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0));
+
if (lc->sip_conf.ping_with_options){
#ifdef BUILD_UPNP
if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp &&
@@ -518,40 +1065,67 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
/*the following sends an option request back to the caller so that
we get a chance to discover our nat'd address before answering.*/
call->ping_op=sal_op_new(lc->sal);
- from_str=linphone_address_as_string_uri_only(from);
+
+ linphone_configure_op(lc, call->ping_op, from, NULL, FALSE);
+
sal_op_set_route(call->ping_op,sal_op_get_network_origin(op));
sal_op_set_user_pointer(call->ping_op,call);
- sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str);
- ms_free(from_str);
+
+ sal_ping(call->ping_op,sal_op_get_from(call->ping_op), sal_op_get_to(call->ping_op));
}
}
linphone_address_clean(from);
- linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
+ linphone_call_get_local_ip(call, from);
linphone_call_init_common(call, from, to);
+ call->params = linphone_call_params_new();
call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/
- linphone_core_init_default_params(lc, &call->params);
+ call->dest_proxy = linphone_core_lookup_known_proxy(call->core, to);
+ linphone_core_init_default_params(lc, call->params);
+
+ /*
+ * Initialize call parameters according to incoming call parameters. This is to avoid to ask later (during reINVITEs) for features that the remote
+ * end apparently does not support. This features are: privacy, video
+ */
+ /*set privacy*/
+ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
+ /*set video support */
md=sal_call_get_remote_media_description(op);
- call->params.has_video &= !!lc->video_policy.automatically_accept;
+ call->params->has_video = linphone_core_video_enabled(lc) && lc->video_policy.automatically_accept;
if (md) {
// It is licit to receive an INVITE without SDP
// In this case WE chose the media parameters according to policy.
- call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
- }
- switch (linphone_core_get_firewall_policy(call->core)) {
- case LinphonePolicyUseIce:
- call->ice_session = ice_session_new();
- ice_session_set_role(call->ice_session, IR_Controlled);
- linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
- if (call->ice_session != NULL) {
- linphone_call_init_media_streams(call);
- linphone_call_start_media_streams_for_ice_gathering(call);
- if (linphone_core_gather_ice_candidates(call->core,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- linphone_call_stop_media_streams_for_ice_gathering(call);
+ linphone_call_set_compatible_incoming_call_parameters(call, md);
+ /* set multicast role & address if any*/
+ if (!sal_call_is_offerer(op)){
+ for (i=0;inb_streams;i++){
+ if (md->streams[i].rtp_addr[0]!='\0' && ms_is_multicast(md->streams[i].rtp_addr)){
+ md->streams[i].multicast_role = SalMulticastReceiver;
+ strncpy(call->media_ports[i].multicast_ip,md->streams[i].rtp_addr,sizeof(call->media_ports[i].multicast_ip));
}
}
+ }
+ }
+
+ fpol=linphone_core_get_firewall_policy(call->core);
+ /*create the ice session now if ICE is required*/
+ if (fpol==LinphonePolicyUseIce){
+ if (md){
+ call->ice_session = ice_session_new();
+ /*for backward compatibility purposes, shall be enabled by default in futur*/
+ ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0));
+ ice_session_set_role(call->ice_session, IR_Controlled);
+ }else{
+ fpol=LinphonePolicyNoFirewall;
+ ms_warning("ICE not supported for incoming INVITE without SDP.");
+ }
+ }
+
+ /*reserve the sockets immediately*/
+ linphone_call_init_media_streams(call);
+ switch (fpol) {
+ case LinphonePolicyUseIce:
+ linphone_call_prepare_ice(call,TRUE);
break;
case LinphonePolicyUseStun:
call->ping_time=linphone_core_run_stun_tests(call->core,call);
@@ -559,12 +1133,13 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
break;
case LinphonePolicyUseUpnp:
#ifdef BUILD_UPNP
- call->upnp_session = linphone_upnp_session_new(call);
- if (call->upnp_session != NULL) {
- linphone_call_init_media_streams(call);
- if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) {
- /* uPnP port mappings failed, proceed with the call anyway. */
- linphone_call_delete_upnp_session(call);
+ if(!lc->rtp_conf.disable_upnp) {
+ call->upnp_session = linphone_upnp_session_new(call);
+ if (call->upnp_session != NULL) {
+ if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ }
}
}
#endif //BUILD_UPNP
@@ -572,13 +1147,55 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
default:
break;
}
- call->camera_active=call->params.has_video;
-
+
discover_mtu(lc,linphone_address_get_domain(from));
return call;
}
-/* this function is called internally to get rid of a call.
+/*
+ * Frees the media resources of the call.
+ * This has to be done at the earliest, unlike signaling resources that sometimes need to be kept a bit more longer.
+ * It is called by linphone_call_set_terminated() (for termination of calls signaled to the application), or directly by the destructor of LinphoneCall
+ * (_linphone_call_destroy) if the call was never notified to the application.
+ */
+void linphone_call_free_media_resources(LinphoneCall *call){
+ linphone_call_stop_media_streams(call);
+ ms_media_stream_sessions_uninit(&call->sessions[0]);
+ ms_media_stream_sessions_uninit(&call->sessions[1]);
+ linphone_call_delete_upnp_session(call);
+ linphone_call_delete_ice_session(call);
+ linphone_call_stats_uninit(&call->stats[0]);
+ linphone_call_stats_uninit(&call->stats[1]);
+}
+
+/*
+ * Called internally when reaching the Released state, to perform cleanups to break circular references.
+**/
+static void linphone_call_set_released(LinphoneCall *call){
+ if (call->op!=NULL) {
+ /*transfer the last error so that it can be obtained even in Released state*/
+ if (call->non_op_error.reason==SalReasonNone){
+ const SalErrorInfo *ei=sal_op_get_error_info(call->op);
+ sal_error_info_set(&call->non_op_error,ei->reason,ei->protocol_code,ei->status_string,ei->warnings);
+ }
+ /* so that we cannot have anymore upcalls for SAL
+ concerning this call*/
+ sal_op_release(call->op);
+ call->op=NULL;
+ }
+ /*it is necessary to reset pointers to other call to prevent circular references that would result in memory never freed.*/
+ if (call->referer){
+ linphone_call_unref(call->referer);
+ call->referer=NULL;
+ }
+ if (call->transfer_target){
+ linphone_call_unref(call->transfer_target);
+ call->transfer_target=NULL;
+ }
+ linphone_call_unref(call);
+}
+
+/* this function is called internally to get rid of a call that was notified to the application, because it reached the end or error state.
It performs the following tasks:
- remove the call from the internal list of calls
- update the call logs accordingly
@@ -587,12 +1204,9 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
static void linphone_call_set_terminated(LinphoneCall *call){
LinphoneCore *lc=call->core;
- linphone_core_update_allocated_audio_bandwidth(lc);
-
- call->owns_call_log=FALSE;
+ linphone_call_free_media_resources(call);
linphone_call_log_completed(call);
-
if (call == lc->current_call){
ms_message("Resetting the current call");
lc->current_call=NULL;
@@ -601,24 +1215,19 @@ static void linphone_call_set_terminated(LinphoneCall *call){
if (linphone_core_del_call(lc,call) != 0){
ms_error("Could not remove the call from the list !!!");
}
-
- if (ms_list_size(lc->calls)==0)
- linphone_core_notify_all_friends(lc,lc->presence_mode);
-
linphone_core_conference_check_uninit(lc);
if (call->ringing_beep){
linphone_core_stop_dtmf(lc);
call->ringing_beep=FALSE;
}
- if (call->referer){
- linphone_call_unref(call->referer);
- call->referer=NULL;
- }
}
void linphone_call_fix_call_parameters(LinphoneCall *call){
- call->params.has_video=call->current_params.has_video;
- call->params.media_encryption=call->current_params.media_encryption;
+ if (sal_call_is_offerer(call->op)) {
+ /*get remote params*/
+ const LinphoneCallParams* rcp = linphone_call_get_remote_params(call);
+ call->current_params->video_declined = call->params->has_video && !rcp->has_video;
+ }
}
const char *linphone_call_state_to_string(LinphoneCallState cs){
@@ -661,6 +1270,10 @@ const char *linphone_call_state_to_string(LinphoneCallState cs){
return "LinphoneCallUpdating";
case LinphoneCallReleased:
return "LinphoneCallReleased";
+ case LinphoneCallEarlyUpdatedByRemote:
+ return "LinphoneCallEarlyUpdatedByRemote";
+ case LinphoneCallEarlyUpdating:
+ return "LinphoneCallEarlyUpdating";
}
return "undefined state";
}
@@ -669,62 +1282,95 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
LinphoneCore *lc=call->core;
if (call->state!=cstate){
+ call->prevstate=call->state;
if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
if (cstate!=LinphoneCallReleased){
- ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
- linphone_call_state_to_string(cstate));
+ ms_fatal("Spurious call state change from %s to %s, ignored." ,linphone_call_state_to_string(call->state)
+ ,linphone_call_state_to_string(cstate));
return;
}
}
- ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
- linphone_call_state_to_string(cstate));
+ ms_message("Call %p: moving from state %s to %s",call
+ ,linphone_call_state_to_string(call->state)
+ ,linphone_call_state_to_string(cstate));
+
if (cstate!=LinphoneCallRefered){
/*LinphoneCallRefered is rather an event, not a state.
Indeed it does not change the state of the call (still paused or running)*/
call->state=cstate;
}
- if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
- switch(call->reason){
- case LinphoneReasonDeclined:
- call->log->status=LinphoneCallDeclined;
- break;
- case LinphoneReasonNotAnswered:
- call->log->status=LinphoneCallMissed;
+
+ switch (cstate) {
+ case LinphoneCallOutgoingInit:
+ case LinphoneCallIncomingReceived:
+#ifdef ANDROID
+ ms_message("Call [%p] acquires both wifi and multicast lock",call);
+ linphone_core_wifi_lock_acquire(call->core);
+ linphone_core_multicast_lock_acquire(call->core); /*does no affect battery more than regular rtp traffic*/
+#endif
+ break;
+ case LinphoneCallEnd:
+ case LinphoneCallError:
+ switch(call->non_op_error.reason){
+ case SalReasonDeclined:
+ call->log->status=LinphoneCallDeclined;
break;
- default:
+ case SalReasonRequestTimeout:
+ call->log->status=LinphoneCallMissed;
+ break;
+ default:
break;
}
- linphone_call_set_terminated (call);
- }
- if (cstate == LinphoneCallConnected) {
+ linphone_call_set_terminated(call);
+ break;
+ case LinphoneCallConnected:
call->log->status=LinphoneCallSuccess;
- call->media_start_time=time(NULL);
+ call->log->connected_date_time=time(NULL);
+ break;
+ case LinphoneCallReleased:
+#ifdef ANDROID
+ ms_message("Call [%p] releases wifi/multicast lock",call);
+ linphone_core_wifi_lock_release(call->core);
+ linphone_core_multicast_lock_release(call->core);
+#endif
+ break;
+ case LinphoneCallStreamsRunning:
+ if (call->prevstate == LinphoneCallUpdating || call->prevstate == LinphoneCallUpdatedByRemote) {
+ linphone_core_notify_display_status(lc,_("Call parameters were successfully modified."));
+ }
+ break;
+ default:
+ break;
}
- if (lc->vtable.call_state_changed)
- lc->vtable.call_state_changed(lc,call,cstate,message);
- if (cstate==LinphoneCallReleased){
- if (call->op!=NULL) {
- /* so that we cannot have anymore upcalls for SAL
- concerning this call*/
- sal_op_release(call->op);
- call->op=NULL;
+ if(cstate!=LinphoneCallStreamsRunning) {
+ if (call->dtmfs_timer!=NULL){
+ /*cancelling DTMF sequence, if any*/
+ linphone_call_cancel_dtmfs(call);
}
- linphone_call_unref(call);
}
+ linphone_core_notify_call_state_changed(lc,call,cstate,message);
+ linphone_reporting_call_state_updated(call);
+ if (cstate==LinphoneCallReleased) {/*shall be performed after app notification*/
+ linphone_call_set_released(call);
+ }
+ linphone_core_soundcard_hint_check(lc);
}
}
-static void linphone_call_destroy(LinphoneCall *obj)
-{
-#ifdef BUILD_UPNP
- linphone_call_delete_upnp_session(obj);
-#endif //BUILD_UPNP
- linphone_call_delete_ice_session(obj);
+static void linphone_call_destroy(LinphoneCall *obj){
+ ms_message("Call [%p] freed.",obj);
+ if (obj->audiostream || obj->videostream){
+ linphone_call_free_media_resources(obj);
+ }
if (obj->op!=NULL) {
sal_op_release(obj->op);
obj->op=NULL;
}
+ if (obj->biggestdesc!=NULL){
+ sal_media_description_unref(obj->biggestdesc);
+ obj->biggestdesc=NULL;
+ }
if (obj->resultdesc!=NULL) {
sal_media_description_unref(obj->resultdesc);
obj->resultdesc=NULL;
@@ -735,17 +1381,53 @@ static void linphone_call_destroy(LinphoneCall *obj)
}
if (obj->ping_op) {
sal_op_release(obj->ping_op);
+ obj->ping_op=NULL;
}
if (obj->refer_to){
ms_free(obj->refer_to);
+ obj->refer_to=NULL;
+ }
+ if (obj->referer){
+ linphone_call_unref(obj->referer);
+ obj->referer=NULL;
+ }
+ if (obj->transfer_target){
+ linphone_call_unref(obj->transfer_target);
+ obj->transfer_target=NULL;
+ }
+ if (obj->log) {
+ linphone_call_log_unref(obj->log);
+ obj->log=NULL;
}
- if (obj->owns_call_log)
- linphone_call_log_destroy(obj->log);
if (obj->auth_token) {
ms_free(obj->auth_token);
+ obj->auth_token=NULL;
}
- linphone_call_params_uninit(&obj->params);
- ms_free(obj);
+ if (obj->dtls_certificate_fingerprint) {
+ ms_free(obj->dtls_certificate_fingerprint);
+ obj->dtls_certificate_fingerprint=NULL;
+ }
+ if (obj->dtmfs_timer) {
+ linphone_call_cancel_dtmfs(obj);
+ }
+ if (obj->params){
+ linphone_call_params_unref(obj->params);
+ obj->params=NULL;
+ }
+ if (obj->current_params){
+ linphone_call_params_unref(obj->current_params);
+ obj->current_params=NULL;
+ }
+ if (obj->remote_params != NULL) {
+ linphone_call_params_unref(obj->remote_params);
+ obj->remote_params=NULL;
+ }
+ if (obj->me) {
+ linphone_address_unref(obj->me);
+ obj->me = NULL;
+ }
+
+ sal_error_info_reset(&obj->non_op_error);
}
/**
@@ -753,75 +1435,139 @@ static void linphone_call_destroy(LinphoneCall *obj)
* @{
**/
-/**
- * Increments the call 's reference count.
- * An application that wishes to retain a pointer to call object
- * must use this function to unsure the pointer remains
- * valid. Once the application no more needs this pointer,
- * it must call linphone_call_unref().
-**/
LinphoneCall * linphone_call_ref(LinphoneCall *obj){
- obj->refcnt++;
+ belle_sip_object_ref(obj);
return obj;
}
-/**
- * Decrements the call object reference count.
- * See linphone_call_ref().
-**/
void linphone_call_unref(LinphoneCall *obj){
- obj->refcnt--;
- if (obj->refcnt==0){
- linphone_call_destroy(obj);
- }
+ belle_sip_object_unref(obj);
+}
+static unsigned int linphone_call_get_n_active_streams(const LinphoneCall *call) {
+ SalMediaDescription *md=NULL;
+ if (call->op)
+ md = sal_call_get_remote_media_description(call->op);
+ if (!md)
+ return 0;
+ return sal_media_description_nb_active_streams_of_type(md, SalAudio) + sal_media_description_nb_active_streams_of_type(md, SalVideo);
}
/**
* Returns current parameters associated to the call.
**/
const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){
- if (call->params.record_file)
- call->current_params.record_file=call->params.record_file;
- return &call->current_params;
-}
+ SalMediaDescription *md=call->resultdesc;
+#ifdef VIDEO_ENABLED
+ VideoStream *vstream;
+#endif
+ MS_VIDEO_SIZE_ASSIGN(call->current_params->sent_vsize, UNKNOWN);
+ MS_VIDEO_SIZE_ASSIGN(call->current_params->recv_vsize, UNKNOWN);
+#ifdef VIDEO_ENABLED
+ vstream = call->videostream;
+ if (vstream != NULL) {
+ call->current_params->sent_vsize = video_stream_get_sent_video_size(vstream);
+ call->current_params->recv_vsize = video_stream_get_received_video_size(vstream);
+ call->current_params->sent_fps = video_stream_get_sent_framerate(vstream);
+ call->current_params->received_fps = video_stream_get_received_framerate(vstream);
+ }
+#endif
-static bool_t is_video_active(const SalStreamDescription *sd){
- return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
+ /* REVISITED
+ * Previous code was buggy.
+ * Relying on the mediastream's state (added by jehan: only) to know the current encryption is unreliable.
+ * For (added by jehan: both DTLS and) ZRTP it is though necessary.
+ * But for all others the current_params->media_encryption state should reflect (added by jehan: both) what is agreed by the offer/answer
+ * mechanism (added by jehan: and encryption status from media which is much stronger than only result of offer/answer )
+ * Typically there can be inactive streams for which the media layer has no idea of whether they are encrypted or not.
+ */
+
+ switch (call->params->media_encryption) {
+ case LinphoneMediaEncryptionZRTP:
+ if (linphone_call_all_streams_encrypted(call) && linphone_call_get_authentication_token(call)) {
+ call->current_params->media_encryption=LinphoneMediaEncryptionZRTP;
+ } else {
+ call->current_params->media_encryption=LinphoneMediaEncryptionNone;
+ }
+ break;
+ case LinphoneMediaEncryptionDTLS:
+ case LinphoneMediaEncryptionSRTP:
+ if (linphone_call_get_n_active_streams(call)==0 || linphone_call_all_streams_encrypted(call)) {
+ call->current_params->media_encryption = call->params->media_encryption;
+ } else {
+ call->current_params->media_encryption=LinphoneMediaEncryptionNone;
+ }
+ break;
+ case LinphoneMediaEncryptionNone:
+ call->current_params->media_encryption=LinphoneMediaEncryptionNone;
+ break;
+ }
+
+ call->current_params->avpf_enabled = linphone_call_all_streams_avpf_enabled(call);
+ if (call->current_params->avpf_enabled == TRUE) {
+ call->current_params->avpf_rr_interval = linphone_call_get_avpf_rr_interval(call);
+ } else {
+ call->current_params->avpf_rr_interval = 0;
+ }
+ if (md){
+ const char *rtp_addr;
+
+ SalStreamDescription *sd=sal_media_description_find_best_stream(md,SalAudio);
+ call->current_params->audio_dir=sd ? media_direction_from_sal_stream_dir(sd->dir) : LinphoneMediaDirectionInactive;
+ if (call->current_params->audio_dir != LinphoneMediaDirectionInactive) {
+ rtp_addr = sd->rtp_addr[0]!='\0' ? sd->rtp_addr : call->resultdesc->addr;
+ call->current_params->audio_multicast_enabled = ms_is_multicast(rtp_addr);
+ } else
+ call->current_params->audio_multicast_enabled = FALSE;
+
+ sd=sal_media_description_find_best_stream(md,SalVideo);
+ call->current_params->video_dir=sd ? media_direction_from_sal_stream_dir(sd->dir) : LinphoneMediaDirectionInactive;
+ if (call->current_params->video_dir != LinphoneMediaDirectionInactive) {
+ rtp_addr = sd->rtp_addr[0]!='\0' ? sd->rtp_addr : call->resultdesc->addr;
+ call->current_params->video_multicast_enabled = ms_is_multicast(rtp_addr);
+ } else
+ call->current_params->video_multicast_enabled = FALSE;
+ }
+
+ return call->current_params;
}
/**
* Returns call parameters proposed by remote.
- *
+ *
* This is useful when receiving an incoming call, to know whether the remote party
* supports video, encryption or whatever.
**/
const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
- LinphoneCallParams *cp=&call->remote_params;
- memset(cp,0,sizeof(*cp));
if (call->op){
- SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
- if (md){
- SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
+ LinphoneCallParams *cp;
+ SalMediaDescription *md;
+ if (call->remote_params != NULL) linphone_call_params_unref(call->remote_params);
+ cp = call->remote_params = linphone_call_params_new();
+ md=sal_call_get_remote_media_description(call->op);
+ if (md) {
+ SalStreamDescription *sd;
+ unsigned int i;
+ unsigned int nb_audio_streams = sal_media_description_nb_active_streams_of_type(md, SalAudio);
+ unsigned int nb_video_streams = sal_media_description_nb_active_streams_of_type(md, SalVideo);
- asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
- vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
- secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
- secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
- if (secure_vsd){
- cp->has_video=is_video_active(secure_vsd);
- if (secure_asd || asd==NULL)
- cp->media_encryption=LinphoneMediaEncryptionSRTP;
- }else if (vsd){
- cp->has_video=is_video_active(vsd);
+ for (i = 0; i < nb_video_streams; i++) {
+ sd = sal_media_description_get_active_stream_of_type(md, SalVideo, i);
+ if (sal_stream_description_active(sd) == TRUE) cp->has_video = TRUE;
+ if (sal_stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP;
+ }
+ for (i = 0; i < nb_audio_streams; i++) {
+ sd = sal_media_description_get_active_stream_of_type(md, SalAudio, i);
+ if (sal_stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP;
}
if (!cp->has_video){
if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){
cp->low_bandwidth=TRUE;
}
}
- cp->custom_headers=(SalCustomHeader*)sal_op_get_custom_header(call->op);
- return cp;
+ if (md->name[0]!='\0') linphone_call_params_set_session_name(cp,md->name);
}
+ cp->custom_headers=sal_custom_header_clone((SalCustomHeader*)sal_op_get_recv_custom_header(call->op));
+ return cp;
}
return NULL;
}
@@ -854,31 +1600,39 @@ LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
* Returns the reason for a call termination (either error or normal termination)
**/
LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
- return call->reason;
+ return linphone_error_info_get_reason(linphone_call_get_error_info(call));
}
/**
- * Get the user_pointer in the LinphoneCall
- *
- * @ingroup call_control
- *
- * return user_pointer an opaque user pointer that can be retrieved at any time
+ * Returns full details about call errors or termination reasons.
**/
-void *linphone_call_get_user_pointer(LinphoneCall *call)
-{
- return call->user_pointer;
+const LinphoneErrorInfo *linphone_call_get_error_info(const LinphoneCall *call){
+ if (call->non_op_error.reason!=SalReasonNone){
+ return (const LinphoneErrorInfo*)&call->non_op_error;
+ }else return linphone_error_info_from_sal_op(call->op);
}
/**
- * Set the user_pointer in the LinphoneCall
+ * Get the user pointer associated with the LinphoneCall
+ *
+ * @ingroup call_control
+ * @return an opaque user pointer that can be retrieved at any time
+**/
+void *linphone_call_get_user_data(const LinphoneCall *call)
+{
+ return call->user_data;
+}
+
+/**
+ * Set the user pointer associated with the LinphoneCall
*
* @ingroup call_control
*
- * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
+ * the user pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
**/
-void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
+void linphone_call_set_user_data(LinphoneCall *call, void *user_pointer)
{
- call->user_pointer = user_pointer;
+ call->user_data = user_pointer;
}
/**
@@ -895,6 +1649,21 @@ const char *linphone_call_get_refer_to(const LinphoneCall *call){
return call->refer_to;
}
+/**
+ * Returns the transferer if this call was started automatically as a result of an incoming transfer request.
+ * The call in which the transfer request was received is returned in this case.
+**/
+LinphoneCall *linphone_call_get_transferer_call(const LinphoneCall *call){
+ return call->referer;
+}
+
+/**
+ * When this call has received a transfer request, returns the new call that was automatically created as a result of the transfer.
+**/
+LinphoneCall *linphone_call_get_transfer_target_call(const LinphoneCall *call){
+ return call->transfer_target;
+}
+
/**
* Returns direction of the call (incoming or outgoing).
**/
@@ -916,12 +1685,17 @@ const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
* Returns the far end's sip contact as a string, if available.
**/
const char *linphone_call_get_remote_contact(LinphoneCall *call){
- if (call->op){
- return sal_op_get_remote_contact(call->op);
+ const LinphoneCallParams* lcp = linphone_call_get_remote_params(call);
+ if( lcp ){
+ // we're not using sal_op_get_remote_contact() here because the returned value is stripped from
+ // params that we need, like the instanceid. Getting it from the headers will make sure we
+ // get everything
+ return linphone_call_params_get_custom_header(lcp, "Contact");
}
return NULL;
}
+
/**
* Returns true if this calls has received a transfer that has not been
* executed yet.
@@ -938,8 +1712,8 @@ bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
* Returns call's duration in seconds.
**/
int linphone_call_get_duration(const LinphoneCall *call){
- if (call->media_start_time==0) return 0;
- return time(NULL)-call->media_start_time;
+ if (call->log->connected_date_time==0) return 0;
+ return time(NULL)-call->log->connected_date_time;
}
/**
@@ -961,32 +1735,45 @@ LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
**/
void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
#ifdef VIDEO_ENABLED
- if (call->videostream!=NULL && call->videostream->ms.ticker!=NULL){
- LinphoneCore *lc=call->core;
- MSWebCam *nowebcam=get_nowebcam_device();
- if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
- video_stream_change_camera(call->videostream,
- enable ? lc->video_conf.device : nowebcam);
+ call->camera_enabled=enable;
+ if ((call->state==LinphoneCallStreamsRunning || call->state==LinphoneCallOutgoingEarlyMedia || call->state==LinphoneCallIncomingEarlyMedia)
+ && call->videostream!=NULL && video_stream_started(call->videostream) ){
+ if (video_stream_get_camera(call->videostream) != linphone_call_get_video_device(call)) {
+ const char *cur_cam, *new_cam;
+ cur_cam = video_stream_get_camera(call->videostream) ? ms_web_cam_get_name(video_stream_get_camera(call->videostream)) : "NULL";
+ new_cam = linphone_call_get_video_device(call) ? ms_web_cam_get_name(linphone_call_get_video_device(call)) : "NULL";
+ ms_message("Switching video cam from [%s] to [%s] on call [%p]" , cur_cam, new_cam, call);
+ video_stream_change_camera(call->videostream, linphone_call_get_video_device(call));
}
}
- call->camera_active=enable;
#endif
}
-#ifdef VIDEO_ENABLED
/**
* Request remote side to send us a Video Fast Update.
**/
-void linphone_call_send_vfu_request(LinphoneCall *call)
-{
- if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
- sal_call_send_vfu_request(call->op);
-}
+void linphone_call_send_vfu_request(LinphoneCall *call) {
+#ifdef VIDEO_ENABLED
+ const LinphoneCallParams *current_params = linphone_call_get_current_params(call);
+ if (current_params->avpf_enabled && call->videostream && media_stream_get_state((const MediaStream *)call->videostream) == MSStreamStarted) {
+ ms_message("Request Full Intra Request on call [%p]", call);
+ video_stream_send_fir(call->videostream);
+ } else if (call->core->sip_conf.vfu_with_info) {
+ if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
+ sal_call_send_vfu_request(call->op);
+ } else {
+ ms_message("vfu request using sip disabled from config [sip,vfu_with_info]");
+ }
#endif
+}
/**
* Take a photo of currently received video and write it into a jpeg file.
+ * Note that the snapshot is asynchronous, an application shall not assume that the file is created when the function returns.
+ * @param call a LinphoneCall
+ * @param file a path where to write the jpeg content.
+ * @return 0 if successfull, -1 otherwise (typically if jpeg format is not supported).
**/
int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
#ifdef VIDEO_ENABLED
@@ -994,153 +1781,51 @@ int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
}
ms_warning("Cannot take snapshot: no currently running video stream on this call.");
+#endif
+ return -1;
+}
+
+/**
+ * Take a photo of currently captured video and write it into a jpeg file.
+ * Note that the snapshot is asynchronous, an application shall not assume that the file is created when the function returns.
+ * @param call a LinphoneCall
+ * @param file a path where to write the jpeg content.
+ * @return 0 if successfull, -1 otherwise (typically if jpeg format is not supported).
+**/
+int linphone_call_take_preview_snapshot(LinphoneCall *call, const char *file){
+#ifdef VIDEO_ENABLED
+ if (call->videostream!=NULL && call->videostream->local_jpegwriter!=NULL){
+ return ms_filter_call_method(call->videostream->local_jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
+ }
+ ms_warning("Cannot take local snapshot: no currently running video stream on this call.");
return -1;
#endif
return -1;
}
/**
- * Returns TRUE if camera pictures are sent to the remote party.
+ * Returns TRUE if camera pictures are allowed to be sent to the remote party.
**/
bool_t linphone_call_camera_enabled (const LinphoneCall *call){
- return call->camera_active;
+ return call->camera_enabled;
}
-/**
- * Enable video stream.
-**/
-void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
- cp->has_video=enabled;
-}
-
-/**
- * Returns the audio codec used in the call, described as a PayloadType structure.
-**/
-const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
- return cp->audio_codec;
-}
-
-
-/**
- * Returns the video codec used in the call, described as a PayloadType structure.
-**/
-const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
- return cp->video_codec;
-}
/**
* @ingroup call_control
- * Use to know if this call has been configured in low bandwidth mode.
- * This mode can be automatically discovered thanks to a stun server when activate_edge_workarounds=1 in section [net] of configuration file.
- * An application that would have reliable way to know network capacity may not use activate_edge_workarounds=1 but instead manually configure
- * low bandwidth mode with linphone_call_params_enable_low_bandwidth().
- * When enabled, this param may transform a call request with video in audio only mode.
- * @return TRUE if low bandwidth has been configured/detected
- */
-bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
- return cp->low_bandwidth;
-}
-
-/**
- * @ingroup call_control
- * Indicate low bandwith mode.
- * Configuring a call to low bandwidth mode will result in the core to activate several settings for the call in order to ensure that bitrate usage
- * is lowered to the minimum possible. Typically, ptime (packetization time) will be increased, audio codec's output bitrate will be targetted to 20kbit/s provided
- * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled.
- *
-**/
-void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){
- cp->low_bandwidth=enabled;
-}
-
-/**
- * Returns whether video is enabled.
-**/
-bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
- return cp->has_video;
-}
-
-/**
- * Returns kind of media encryption selected for the call.
-**/
-enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
- return cp->media_encryption;
-}
-
-/**
- * Set requested media encryption for a call.
-**/
-void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
- cp->media_encryption = e;
-}
-
-
-/**
- * Enable sending of real early media (during outgoing calls).
-**/
-void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
- cp->real_early_media=enabled;
-}
-
-/**
- * Indicates whether sending of early media was enabled.
-**/
-bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
- return cp->real_early_media;
-}
-
-/**
- * Returns true if the call is part of the locally managed conference.
-**/
-bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
- return cp->in_conference;
-}
-
-/**
- * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
- * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
-**/
-void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
- cp->audio_bw=bandwidth;
-}
-
-void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char *header_value){
- params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value);
-}
-
-const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name){
- return sal_custom_header_find(params->custom_headers,header_name);
-}
-
-void _linphone_call_params_copy(LinphoneCallParams *ncp, const LinphoneCallParams *cp){
- memcpy(ncp,cp,sizeof(LinphoneCallParams));
- if (cp->record_file) ncp->record_file=ms_strdup(cp->record_file);
- /*
- * 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);
-}
-
-/**
- * Copy existing LinphoneCallParams to a new LinphoneCallParams object.
-**/
-LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
- LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
- _linphone_call_params_copy(ncp,cp);
- return ncp;
-}
-
-void linphone_call_params_uninit(LinphoneCallParams *p){
- if (p->record_file) ms_free(p->record_file);
- if (p->custom_headers) sal_custom_header_free(p->custom_headers);
-}
-
-/**
- * Destroy LinphoneCallParams.
-**/
-void linphone_call_params_destroy(LinphoneCallParams *p){
- linphone_call_params_uninit(p);
- ms_free(p);
+ * @return string value of LinphonePrivacy enum
+ **/
+const char* linphone_privacy_to_string(LinphonePrivacy privacy) {
+ switch(privacy) {
+ case LinphonePrivacyDefault: return "LinphonePrivacyDefault";
+ case LinphonePrivacyUser: return "LinphonePrivacyUser";
+ case LinphonePrivacyHeader: return "LinphonePrivacyHeader";
+ case LinphonePrivacySession: return "LinphonePrivacySession";
+ case LinphonePrivacyId: return "LinphonePrivacyId";
+ case LinphonePrivacyNone: return "LinphonePrivacyNone";
+ case LinphonePrivacyCritical: return "LinphonePrivacyCritical";
+ default: return "Unknown privacy mode";
+ }
}
@@ -1152,23 +1837,36 @@ void linphone_call_params_destroy(LinphoneCallParams *p){
#ifdef TEST_EXT_RENDERER
static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
ms_message("rendercb, local buffer=%p, remote buffer=%p",
- local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
+ local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
}
#endif
#ifdef VIDEO_ENABLED
static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
- LinphoneCall* call = (LinphoneCall*) user_pointer;
- ms_warning("In linphonecall.c: video_stream_event_cb");
+ LinphoneCall* call = (LinphoneCall*) user_pointer;
switch (event_id) {
case MS_VIDEO_DECODER_DECODING_ERRORS:
- ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
- linphone_call_send_vfu_request(call);
+ ms_warning("MS_VIDEO_DECODER_DECODING_ERRORS");
+ if (call->videostream && (video_stream_is_decoding_error_to_be_reported(call->videostream, 5000) == TRUE)) {
+ video_stream_decoding_error_reported(call->videostream);
+ linphone_call_send_vfu_request(call);
+ }
+ break;
+ case MS_VIDEO_DECODER_RECOVERED_FROM_ERRORS:
+ ms_message("MS_VIDEO_DECODER_RECOVERED_FROM_ERRORS");
+ if (call->videostream) {
+ video_stream_decoding_error_recovered(call->videostream);
+ }
break;
case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
ms_message("First video frame decoded successfully");
if (call->nextVideoFrameDecoded._func != NULL)
- call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
+ call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
+ break;
+ case MS_VIDEO_DECODER_SEND_PLI:
+ case MS_VIDEO_DECODER_SEND_SLI:
+ case MS_VIDEO_DECODER_SEND_RPSI:
+ /* Handled internally by mediastreamer2. */
break;
default:
ms_warning("Unhandled event %i", event_id);
@@ -1181,17 +1879,169 @@ void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, Lin
call->nextVideoFrameDecoded._func = cb;
call->nextVideoFrameDecoded._user_data = user_data;
#ifdef VIDEO_ENABLED
- ms_filter_call_method_noarg(call->videostream->ms.decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
+ if (call->videostream && call->videostream->ms.decoder)
+ ms_filter_call_method_noarg(call->videostream->ms.decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
#endif
}
+static void port_config_set_random_choosed(LinphoneCall *call, int stream_index, RtpSession *session){
+ call->media_ports[stream_index].rtp_port=rtp_session_get_local_port(session);
+ call->media_ports[stream_index].rtcp_port=rtp_session_get_local_rtcp_port(session);
+}
+
+static void _linphone_call_prepare_ice_for_stream(LinphoneCall *call, int stream_index, bool_t create_checklist){
+ MediaStream *ms=stream_index == 0 ? (MediaStream*)call->audiostream : (MediaStream*)call->videostream;
+ if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
+ IceCheckList *cl;
+ rtp_session_set_pktinfo(ms->sessions.rtp_session, TRUE);
+ cl=ice_session_check_list(call->ice_session, stream_index);
+ if (cl == NULL && create_checklist) {
+ cl=ice_check_list_new();
+ ice_session_add_check_list(call->ice_session, cl, stream_index);
+ ms_message("Created new ICE check list for stream [%i]",stream_index);
+ }
+ if (cl){
+ ms->ice_check_list = cl;
+ ice_check_list_set_rtp_session(ms->ice_check_list, ms->sessions.rtp_session);
+ }
+ }
+}
+
+int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){
+ SalMediaDescription *remote = NULL;
+ bool_t has_video=FALSE;
+
+ if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
+ if (incoming_offer){
+ remote=sal_call_get_remote_media_description(call->op);
+ has_video=linphone_core_video_enabled(call->core) && linphone_core_media_description_contains_video_stream(remote);
+ }else has_video=call->params->has_video;
+
+ _linphone_call_prepare_ice_for_stream(call,0,TRUE);
+ if (has_video) _linphone_call_prepare_ice_for_stream(call,1,TRUE);
+ /*start ICE gathering*/
+ if (incoming_offer)
+ linphone_call_update_ice_from_remote_media_description(call,remote); /*this may delete the ice session*/
+ if (call->ice_session && !ice_session_candidates_gathered(call->ice_session)){
+ if (call->audiostream->ms.state==MSStreamInitialized)
+ audio_stream_prepare_sound(call->audiostream, NULL, NULL);
+#ifdef VIDEO_ENABLED
+ if (has_video && call->videostream && call->videostream->ms.state==MSStreamInitialized) {
+ video_stream_prepare_video(call->videostream);
+ }
+#endif
+ if (linphone_core_gather_ice_candidates(call->core,call)<0) {
+ /* Ice candidates gathering failed, proceed with the call anyway. */
+ linphone_call_delete_ice_session(call);
+ linphone_call_stop_media_streams_for_ice_gathering(call);
+ return -1;
+ }
+ return 1;/*gathering in progress, wait*/
+ }
+ }
+ return 0;
+}
+
+/*eventually join to a multicast group if told to do so*/
+static void linphone_call_join_multicast_group(LinphoneCall *call, int stream_index, MediaStream *ms){
+ if (call->media_ports[stream_index].multicast_ip[stream_index]!='\0'){
+ media_stream_join_multicast_group(ms, call->media_ports[stream_index].multicast_ip);
+ } else
+ ms_error("Cannot join multicast group if multicast ip is not set for call [%p]",call);
+}
+
+static SalMulticastRole linphone_call_get_multicast_role(const LinphoneCall *call,SalStreamType type) {
+ SalMulticastRole multicast_role=SalMulticastInactive;
+ SalMediaDescription *remotedesc, *localdesc;
+ SalStreamDescription *stream_desc = NULL;
+ if (!call->op) goto end;
+ remotedesc = sal_call_get_remote_media_description(call->op);
+ localdesc = call->localdesc;
+ if (!localdesc && !remotedesc && call->dir == LinphoneCallOutgoing) {
+ /*well using call dir*/
+ if ((type == SalAudio && linphone_call_params_audio_multicast_enabled(call->params))
+ || (type == SalVideo && linphone_call_params_video_multicast_enabled(call->params)))
+ multicast_role=SalMulticastSender;
+ } else if (localdesc && (!remotedesc || sal_call_is_offerer(call->op))) {
+ stream_desc = sal_media_description_find_best_stream(localdesc, type);
+ } else if (!sal_call_is_offerer(call->op) && remotedesc)
+ stream_desc = sal_media_description_find_best_stream(remotedesc, type);
+
+ if (stream_desc)
+ multicast_role=stream_desc->multicast_role;
+ else
+ ms_message("Cannot determine multicast role for stream type [%s] on call [%p]",sal_stream_type_to_string(type),call);
+
+
+ end:
+ return multicast_role;
+
+}
+
+static void setup_dtls_params(LinphoneCall *call, MediaStream* stream) {
+ LinphoneCore *lc=call->core;
+ if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) {
+ MSDtlsSrtpParams params;
+ char *certificate, *key;
+ memset(¶ms,0,sizeof(MSDtlsSrtpParams));
+ /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */
+ /* This will parse the directory to find a matching fingerprint or generate it if not found */
+ /* returned string must be freed */
+ sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE);
+
+ if (key!= NULL && certificate!=NULL) {
+ params.pem_certificate = (char *)certificate;
+ params.pem_pkey = (char *)key;
+ params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */
+ media_stream_enable_dtls(stream,¶ms);
+ ms_free(certificate);
+ ms_free(key);
+ } else {
+ ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled");
+ /* TODO : check if encryption forced, if yes, stop call */
+ }
+ }
+}
+
void linphone_call_init_audio_stream(LinphoneCall *call){
LinphoneCore *lc=call->core;
AudioStream *audiostream;
+ const char *location;
int dscp;
+ char rtcp_tool[128]={0};
+ char* cname;
+
+
+ snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
if (call->audiostream != NULL) return;
- call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc));
+ if (call->sessions[0].rtp_session==NULL){
+ SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalAudio);
+ SalMediaDescription *remotedesc=NULL;
+ SalStreamDescription *stream_desc = NULL;
+ if (call->op) remotedesc = sal_call_get_remote_media_description(call->op);
+ if (remotedesc)
+ stream_desc = sal_media_description_find_best_stream(remotedesc, SalAudio);
+
+ call->audiostream=audiostream=audio_stream_new2(linphone_call_get_bind_ip_for_stream(call,0),
+ multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[0].rtp_port,
+ multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[0].rtcp_port);
+ if (multicast_role == SalMulticastReceiver)
+ linphone_call_join_multicast_group(call, 0, &audiostream->ms);
+ rtp_session_enable_network_simulation(call->audiostream->ms.sessions.rtp_session, &lc->net_conf.netsim_params);
+ cname = linphone_address_as_string_uri_only(call->me);
+ audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
+ ms_free(cname);
+ rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc));
+ setup_dtls_params(call, &audiostream->ms);
+ media_stream_reclaim_sessions(&audiostream->ms, &call->sessions[0]);
+ }else{
+ call->audiostream=audio_stream_new_with_sessions(&call->sessions[0]);
+ }
+ audiostream=call->audiostream;
+ if (call->media_ports[0].rtp_port==-1){
+ port_config_set_random_choosed(call,0,audiostream->ms.sessions.rtp_session);
+ }
dscp=linphone_core_get_audio_dscp(lc);
if (dscp!=-1)
audio_stream_set_dscp(audiostream,dscp);
@@ -1202,16 +2052,27 @@ void linphone_call_init_audio_stream(LinphoneCall *call){
else if (strcasecmp(type,"full")==0)
audio_stream_enable_echo_limiter(audiostream,ELControlFull);
}
+
+ /* equalizer location in the graph: 'mic' = in input graph, otherwise in output graph.
+ Any other value than mic will default to output graph for compatibility */
+ location = lp_config_get_string(lc->config,"sound","eq_location","hp");
+ audiostream->eq_loc = (strcasecmp(location,"mic") == 0) ? MSEqualizerMic : MSEqualizerHP;
+ ms_message("Equalizer location: %s", location);
+
audio_stream_enable_gain_control(audiostream,TRUE);
if (linphone_core_echo_cancellation_enabled(lc)){
int len,delay,framesize;
- const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
- if (statestr && audiostream->ec){
- ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
+ if (audiostream->ec) {
+ char *statestr=ms_malloc0(EC_STATE_MAX_LEN);
+ if (lp_config_relative_file_exists(lc->config, EC_STATE_STORE)
+ && lp_config_read_relative_file(lc->config, EC_STATE_STORE, statestr, EC_STATE_MAX_LEN) == 0) {
+ ms_filter_call_method(audiostream->ec, MS_ECHO_CANCELLER_SET_STATE_STRING, statestr);
+ }
+ ms_free(statestr);
}
}
audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
@@ -1223,62 +2084,89 @@ void linphone_call_init_audio_stream(LinphoneCall *call){
audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc));
if (lc->rtptf){
- RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port);
- RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1);
- rtp_session_set_transports(audiostream->ms.session,artp,artcp);
- }
- if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
- rtp_session_set_pktinfo(audiostream->ms.session, TRUE);
- rtp_session_set_symmetric_rtp(audiostream->ms.session, FALSE);
- if (ice_session_check_list(call->ice_session, 0) == NULL) {
- ice_session_add_check_list(call->ice_session, ice_check_list_new());
+ RtpTransport *meta_rtp;
+ RtpTransport *meta_rtcp;
+
+ rtp_session_get_transports(audiostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp);
+ if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) {
+ meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port));
+ }
+ if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) {
+ meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port));
}
- audiostream->ms.ice_check_list = ice_session_check_list(call->ice_session, 0);
- ice_check_list_set_rtp_session(audiostream->ms.ice_check_list, audiostream->ms.session);
}
call->audiostream_app_evq = ortp_ev_queue_new();
- rtp_session_register_event_queue(audiostream->ms.session,call->audiostream_app_evq);
+ rtp_session_register_event_queue(audiostream->ms.sessions.rtp_session,call->audiostream_app_evq);
+
+ _linphone_call_prepare_ice_for_stream(call,0,FALSE);
}
void linphone_call_init_video_stream(LinphoneCall *call){
#ifdef VIDEO_ENABLED
LinphoneCore *lc=call->core;
+ char* cname;
+ char rtcp_tool[128];
- if (!call->params.has_video) {
- linphone_call_stop_video_stream(call);
- return;
- }
- if (call->videostream != NULL) return;
- if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){
+
+ snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
+
+ if (call->videostream == NULL){
int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
int dscp=linphone_core_get_video_dscp(lc);
-
- call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc));
+ const char *display_filter=linphone_core_get_video_display_filter(lc);
+
+ if (call->sessions[1].rtp_session==NULL){
+ SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalVideo);
+ SalMediaDescription *remotedesc=NULL;
+ SalStreamDescription *stream_desc = NULL;
+ if (call->op) remotedesc = sal_call_get_remote_media_description(call->op);
+ if (remotedesc)
+ stream_desc = sal_media_description_find_best_stream(remotedesc, SalVideo);
+
+ call->videostream=video_stream_new2(linphone_call_get_bind_ip_for_stream(call,1),
+ multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[1].rtp_port,
+ multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[1].rtcp_port);
+ if (multicast_role == SalMulticastReceiver)
+ linphone_call_join_multicast_group(call, 1, &call->videostream->ms);
+ rtp_session_enable_network_simulation(call->videostream->ms.sessions.rtp_session, &lc->net_conf.netsim_params);
+ cname = linphone_address_as_string_uri_only(call->me);
+ video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool);
+ ms_free(cname);
+ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc));
+ setup_dtls_params(call, &call->videostream->ms);
+ media_stream_reclaim_sessions(&call->videostream->ms, &call->sessions[1]);
+ }else{
+ call->videostream=video_stream_new_with_sessions(&call->sessions[1]);
+ }
+
+ if (call->media_ports[1].rtp_port==-1){
+ port_config_set_random_choosed(call,1,call->videostream->ms.sessions.rtp_session);
+ }
if (dscp!=-1)
video_stream_set_dscp(call->videostream,dscp);
video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
- if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->ms.session,video_recv_buf_size);
+ if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->ms.sessions.rtp_session,video_recv_buf_size);
- if( lc->video_conf.displaytype != NULL)
- video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
+ if (display_filter != NULL)
+ video_stream_set_display_filter_name(call->videostream,display_filter);
video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
+
if (lc->rtptf){
- RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
- RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
- rtp_session_set_transports(call->videostream->ms.session,vrtp,vrtcp);
- }
- if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
- rtp_session_set_pktinfo(call->videostream->ms.session, TRUE);
- rtp_session_set_symmetric_rtp(call->videostream->ms.session, FALSE);
- if (ice_session_check_list(call->ice_session, 1) == NULL) {
- ice_session_add_check_list(call->ice_session, ice_check_list_new());
+ RtpTransport *meta_rtp;
+ RtpTransport *meta_rtcp;
+
+ rtp_session_get_transports(call->videostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp);
+ if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) {
+ meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port));
+ }
+ if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) {
+ meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port));
}
- call->videostream->ms.ice_check_list = ice_session_check_list(call->ice_session, 1);
- ice_check_list_set_rtp_session(call->videostream->ms.ice_check_list, call->videostream->ms.session);
}
call->videostream_app_evq = ortp_ev_queue_new();
- rtp_session_register_event_queue(call->videostream->ms.session,call->videostream_app_evq);
+ rtp_session_register_event_queue(call->videostream->ms.sessions.rtp_session,call->videostream_app_evq);
+ _linphone_call_prepare_ice_for_stream(call,1,FALSE);
#ifdef TEST_EXT_RENDERER
video_stream_set_render_callback(call->videostream,rendercb,NULL);
#endif
@@ -1296,13 +2184,12 @@ void linphone_call_init_media_streams(LinphoneCall *call){
static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
-static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){
+static void linphone_core_dtmf_received(LinphoneCall *call, int dtmf){
if (dtmf<0 || dtmf>15){
ms_warning("Bad dtmf value %i",dtmf);
return;
}
- if (lc->vtable.dtmf_received != NULL)
- lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
+ linphone_core_notify_dtmf_received(call->core, call, dtmf_tab[dtmf]);
}
static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
@@ -1327,6 +2214,17 @@ static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
}
}
+void set_mic_gain_db(AudioStream *st, float gain){
+ audio_stream_set_mic_gain_db(st, gain);
+}
+
+void set_playback_gain_db(AudioStream *st, float gain){
+ if (st->volrecv){
+ ms_filter_call_method(st->volrecv,MS_VOLUME_SET_DB_GAIN,&gain);
+ }else ms_warning("Could not apply playback gain: gain control wasn't activated.");
+}
+
+/*This function is not static because used internally in linphone-daemon project*/
void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
float mic_gain=lc->sound_conf.soft_mic_lev;
float thres = 0;
@@ -1343,15 +2241,15 @@ void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t mute
int spk_agc;
if (!muted)
- linphone_core_set_mic_gain_db (lc, mic_gain);
+ set_mic_gain_db(st,mic_gain);
else
audio_stream_set_mic_gain(st,0);
recv_gain = lc->sound_conf.soft_play_lev;
if (recv_gain != 0) {
- linphone_core_set_playback_gain_db (lc,recv_gain);
+ set_playback_gain_db(st,recv_gain);
}
-
+
if (st->volsend){
ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
@@ -1385,35 +2283,92 @@ void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t mute
parametrize_equalizer(lc,st);
}
-static void post_configure_audio_streams(LinphoneCall*call){
+static void post_configure_audio_streams(LinphoneCall *call, bool_t muted){
AudioStream *st=call->audiostream;
LinphoneCore *lc=call->core;
- _post_configure_audio_stream(st,lc,call->audio_muted);
- if (lc->vtable.dtmf_received!=NULL){
+ _post_configure_audio_stream(st,lc,muted);
+ if (linphone_core_dtmf_received_has_listener(lc)){
audio_stream_play_received_dtmfs(call->audiostream,FALSE);
}
- if (call->record_active)
+ if (call->record_active)
linphone_call_start_recording(call);
}
-static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
+static int get_ideal_audio_bw(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc){
+ int remote_bw=0;
+ int upload_bw;
+ int total_upload_bw=linphone_core_get_upload_bandwidth(call->core);
+ const LinphoneCallParams *params=call->params;
+ bool_t will_use_video=linphone_core_media_description_contains_video_stream(md);
+ bool_t forced=FALSE;
+
+ if (desc->bandwidth>0) remote_bw=desc->bandwidth;
+ else if (md->bandwidth>0) {
+ /*case where b=AS is given globally, not per stream*/
+ remote_bw=md->bandwidth;
+ }
+ if (params->up_bw>0){
+ forced=TRUE;
+ upload_bw=params->up_bw;
+ }else upload_bw=total_upload_bw;
+ upload_bw=get_min_bandwidth(upload_bw,remote_bw);
+ if (!will_use_video || forced) return upload_bw;
+
+ if (bandwidth_is_greater(upload_bw,512)){
+ upload_bw=100;
+ }else if (bandwidth_is_greater(upload_bw,256)){
+ upload_bw=64;
+ }else if (bandwidth_is_greater(upload_bw,128)){
+ upload_bw=40;
+ }else if (bandwidth_is_greater(upload_bw,0)){
+ upload_bw=24;
+ }
+ return upload_bw;
+}
+
+static int get_video_bw(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc){
+ int remote_bw=0;
int bw;
+ if (desc->bandwidth>0) remote_bw=desc->bandwidth;
+ else if (md->bandwidth>0) {
+ /*case where b=AS is given globally, not per stream*/
+ remote_bw=get_remaining_bandwidth_for_video(md->bandwidth,call->audio_bw);
+ } else {
+ remote_bw = lp_config_get_int(call->core->config, "net", "default_max_bandwidth", 1500);
+ }
+ bw=get_min_bandwidth(get_remaining_bandwidth_for_video(linphone_core_get_upload_bandwidth(call->core),call->audio_bw),remote_bw);
+ return bw;
+}
+
+static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
+ int bw=0;
const MSList *elem;
RtpProfile *prof=rtp_profile_new("Call profile");
bool_t first=TRUE;
- int remote_bw=0;
LinphoneCore *lc=call->core;
int up_ptime=0;
- const LinphoneCallParams *params=&call->params;
+ const LinphoneCallParams *params=call->params;
+
*used_pt=-1;
+ if (desc->type==SalAudio)
+ bw=get_ideal_audio_bw(call,md,desc);
+ else if (desc->type==SalVideo)
+ bw=get_video_bw(call,md,desc);
+
for(elem=desc->payloads;elem!=NULL;elem=elem->next){
PayloadType *pt=(PayloadType*)elem->data;
int number;
+ /* make a copy of the payload type, so that we left the ones from the SalStreamDescription unchanged.
+ If the SalStreamDescription is freed, this will have no impact on the running streams*/
+ pt=payload_type_clone(pt);
if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
+ /*first codec in list is the selected one*/
if (desc->type==SalAudio){
- linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
+ /*this will update call->audio_bw*/
+ linphone_core_update_allocated_audio_bandwidth_in_call(call,pt,bw);
+ bw=call->audio_bw;
if (params->up_ptime)
up_ptime=params->up_ptime;
else up_ptime=linphone_core_get_upload_ptime(lc);
@@ -1421,27 +2376,10 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m
*used_pt=payload_type_get_number(pt);
first=FALSE;
}
- if (desc->bandwidth>0) remote_bw=desc->bandwidth;
- else if (md->bandwidth>0) {
- /*case where b=AS is given globally, not per stream*/
- remote_bw=md->bandwidth;
- if (desc->type==SalVideo){
- remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
- }
- }
-
- if (desc->type==SalAudio){
- int audio_bw=call->audio_bw;
- if (params->up_bw){
- if (params->up_bw< audio_bw)
- audio_bw=params->up_bw;
- }
- bw=get_min_bandwidth(audio_bw,remote_bw);
- }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
- if (bw>0) pt->normal_bitrate=bw*1000;
- else if (desc->type==SalAudio){
- pt->normal_bitrate=-1;
- }
+ if (pt->flags & PAYLOAD_TYPE_BITRATE_OVERRIDE){
+ ms_message("Payload type [%s/%i] has explicit bitrate [%i] kbit/s", pt->mime_type, pt->clock_rate, pt->normal_bitrate/1000);
+ pt->normal_bitrate=get_min_bandwidth(pt->normal_bitrate,bw*1000);
+ } else pt->normal_bitrate=bw*1000;
if (desc->ptime>0){
up_ptime=desc->ptime;
}
@@ -1469,19 +2407,172 @@ static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
static bool_t linphone_call_sound_resources_available(LinphoneCall *call){
LinphoneCore *lc=call->core;
LinphoneCall *current=linphone_core_get_current_call(lc);
- return !linphone_core_is_in_conference(lc) &&
+ return !linphone_core_is_in_conference(lc) &&
(current==NULL || current==call);
}
+
static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
- int i;
- for(i=0; itype == SalAudio) {
+ session = call->audiostream->ms.sessions.rtp_session;
+ } else if (stream->type == SalVideo) {
+ session = call->videostream->ms.sessions.rtp_session;
+ } else {
+ // Do nothing for streams that are not audio or video
+ return;
+ }
+ if (stream->rtcp_fb.generic_nack_enabled)
+ rtp_session_enable_avpf_feature(session, ORTP_AVPF_FEATURE_GENERIC_NACK, TRUE);
+ else
+ rtp_session_enable_avpf_feature(session, ORTP_AVPF_FEATURE_GENERIC_NACK, FALSE);
+ if (stream->rtcp_fb.tmmbr_enabled)
+ rtp_session_enable_avpf_feature(session, ORTP_AVPF_FEATURE_TMMBR, TRUE);
+ else
+ rtp_session_enable_avpf_feature(session, ORTP_AVPF_FEATURE_TMMBR, FALSE);
+}
+
+static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *call, SalStreamType type) {
+ RtpSession *session;
+ const OrtpRtcpXrConfiguration *localconfig;
+ const OrtpRtcpXrConfiguration *remoteconfig;
+ OrtpRtcpXrConfiguration currentconfig;
+ const SalStreamDescription *localstream;
+ const SalStreamDescription *remotestream;
+ SalMediaDescription *remotedesc = sal_call_get_remote_media_description(call->op);
+
+ if (!remotedesc) return;
+
+ localstream = sal_media_description_find_best_stream(call->localdesc, type);
+ if (!localstream) return;
+ localconfig = &localstream->rtcp_xr;
+ remotestream = sal_media_description_find_best_stream(remotedesc, type);
+ if (!remotestream) return;
+ remoteconfig = &remotestream->rtcp_xr;
+
+ if (localstream->dir == SalStreamInactive) return;
+ else if (localstream->dir == SalStreamRecvOnly) {
+ /* Use local config for unilateral parameters and remote config for collaborative parameters. */
+ memcpy(¤tconfig, localconfig, sizeof(currentconfig));
+ currentconfig.rcvr_rtt_mode = remoteconfig->rcvr_rtt_mode;
+ currentconfig.rcvr_rtt_max_size = remoteconfig->rcvr_rtt_max_size;
+ } else {
+ memcpy(¤tconfig, remoteconfig, sizeof(currentconfig));
+ }
+ if (type == SalAudio) {
+ session = call->audiostream->ms.sessions.rtp_session;
+ } else {
+ session = call->videostream->ms.sessions.rtp_session;
+ }
+ rtp_session_configure_rtcp_xr(session, ¤tconfig);
+}
+void static start_dtls( MSMediaStreamSessions *sessions, const SalStreamDescription *sd,const SalStreamDescription *remote) {
+ if (sal_stream_description_has_dtls(sd) == TRUE) {
+ /*DTLS*/
+ SalDtlsRole salRole = sd->dtls_role;
+ if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */
+ /* give the peer certificate fingerprint to dtls context */
+ ms_dtls_srtp_set_peer_fingerprint(sessions->dtls_context, remote->dtls_fingerprint);
+ ms_dtls_srtp_set_role(sessions->dtls_context, (salRole == SalDtlsRoleIsClient)?MSDtlsSrtpRoleIsClient:MSDtlsSrtpRoleIsServer); /* set the role to client */
+ ms_dtls_srtp_start(sessions->dtls_context); /* then start the engine, it will send the DTLS client Hello */
+ } else {
+ ms_warning("unable to start DTLS engine on stream session [%p], Dtls role in resulting media description is invalid",sessions);
+ }
+ }
+}
+void static start_dtls_on_all_streams(LinphoneCall *call) {
+ SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op);
+ SalMediaDescription *result_desc = sal_call_get_final_media_description(call->op);
+ if( remote_desc == NULL || result_desc == NULL ){
+ /* this can happen in some tricky cases (early-media without SDP in the 200). In that case, simply skip DTLS code */
+ return;
+ }
+
+ if (call->audiostream && (media_stream_get_state((const MediaStream *)call->audiostream) == MSStreamStarted))/*dtls must start at the end of ice*/
+ start_dtls(&call->audiostream->ms.sessions
+ ,sal_media_description_find_best_stream(result_desc,SalAudio)
+ ,sal_media_description_find_best_stream(remote_desc,SalAudio));
+#if VIDEO_ENABLED
+ if (call->videostream && (media_stream_get_state((const MediaStream *)call->videostream) == MSStreamStarted))/*dtls must start at the end of ice*/
+ start_dtls(&call->videostream->ms.sessions
+ ,sal_media_description_find_best_stream(result_desc,SalVideo)
+ ,sal_media_description_find_best_stream(remote_desc,SalVideo));
+#endif
+ return;
+}
+
+void static set_dtls_fingerprint( MSMediaStreamSessions *sessions, const SalStreamDescription *sd,const SalStreamDescription *remote) {
+ if (sal_stream_description_has_dtls(sd) == TRUE) {
+ /*DTLS*/
+ SalDtlsRole salRole = sd->dtls_role;
+ if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */
+ /* give the peer certificate fingerprint to dtls context */
+ ms_dtls_srtp_set_peer_fingerprint(sessions->dtls_context, remote->dtls_fingerprint);
+ } else {
+ ms_warning("unable to start DTLS engine on stream session [%p], Dtls role in resulting media description is invalid",sessions);
+ }
+ }
+}
+
+void static set_dtls_fingerprint_on_all_streams(LinphoneCall *call) {
+ SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op);
+ SalMediaDescription *result_desc = sal_call_get_final_media_description(call->op);
+
+ if( remote_desc == NULL || result_desc == NULL ){
+ /* this can happen in some tricky cases (early-media without SDP in the 200). In that case, simply skip DTLS code */
+ return;
+ }
+
+ if (call->audiostream && (media_stream_get_state((const MediaStream *)call->audiostream) == MSStreamStarted))/*dtls must start at the end of ice*/
+ set_dtls_fingerprint(&call->audiostream->ms.sessions
+ ,sal_media_description_find_best_stream(result_desc,SalAudio)
+ ,sal_media_description_find_best_stream(remote_desc,SalAudio));
+#if VIDEO_ENABLED
+ if (call->videostream && (media_stream_get_state((const MediaStream *)call->videostream) == MSStreamStarted))/*dtls must start at the end of ice*/
+ set_dtls_fingerprint(&call->videostream->ms.sessions
+ ,sal_media_description_find_best_stream(result_desc,SalVideo)
+ ,sal_media_description_find_best_stream(remote_desc,SalVideo));
+#endif
+ return;
+}
+
+static RtpSession * create_audio_rtp_io_session(LinphoneCall *call) {
+ PayloadType *pt;
+ LinphoneCore *lc = call->core;
+ const char *local_ip = lp_config_get_string(lc->config, "sound", "rtp_local_addr", "127.0.0.1");
+ const char *remote_ip = lp_config_get_string(lc->config, "sound", "rtp_remote_addr", "127.0.0.1");
+ int local_port = lp_config_get_int(lc->config, "sound", "rtp_local_port", 17076);
+ int remote_port = lp_config_get_int(lc->config, "sound", "rtp_remote_port", 17078);
+ int ptnum = lp_config_get_int(lc->config, "sound", "rtp_ptnum", 0);
+ const char *rtpmap = lp_config_get_string(lc->config, "sound", "rtp_map", "pcmu/8000/1");
+ int symmetric = lp_config_get_int(lc->config, "sound", "rtp_symmetric", 0);
+ RtpSession *rtp_session = NULL;
+ pt = rtp_profile_get_payload_from_rtpmap(call->audio_profile, rtpmap);
+ if (pt != NULL) {
+ call->rtp_io_audio_profile = rtp_profile_new("RTP IO audio profile");
+ rtp_profile_set_payload(call->rtp_io_audio_profile, ptnum, payload_type_clone(pt));
+ rtp_session = ms_create_duplex_rtp_session(local_ip, local_port, -1);
+ rtp_session_set_profile(rtp_session, call->rtp_io_audio_profile);
+ rtp_session_set_remote_addr_and_port(rtp_session, remote_ip, remote_port, -1);
+ rtp_session_enable_rtcp(rtp_session, FALSE);
+ rtp_session_set_payload_type(rtp_session, ptnum);
+ rtp_session_set_jitter_compensation(rtp_session, linphone_core_get_audio_jittcomp(lc));
+ rtp_session_set_symmetric_rtp(rtp_session, (bool_t)symmetric);
+ }
+ return rtp_session;
+}
+
+static void linphone_call_start_audio_stream(LinphoneCall *call, LinphoneCallState next_state, bool_t use_arc){
LinphoneCore *lc=call->core;
int used_pt=-1;
char rtcp_tool[128]={0};
@@ -1494,17 +2585,15 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
const char *recfile;
const SalStreamDescription *local_st_desc;
int crypto_idx;
+ MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
+ bool_t use_rtp_io = lp_config_get_int(lc->config, "sound", "rtp_io", FALSE);
snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
- /* look for savp stream first */
- stream=sal_media_description_find_stream(call->resultdesc,
- SalProtoRtpSavp,SalAudio);
- /* no savp audio stream, use avp */
- if (!stream)
- stream=sal_media_description_find_stream(call->resultdesc,
- SalProtoRtpAvp,SalAudio);
+ stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio);
if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
+ const char *rtp_addr=stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr;
+ bool_t is_multicast=ms_is_multicast(rtp_addr);
playcard=lc->sound_conf.lsd_card ?
lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
captcard=lc->sound_conf.capt_sndcard;
@@ -1513,35 +2602,42 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
if (used_pt!=-1){
- call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
+ bool_t ok = TRUE;
+ call->current_params->audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
if (playcard==NULL) {
ms_warning("No card defined for playback !");
}
if (captcard==NULL) {
ms_warning("No card defined for capture !");
}
- /*Replace soundcard filters by inactive file players or recorders
- when placed in recvonly or sendonly mode*/
- if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){
+ /*Don't use file or soundcard capture when placed in recv-only mode*/
+ if (stream->rtp_port==0
+ || stream->dir==SalStreamRecvOnly
+ || (stream->multicast_role == SalMulticastReceiver && is_multicast)){
captcard=NULL;
playfile=NULL;
- }else if (stream->dir==SalStreamSendOnly){
+ }
+ if (next_state == LinphoneCallPaused){
+ /*in paused state, we never use soundcard*/
playcard=NULL;
captcard=NULL;
recfile=NULL;
/*And we will eventually play "playfile" if set by the user*/
- /*playfile=NULL;*/
}
- if (send_ringbacktone){
+ if (call->playing_ringbacktone){
captcard=NULL;
playfile=NULL;/* it is setup later*/
+ if (lp_config_get_int(lc->config,"sound","send_ringback_without_playback", 0) == 1){
+ playcard = NULL;
+ recfile = NULL;
+ }
}
/*if playfile are supplied don't use soundcards*/
- if (lc->use_files) {
+ if (lc->use_files || use_rtp_io) {
captcard=NULL;
playcard=NULL;
}
- if (call->params.in_conference){
+ if (call->params->in_conference){
/* first create the graph without soundcard resources*/
captcard=playcard=NULL;
}
@@ -1550,295 +2646,437 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
captcard=playcard=NULL;
}
use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
+ audio_stream_enable_echo_canceller(call->audiostream, use_ec);
if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
+ media_stream_set_adaptive_bitrate_algorithm(&call->audiostream->ms,
+ ms_qos_analyzer_algorithm_from_string(linphone_core_get_adaptive_rate_algorithm(lc)));
audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
- if (!call->params.in_conference && call->params.record_file)
- audio_stream_mixed_record_open(call->audiostream,call->params.record_file);
- audio_stream_start_full(
- call->audiostream,
- call->audio_profile,
- stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr,
- stream->rtp_port,
- stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
- linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0,
- used_pt,
- linphone_core_get_audio_jittcomp(lc),
- playfile,
- recfile,
- playcard,
- captcard,
- use_ec
- );
- post_configure_audio_streams(call);
- if (muted && !send_ringbacktone){
- audio_stream_set_mic_gain(call->audiostream,0);
+ rtp_session_set_jitter_compensation(call->audiostream->ms.sessions.rtp_session,linphone_core_get_audio_jittcomp(lc));
+ if (!call->params->in_conference && call->params->record_file){
+ audio_stream_mixed_record_open(call->audiostream,call->params->record_file);
+ call->current_params->record_file=ms_strdup(call->params->record_file);
}
- if (stream->dir==SalStreamSendOnly && playfile!=NULL){
+ /* valid local tags are > 0 */
+ if (sal_stream_description_has_srtp(stream) == TRUE) {
+ local_st_desc=sal_media_description_find_stream(call->localdesc,stream->proto,SalAudio);
+ crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
+
+ if (crypto_idx >= 0) {
+ ms_media_stream_sessions_set_srtp_recv_key_b64(&call->audiostream->ms.sessions, stream->crypto[0].algo,stream->crypto[0].master_key);
+ ms_media_stream_sessions_set_srtp_send_key_b64(&call->audiostream->ms.sessions, stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key);
+ } else {
+ ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
+ }
+ }
+ configure_rtp_session_for_rtcp_fb(call, stream);
+ configure_rtp_session_for_rtcp_xr(lc, call, SalAudio);
+ if (is_multicast)
+ rtp_session_set_multicast_ttl(call->audiostream->ms.sessions.rtp_session,stream->ttl);
+
+ if (use_rtp_io) {
+ io.input.type = io.output.type = MSResourceRtp;
+ io.input.session = io.output.session = create_audio_rtp_io_session(call);
+ if (io.input.session == NULL) {
+ ok = FALSE;
+ }
+ }else {
+ if (playcard){
+ io.output.type = MSResourceSoundcard;
+ io.output.soundcard = playcard;
+ }else{
+ io.output.type = MSResourceFile;
+ io.output.file = recfile;
+ }
+ if (captcard){
+ io.input.type = MSResourceSoundcard;
+ io.input.soundcard = captcard;
+ }else{
+ io.input.type = MSResourceFile;
+ io.input.file = playfile;
+ }
+
+ }
+ if (ok == TRUE) {
+ audio_stream_start_from_io(call->audiostream,
+ call->audio_profile,
+ rtp_addr,
+ stream->rtp_port,
+ stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
+ (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0,
+ used_pt,
+ &io
+ );
+ post_configure_audio_streams(call, (call->all_muted || call->audio_muted) && !call->playing_ringbacktone);
+ }
+
+ ms_media_stream_sessions_set_encryption_mandatory(&call->audiostream->ms.sessions,linphone_core_is_media_encryption_mandatory(call->core));
+
+ if (next_state == LinphoneCallPaused && captcard == NULL && playfile != NULL){
int pause_time=500;
ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
}
- if (send_ringbacktone){
+ if (call->playing_ringbacktone){
setup_ring_player(lc,call);
}
- audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
-
- /* valid local tags are > 0 */
- if (stream->proto == SalProtoRtpSavp) {
- local_st_desc=sal_media_description_find_stream(call->localdesc,
- SalProtoRtpSavp,SalAudio);
- crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
- if (crypto_idx >= 0) {
- audio_stream_enable_srtp(
- call->audiostream,
- stream->crypto[0].algo,
- local_st_desc->crypto[crypto_idx].master_key,
- stream->crypto[0].master_key);
- call->audiostream_encrypted=TRUE;
- } else {
- ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
- call->audiostream_encrypted=FALSE;
- }
- }else call->audiostream_encrypted=FALSE;
- if (call->params.in_conference){
+ if (call->params->in_conference){
/*transform the graph to connect it to the conference filter */
- mute=stream->dir==SalStreamRecvOnly;
+ mute = stream->dir==SalStreamRecvOnly;
linphone_call_add_to_conf(call, mute);
}
- call->current_params.in_conference=call->params.in_conference;
- call->current_params.low_bandwidth=call->params.low_bandwidth;
+ call->current_params->in_conference=call->params->in_conference;
+ call->current_params->low_bandwidth=call->params->low_bandwidth;
}else ms_warning("No audio stream accepted ?");
}
}
-static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){
+#ifdef VIDEO_ENABLED
+static RtpSession * create_video_rtp_io_session(LinphoneCall *call) {
+ PayloadType *pt;
+ LinphoneCore *lc = call->core;
+ const char *local_ip = lp_config_get_string(lc->config, "video", "rtp_local_addr", "127.0.0.1");
+ const char *remote_ip = lp_config_get_string(lc->config, "video", "rtp_remote_addr", "127.0.0.1");
+ int local_port = lp_config_get_int(lc->config, "video", "rtp_local_port", 19076);
+ int remote_port = lp_config_get_int(lc->config, "video", "rtp_remote_port", 19078);
+ int ptnum = lp_config_get_int(lc->config, "video", "rtp_ptnum", 0);
+ const char *rtpmap = lp_config_get_string(lc->config, "video", "rtp_map", "vp8/90000/1");
+ int symmetric = lp_config_get_int(lc->config, "video", "rtp_symmetric", 0);
+ RtpSession *rtp_session = NULL;
+ pt = rtp_profile_get_payload_from_rtpmap(call->video_profile, rtpmap);
+ if (pt != NULL) {
+ call->rtp_io_video_profile = rtp_profile_new("RTP IO video profile");
+ rtp_profile_set_payload(call->rtp_io_video_profile, ptnum, payload_type_clone(pt));
+ rtp_session = ms_create_duplex_rtp_session(local_ip, local_port, -1);
+ rtp_session_set_profile(rtp_session, call->rtp_io_video_profile);
+ rtp_session_set_remote_addr_and_port(rtp_session, remote_ip, remote_port, -1);
+ rtp_session_enable_rtcp(rtp_session, FALSE);
+ rtp_session_set_payload_type(rtp_session, ptnum);
+ rtp_session_set_symmetric_rtp(rtp_session, (bool_t)symmetric);
+ }
+ return rtp_session;
+}
+#endif
+
+static void linphone_call_start_video_stream(LinphoneCall *call, LinphoneCallState next_state){
#ifdef VIDEO_ENABLED
LinphoneCore *lc=call->core;
int used_pt=-1;
- /* look for savp stream first */
- const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
- SalProtoRtpSavp,SalVideo);
- char rtcp_tool[128]={0};
- snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
-
- /* no savp audio stream, use avp */
- if (!vstream)
- vstream=sal_media_description_find_stream(call->resultdesc,
- SalProtoRtpAvp,SalVideo);
-
+ const SalStreamDescription *vstream;
+ MSFilter* source = NULL;
+ bool_t reused_preview = FALSE;
+ bool_t use_rtp_io = lp_config_get_int(lc->config, "video", "rtp_io", FALSE);
+ MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
+
/* shutdown preview */
if (lc->previewstream!=NULL) {
- video_preview_stop(lc->previewstream);
+ if( lc->video_conf.reuse_preview_source == FALSE) video_preview_stop(lc->previewstream);
+ else source = video_preview_stop_reuse_source(lc->previewstream);
lc->previewstream=NULL;
}
-
+
+ vstream = sal_media_description_find_best_stream(call->resultdesc, SalVideo);
if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
+ const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,vstream->proto,SalVideo);
+ bool_t is_multicast=ms_is_multicast(rtp_addr);
call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
- if (used_pt!=-1){
- call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
- VideoStreamDir dir=VideoStreamSendRecv;
- MSWebCam *cam=lc->video_conf.device;
- bool_t is_inactive=FALSE;
- call->current_params.has_video=TRUE;
+ if (used_pt!=-1){
+ MediaStreamDir dir= MediaStreamSendRecv;
+ bool_t is_inactive=FALSE;
+ MSWebCam *cam;
+
+ call->current_params->video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
+ call->current_params->has_video=TRUE;
video_stream_enable_adaptive_bitrate_control(call->videostream,
- linphone_core_adaptive_rate_control_enabled(lc));
+ linphone_core_adaptive_rate_control_enabled(lc));
+ media_stream_set_adaptive_bitrate_algorithm(&call->videostream->ms,
+ ms_qos_analyzer_algorithm_from_string(linphone_core_get_adaptive_rate_algorithm(lc)));
video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc));
+ rtp_session_set_jitter_compensation(call->videostream->ms.sessions.rtp_session, linphone_core_get_video_jittcomp(lc));
+ if (lc->video_conf.preview_vsize.width!=0)
+ video_stream_set_preview_size(call->videostream,lc->video_conf.preview_vsize);
+ video_stream_set_fps(call->videostream,linphone_core_get_preferred_framerate(lc));
video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
- if (lc->video_window_id!=0)
- video_stream_set_native_window_id(call->videostream,lc->video_window_id);
- if (lc->preview_window_id!=0)
- video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id);
+ if (call->video_window_id != NULL)
+ video_stream_set_native_window_id(call->videostream, call->video_window_id);
+ else if (lc->video_window_id != NULL)
+ video_stream_set_native_window_id(call->videostream, lc->video_window_id);
+ if (lc->preview_window_id != NULL)
+ video_stream_set_native_preview_window_id(call->videostream, lc->preview_window_id);
video_stream_use_preview_video_window (call->videostream,lc->use_preview_window);
-
- if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
- cam=get_nowebcam_device();
- dir=VideoStreamSendOnly;
+
+ if (is_multicast){
+ if (vstream->multicast_role == SalMulticastReceiver)
+ dir=MediaStreamRecvOnly;
+ else
+ dir=MediaStreamSendOnly;
+ } else if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
+ dir=MediaStreamSendOnly;
}else if (vstream->dir==SalStreamRecvOnly && lc->video_conf.display ){
- dir=VideoStreamRecvOnly;
+ dir=MediaStreamRecvOnly;
}else if (vstream->dir==SalStreamSendRecv){
if (lc->video_conf.display && lc->video_conf.capture)
- dir=VideoStreamSendRecv;
+ dir=MediaStreamSendRecv;
else if (lc->video_conf.display)
- dir=VideoStreamRecvOnly;
+ dir=MediaStreamRecvOnly;
else
- dir=VideoStreamSendOnly;
+ dir=MediaStreamSendOnly;
}else{
ms_warning("video stream is inactive.");
/*either inactive or incompatible with local capabilities*/
is_inactive=TRUE;
}
- if (call->camera_active==FALSE || all_inputs_muted){
- cam=get_nowebcam_device();
- }
+ cam = linphone_call_get_video_device(call);
if (!is_inactive){
+ if (sal_stream_description_has_srtp(vstream) == TRUE) {
+ int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag);
+ if (crypto_idx >= 0) {
+ ms_media_stream_sessions_set_srtp_recv_key_b64(&call->videostream->ms.sessions, vstream->crypto[0].algo,vstream->crypto[0].master_key);
+ ms_media_stream_sessions_set_srtp_send_key_b64(&call->videostream->ms.sessions, vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key);
+ }
+ }
+ configure_rtp_session_for_rtcp_fb(call, vstream);
+ configure_rtp_session_for_rtcp_xr(lc, call, SalVideo);
+
call->log->video_enabled = TRUE;
video_stream_set_direction (call->videostream, dir);
ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
video_stream_set_device_rotation(call->videostream, lc->device_rotation);
- video_stream_start(call->videostream,
- call->video_profile, rtp_addr, vstream->rtp_port,
- rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0,
- used_pt, linphone_core_get_video_jittcomp(lc), cam);
- video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
- }
-
- if (vstream->proto == SalProtoRtpSavp) {
- const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
- SalProtoRtpSavp,SalVideo);
-
- video_stream_enable_strp(
- call->videostream,
- vstream->crypto[0].algo,
- local_st_desc->crypto[0].master_key,
- vstream->crypto[0].master_key
- );
- call->videostream_encrypted=TRUE;
- }else{
- call->videostream_encrypted=FALSE;
+ video_stream_set_freeze_on_error(call->videostream, lp_config_get_int(lc->config, "video", "freeze_on_error", 0));
+ if (is_multicast)
+ rtp_session_set_multicast_ttl(call->videostream->ms.sessions.rtp_session,vstream->ttl);
+
+ video_stream_use_video_preset(call->videostream, lp_config_get_string(lc->config, "video", "preset", NULL));
+ if (lc->video_conf.reuse_preview_source && source) {
+ ms_message("video_stream_start_with_source kept: %p", source);
+ video_stream_start_with_source(call->videostream,
+ call->video_profile, rtp_addr, vstream->rtp_port,
+ rtcp_addr,
+ linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0,
+ used_pt, linphone_core_get_video_jittcomp(lc), cam, source);
+ reused_preview = TRUE;
+ } else {
+ bool_t ok = TRUE;
+ if (use_rtp_io) {
+ io.input.type = io.output.type = MSResourceRtp;
+ io.input.session = io.output.session = create_video_rtp_io_session(call);
+ if (io.input.session == NULL) {
+ ok = FALSE;
+ ms_warning("Cannot create video RTP IO session");
+ }
+ } else {
+ io.input.type = MSResourceCamera;
+ io.input.camera = cam;
+ io.output.type = MSResourceDefault;
+ }
+ if (ok) {
+ video_stream_start_from_io(call->videostream,
+ call->video_profile, rtp_addr, vstream->rtp_port,
+ rtcp_addr,
+ (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0,
+ used_pt, &io);
+ }
+ }
+ ms_media_stream_sessions_set_encryption_mandatory(&call->videostream->ms.sessions,linphone_core_is_media_encryption_mandatory(call->core));
}
}else ms_warning("No video stream accepted.");
}else{
- ms_warning("No valid video stream defined.");
+ ms_message("No valid video stream defined.");
+ }
+ if( reused_preview == FALSE && source != NULL ){
+ /* destroy not-reused source filter */
+ ms_warning("Video preview (%p) not reused: destroying it.", source);
+ ms_filter_destroy(source);
}
#endif
}
-void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
+static void setZrtpCryptoTypesParameters(MSZrtpParams *params, LinphoneCore *lc)
+{
+ int i;
+ const MSCryptoSuite *srtp_suites;
+ MsZrtpCryptoTypesCount ciphersCount, authTagsCount;
+
+ if (params == NULL) return;
+ if (lc == NULL) return;
+
+ srtp_suites = linphone_core_get_srtp_crypto_suites(lc);
+ if (srtp_suites!=NULL) {
+ for(i=0; srtp_suites[i]!=MS_CRYPTO_SUITE_INVALID && iciphers[params->ciphersCount++] = MS_ZRTP_CIPHER_AES1;
+ params->authTags[params->authTagsCount++] = MS_ZRTP_AUTHTAG_HS32;
+ break;
+ case MS_AES_128_NO_AUTH:
+ params->ciphers[params->ciphersCount++] = MS_ZRTP_CIPHER_AES1;
+ break;
+ case MS_NO_CIPHER_SHA1_80:
+ params->authTags[params->authTagsCount++] = MS_ZRTP_AUTHTAG_HS80;
+ break;
+ case MS_AES_128_SHA1_80:
+ params->ciphers[params->ciphersCount++] = MS_ZRTP_CIPHER_AES1;
+ params->authTags[params->authTagsCount++] = MS_ZRTP_AUTHTAG_HS80;
+ break;
+ case MS_AES_256_SHA1_80:
+ params->ciphers[params->ciphersCount++] = MS_ZRTP_CIPHER_AES3;
+ params->authTags[params->authTagsCount++] = MS_ZRTP_AUTHTAG_HS80;
+ break;
+ case MS_AES_256_SHA1_32:
+ params->ciphers[params->ciphersCount++] = MS_ZRTP_CIPHER_AES3;
+ params->authTags[params->authTagsCount++] = MS_ZRTP_AUTHTAG_HS80;
+ break;
+ case MS_CRYPTO_SUITE_INVALID:
+ break;
+ }
+ }
+ }
+
+ /* linphone_core_get_srtp_crypto_suites is used to determine sensible defaults; here each can be overridden */
+ ciphersCount = linphone_core_get_zrtp_cipher_suites(lc, params->ciphers); /* if not present in config file, params->ciphers is not modified */
+ if (ciphersCount!=0) { /* use zrtp_cipher_suites config only when present, keep config from srtp_crypto_suite otherwise */
+ params->ciphersCount = ciphersCount;
+ }
+ params->hashesCount = linphone_core_get_zrtp_hash_suites(lc, params->hashes);
+ authTagsCount = linphone_core_get_zrtp_auth_suites(lc, params->authTags); /* if not present in config file, params->authTags is not modified */
+ if (authTagsCount!=0) {
+ params->authTagsCount = authTagsCount; /* use zrtp_auth_suites config only when present, keep config from srtp_crypto_suite otherwise */
+ }
+ params->sasTypesCount = linphone_core_get_zrtp_sas_suites(lc, params->sasTypes);
+ params->keyAgreementsCount = linphone_core_get_zrtp_key_agreement_suites(lc, params->keyAgreements);
+}
+
+void linphone_call_start_media_streams(LinphoneCall *call, LinphoneCallState next_state){
LinphoneCore *lc=call->core;
- LinphoneAddress *me;
- char *cname;
- bool_t use_arc;
-
- call->current_params.audio_codec = NULL;
- call->current_params.video_codec = NULL;
-
- me=linphone_core_get_primary_contact_parsed(lc);
- use_arc=linphone_core_adaptive_rate_control_enabled(lc);
+ bool_t use_arc = linphone_core_adaptive_rate_control_enabled(lc);
#ifdef VIDEO_ENABLED
- const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
- SalProtoRtpAvp,SalVideo);
+ const SalStreamDescription *vstream=sal_media_description_find_best_stream(call->resultdesc,SalVideo);
#endif
+ switch (next_state){
+ case LinphoneCallIncomingEarlyMedia:
+ if (linphone_core_get_remote_ringback_tone(lc)){
+ call->playing_ringbacktone = TRUE;
+ }
+ case LinphoneCallOutgoingEarlyMedia:
+ if (!call->params->real_early_media){
+ call->all_muted = TRUE;
+ }
+ break;
+ default:
+ call->playing_ringbacktone = FALSE;
+ call->all_muted = FALSE;
+ break;
+ }
+
+ call->current_params->audio_codec = NULL;
+ call->current_params->video_codec = NULL;
+
if ((call->audiostream == NULL) && (call->videostream == NULL)) {
ms_fatal("start_media_stream() called without prior init !");
return;
}
- cname=linphone_address_as_string_uri_only(me);
-
#if defined(VIDEO_ENABLED)
if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
/*when video is used, do not make adaptive rate control on audio, it is stupid.*/
use_arc=FALSE;
}
#endif
+ ms_message("linphone_call_start_media_streams() call=[%p] local upload_bandwidth=[%i] kbit/s; local download_bandwidth=[%i] kbit/s",
+ call, linphone_core_get_upload_bandwidth(lc),linphone_core_get_download_bandwidth(lc));
+
if (call->audiostream!=NULL) {
- linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
+ linphone_call_start_audio_stream(call, next_state, use_arc);
+ } else {
+ ms_warning("DTLS no audio stream!");
}
- call->current_params.has_video=FALSE;
+ call->current_params->has_video=FALSE;
if (call->videostream!=NULL) {
- linphone_call_start_video_stream(call,cname,all_inputs_muted);
+ if (call->audiostream) audio_stream_link_video(call->audiostream,call->videostream);
+ linphone_call_start_video_stream(call, next_state);
}
- call->all_muted=all_inputs_muted;
- call->playing_ringbacktone=send_ringbacktone;
call->up_bw=linphone_core_get_upload_bandwidth(lc);
- if (call->params.media_encryption==LinphoneMediaEncryptionZRTP) {
- OrtpZrtpParams params;
- /*will be set later when zrtp is activated*/
- call->current_params.media_encryption=LinphoneMediaEncryptionNone;
-
+ /*might be moved in audio/video stream_start*/
+ if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) {
+ MSZrtpParams params;
+ memset(¶ms,0,sizeof(MSZrtpParams));
+ /*call->current_params.media_encryption will be set later when zrtp is activated*/
params.zid_file=lc->zrtp_secrets_cache;
+ params.uri= linphone_address_as_string_uri_only((call->dir==LinphoneCallIncoming) ? call->log->from : call->log->to);
+ setZrtpCryptoTypesParameters(¶ms,call->core);
audio_stream_enable_zrtp(call->audiostream,¶ms);
- }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
- call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
- LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
+#if VIDEO_ENABLED
+ if (media_stream_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
+ /*audio stream is already encrypted and video stream is active*/
+ memset(¶ms,0,sizeof(MSZrtpParams));
+ video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms);
+ }
+#endif
}
- /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
- * further in the call, for example during pause,resume, conferencing reINVITEs*/
- linphone_call_fix_call_parameters(call);
+ set_dtls_fingerprint_on_all_streams(call);
+
if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) {
ice_session_start_connectivity_checks(call->ice_session);
+ } else {
+ /*should not start dtls until ice is completed*/
+ start_dtls_on_all_streams(call);
}
- goto end;
- end:
- ms_free(cname);
- linphone_address_destroy(me);
-}
-
-void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){
- audio_stream_prepare_sound(call->audiostream, NULL, NULL);
-#ifdef VIDEO_ENABLED
- if (call->videostream) {
- video_stream_prepare_video(call->videostream);
- }
-#endif
}
void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){
- audio_stream_unprepare_sound(call->audiostream);
+ if(call->audiostream && call->audiostream->ms.state==MSStreamPreparing) audio_stream_unprepare_sound(call->audiostream);
#ifdef VIDEO_ENABLED
- if (call->videostream) {
+ if (call->videostream && call->videostream->ms.state==MSStreamPreparing) {
video_stream_unprepare_video(call->videostream);
}
#endif
}
+static bool_t update_stream_crypto_params(LinphoneCall *call, const SalStreamDescription *local_st_desc, SalStreamDescription *old_stream, SalStreamDescription *new_stream, MediaStream *ms){
+ int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
+ if (crypto_idx >= 0) {
+ if (call->localdesc_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED)
+ ms_media_stream_sessions_set_srtp_send_key_b64(&ms->sessions, new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key);
+ if (strcmp(old_stream->crypto[0].master_key,new_stream->crypto[0].master_key)!=0){
+ ms_media_stream_sessions_set_srtp_recv_key_b64(&ms->sessions, new_stream->crypto[0].algo,new_stream->crypto[0].master_key);
+ }
+ return TRUE;
+ } else {
+ ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
+ }
+ return FALSE;
+}
+
void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) {
SalStreamDescription *old_stream;
SalStreamDescription *new_stream;
- int i;
+ const SalStreamDescription *local_st_desc;
- old_stream = sal_media_description_find_stream(old_md, SalProtoRtpSavp, SalAudio);
- new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalAudio);
- if (old_stream && new_stream) {
- const SalStreamDescription *local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalAudio);
- if (local_st_desc) {
- int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
- if (crypto_idx >= 0) {
- audio_stream_enable_srtp(call->audiostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
- call->audiostream_encrypted = TRUE;
- } else {
- ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
- call->audiostream_encrypted = FALSE;
- }
- for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
- old_stream->crypto[i].tag = new_stream->crypto[i].tag;
- old_stream->crypto[i].algo = new_stream->crypto[i].algo;
- strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
- }
- }
+ local_st_desc = sal_media_description_find_secure_stream_of_type(call->localdesc, SalAudio);
+ old_stream = sal_media_description_find_secure_stream_of_type(old_md, SalAudio);
+ new_stream = sal_media_description_find_secure_stream_of_type(new_md, SalAudio);
+ if (call->audiostream && local_st_desc && old_stream && new_stream &&
+ update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->audiostream->ms)){
}
+ start_dtls_on_all_streams(call);
+
#ifdef VIDEO_ENABLED
- old_stream = sal_media_description_find_stream(old_md, SalProtoRtpSavp, SalVideo);
- new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalVideo);
- if (old_stream && new_stream) {
- const SalStreamDescription *local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalVideo);
- if (local_st_desc) {
- int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
- if (crypto_idx >= 0) {
- video_stream_enable_strp(call->videostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
- call->videostream_encrypted = TRUE;
- } else {
- ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
- call->videostream_encrypted = FALSE;
- }
- for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
- old_stream->crypto[i].tag = new_stream->crypto[i].tag;
- old_stream->crypto[i].algo = new_stream->crypto[i].algo;
- strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
- }
- }
+ local_st_desc = sal_media_description_find_secure_stream_of_type(call->localdesc, SalVideo);
+ old_stream = sal_media_description_find_secure_stream_of_type(old_md, SalVideo);
+ new_stream = sal_media_description_find_secure_stream_of_type(new_md, SalVideo);
+ if (call->videostream && local_st_desc && old_stream && new_stream &&
+ update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->videostream->ms)){
}
#endif
}
@@ -1862,71 +3100,118 @@ void linphone_call_delete_ice_session(LinphoneCall *call){
}
}
-#ifdef BUILD_UPNP
+
void linphone_call_delete_upnp_session(LinphoneCall *call){
+#ifdef BUILD_UPNP
if(call->upnp_session!=NULL) {
linphone_upnp_session_destroy(call->upnp_session);
call->upnp_session=NULL;
}
-}
#endif //BUILD_UPNP
-
-static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
- audio_stream_get_local_rtp_stats (st,&log->local_stats);
- log->quality=audio_stream_get_average_quality_rating(st);
}
-void linphone_call_stop_audio_stream(LinphoneCall *call) {
+
+static void linphone_call_log_fill_stats(LinphoneCallLog *log, MediaStream *st){
+ float quality=media_stream_get_average_quality_rating(st);
+ if (quality>=0){
+ if (log->quality!=-1){
+ log->quality*=quality/5.0;
+ }else log->quality=quality;
+ }
+}
+
+static void update_rtp_stats(LinphoneCall *call, int stream_index) {
+ if (stream_index >= linphone_call_get_stream_count(call)) {
+ return;
+ }
+
+ if (call->sessions[stream_index].rtp_session) {
+ const rtp_stats_t *stats = rtp_session_get_stats(call->sessions[stream_index].rtp_session);
+ memcpy(&call->stats[stream_index].rtp_stats, stats, sizeof(*stats));
+ }
+}
+
+static void linphone_call_stop_audio_stream(LinphoneCall *call) {
if (call->audiostream!=NULL) {
- rtp_session_unregister_event_queue(call->audiostream->ms.session,call->audiostream_app_evq);
- ortp_ev_queue_flush(call->audiostream_app_evq);
- ortp_ev_queue_destroy(call->audiostream_app_evq);
- call->audiostream_app_evq=NULL;
+ linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_AUDIO);
+ media_stream_reclaim_sessions(&call->audiostream->ms,&call->sessions[0]);
if (call->audiostream->ec){
const char *state_str=NULL;
ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_STATE_STRING,&state_str);
if (state_str){
ms_message("Writing echo canceler state, %i bytes",(int)strlen(state_str));
- lp_config_set_string(call->core->config,"sound","ec_state",state_str);
+ lp_config_write_relative_file(call->core->config, EC_STATE_STORE, state_str);
}
}
- linphone_call_log_fill_stats (call->log,call->audiostream);
+ audio_stream_get_local_rtp_stats(call->audiostream,&call->log->local_stats);
+ linphone_call_log_fill_stats (call->log,(MediaStream*)call->audiostream);
if (call->endpoint){
linphone_call_remove_from_conf(call);
}
audio_stream_stop(call->audiostream);
+ update_rtp_stats(call, 0);
+ rtp_session_unregister_event_queue(call->sessions[0].rtp_session, call->audiostream_app_evq);
+ ortp_ev_queue_flush(call->audiostream_app_evq);
+ ortp_ev_queue_destroy(call->audiostream_app_evq);
+ call->audiostream_app_evq=NULL;
call->audiostream=NULL;
+ call->current_params->audio_codec = NULL;
}
}
-void linphone_call_stop_video_stream(LinphoneCall *call) {
+static void linphone_call_stop_video_stream(LinphoneCall *call) {
#ifdef VIDEO_ENABLED
if (call->videostream!=NULL){
- rtp_session_unregister_event_queue(call->videostream->ms.session,call->videostream_app_evq);
+ linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_VIDEO);
+ media_stream_reclaim_sessions(&call->videostream->ms,&call->sessions[1]);
+ linphone_call_log_fill_stats(call->log,(MediaStream*)call->videostream);
+ video_stream_stop(call->videostream);
+ call->videostream=NULL;
+ update_rtp_stats(call, 1);
+ rtp_session_unregister_event_queue(call->sessions[1].rtp_session, call->videostream_app_evq);
ortp_ev_queue_flush(call->videostream_app_evq);
ortp_ev_queue_destroy(call->videostream_app_evq);
call->videostream_app_evq=NULL;
- video_stream_stop(call->videostream);
- call->videostream=NULL;
+ call->current_params->video_codec = NULL;
}
#endif
}
+static void unset_rtp_profile(LinphoneCall *call, int i){
+ if (call->sessions[i].rtp_session)
+ rtp_session_set_profile(call->sessions[i].rtp_session,&av_profile);
+}
+
void linphone_call_stop_media_streams(LinphoneCall *call){
- linphone_call_stop_audio_stream(call);
- linphone_call_stop_video_stream(call);
- ms_event_queue_skip(call->core->msevq);
-
+ if (call->audiostream || call->videostream) {
+ if (call->audiostream && call->videostream)
+ audio_stream_unlink_video(call->audiostream, call->videostream);
+ linphone_call_stop_audio_stream(call);
+ linphone_call_stop_video_stream(call);
+
+ if (call->core->msevq != NULL) {
+ ms_event_queue_skip(call->core->msevq);
+ }
+ }
+
if (call->audio_profile){
- rtp_profile_clear_all(call->audio_profile);
rtp_profile_destroy(call->audio_profile);
call->audio_profile=NULL;
+ unset_rtp_profile(call,0);
}
if (call->video_profile){
- rtp_profile_clear_all(call->video_profile);
rtp_profile_destroy(call->video_profile);
call->video_profile=NULL;
+ unset_rtp_profile(call,1);
+ }
+ if (call->rtp_io_audio_profile) {
+ rtp_profile_destroy(call->rtp_io_audio_profile);
+ call->rtp_io_audio_profile = NULL;
+ }
+ if (call->rtp_io_video_profile) {
+ rtp_profile_destroy(call->rtp_io_video_profile);
+ call->rtp_io_video_profile = NULL;
}
}
@@ -2005,6 +3290,32 @@ float linphone_call_get_record_volume(LinphoneCall *call){
return LINPHONE_VOLUME_DB_LOWEST;
}
+float linphone_call_get_speaker_volume_gain(const LinphoneCall *call) {
+ if(call->audiostream) return audio_stream_get_sound_card_output_gain(call->audiostream);
+ else {
+ ms_error("Could not get playback volume: no audio stream");
+ return -1.0f;
+ }
+}
+
+void linphone_call_set_speaker_volume_gain(LinphoneCall *call, float volume) {
+ if(call->audiostream) audio_stream_set_sound_card_output_gain(call->audiostream, volume);
+ else ms_error("Could not set playback volume: no audio stream");
+}
+
+float linphone_call_get_microphone_volume_gain(const LinphoneCall *call) {
+ if(call->audiostream) return audio_stream_get_sound_card_input_gain(call->audiostream);
+ else {
+ ms_error("Could not get record volume: no audio stream");
+ return -1.0f;
+ }
+}
+
+void linphone_call_set_microphone_volume_gain(LinphoneCall *call, float volume) {
+ if(call->audiostream) audio_stream_set_sound_card_input_gain(call->audiostream, volume);
+ else ms_error("Could not set record volume: no audio stream");
+}
+
/**
* Obtain real-time quality rating of the call
*
@@ -2019,14 +3330,24 @@ float linphone_call_get_record_volume(LinphoneCall *call){
* 1-2 = very poor quality
* 0-1 = can't be worse, mostly unusable
*
- * @returns The function returns -1 if no quality measurement is available, for example if no
+ * @return The function returns -1 if no quality measurement is available, for example if no
* active audio stream exist. Otherwise it returns the quality rating.
**/
float linphone_call_get_current_quality(LinphoneCall *call){
+ float audio_rating=-1;
+ float video_rating=-1;
+ float result;
if (call->audiostream){
- return audio_stream_get_quality_rating(call->audiostream);
+ audio_rating=media_stream_get_quality_rating((MediaStream*)call->audiostream)/5.0;
}
- return -1;
+ if (call->videostream){
+ video_rating=media_stream_get_quality_rating((MediaStream*)call->videostream)/5.0;
+ }
+ if (audio_rating<0 && video_rating<0) result=-1;
+ else if (audio_rating<0) result=video_rating*5.0;
+ else if (video_rating<0) result=audio_rating*5.0;
+ else result=audio_rating*video_rating*5.0;
+ return result;
}
/**
@@ -2041,41 +3362,217 @@ float linphone_call_get_average_quality(LinphoneCall *call){
return -1;
}
+static void update_local_stats(LinphoneCallStats *stats, MediaStream *stream) {
+ const MSQualityIndicator *qi = media_stream_get_quality_indicator(stream);
+ if (qi) {
+ stats->local_late_rate=ms_quality_indicator_get_local_late_rate(qi);
+ stats->local_loss_rate=ms_quality_indicator_get_local_loss_rate(qi);
+ }
+ media_stream_get_local_rtp_stats(stream, &stats->rtp_stats);
+}
+
/**
* Access last known statistics for audio stream, for a given call.
**/
-const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) {
- return &call->stats[LINPHONE_CALL_STATS_AUDIO];
+const LinphoneCallStats *linphone_call_get_audio_stats(LinphoneCall *call) {
+ LinphoneCallStats *stats = &call->stats[LINPHONE_CALL_STATS_AUDIO];
+ if (call->audiostream){
+ update_local_stats(stats,(MediaStream*)call->audiostream);
+ }
+ return stats;
}
/**
* Access last known statistics for video stream, for a given call.
**/
-const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) {
- return &call->stats[LINPHONE_CALL_STATS_VIDEO];
-}
-
-/**
- * Enable recording of the call (voice-only).
- * This function must be used before the call parameters are assigned to the call.
- * The call recording can be started and paused after the call is established with
- * linphone_call_start_recording() and linphone_call_pause_recording().
- * @param cp the call parameters
- * @param path path and filename of the file where audio is written.
-**/
-void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
- if (cp->record_file){
- ms_free(cp->record_file);
- cp->record_file=NULL;
+const LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call) {
+ LinphoneCallStats *stats = &call->stats[LINPHONE_CALL_STATS_VIDEO];
+ if (call->videostream){
+ update_local_stats(stats,(MediaStream*)call->videostream);
}
- if (path) cp->record_file=ms_strdup(path);
+ return stats;
+}
+
+static bool_t ice_in_progress(LinphoneCallStats *stats){
+ return stats->ice_state==LinphoneIceStateInProgress;
}
/**
- * Retrieves the path for the audio recoding of the call.
+ * Indicates whether an operation is in progress at the media side.
+ * It can a bad idea to initiate signaling operations (adding video, pausing the call, removing video, changing video parameters) while
+ * the media is busy in establishing the connection (typically ICE connectivity checks). It can result in failures generating loss of time
+ * in future operations in the call.
+ * Applications are invited to check this function after each call state change to decide whether certain operations are permitted or not.
+ * @param call the call
+ * @return TRUE if media is busy in establishing the connection, FALSE otherwise.
**/
-const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp){
- return cp->record_file;
+bool_t linphone_call_media_in_progress(LinphoneCall *call){
+ bool_t ret=FALSE;
+ if (ice_in_progress(&call->stats[LINPHONE_CALL_STATS_AUDIO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_VIDEO]))
+ ret=TRUE;
+ /*TODO: could check zrtp state, upnp state*/
+ return ret;
+}
+
+/**
+ * Get the local loss rate since last report
+ * @return The sender loss rate
+**/
+float linphone_call_stats_get_sender_loss_rate(const LinphoneCallStats *stats) {
+ const report_block_t *srb = NULL;
+
+ if (!stats || !stats->sent_rtcp)
+ return 0.0;
+ /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
+ if (stats->sent_rtcp->b_cont != NULL)
+ msgpullup(stats->sent_rtcp, -1);
+ if (rtcp_is_SR(stats->sent_rtcp))
+ srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
+ else if (rtcp_is_RR(stats->sent_rtcp))
+ srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
+ if (!srb)
+ return 0.0;
+ return 100.0 * report_block_get_fraction_lost(srb) / 256.0;
+}
+
+/**
+ * Gets the remote reported loss rate since last report
+ * @return The receiver loss rate
+**/
+float linphone_call_stats_get_receiver_loss_rate(const LinphoneCallStats *stats) {
+ const report_block_t *rrb = NULL;
+
+ if (!stats || !stats->received_rtcp)
+ return 0.0;
+ /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
+ if (stats->received_rtcp->b_cont != NULL)
+ msgpullup(stats->received_rtcp, -1);
+ if (rtcp_is_RR(stats->received_rtcp))
+ rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
+ else if (rtcp_is_SR(stats->received_rtcp))
+ rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
+ if (!rrb)
+ return 0.0;
+ return 100.0 * report_block_get_fraction_lost(rrb) / 256.0;
+}
+
+/**
+ * Gets the local interarrival jitter
+ * @return The interarrival jitter at last emitted sender report
+**/
+float linphone_call_stats_get_sender_interarrival_jitter(const LinphoneCallStats *stats, LinphoneCall *call) {
+ const LinphoneCallParams *params;
+ const PayloadType *pt;
+ const report_block_t *srb = NULL;
+
+ if (!stats || !call || !stats->sent_rtcp)
+ return 0.0;
+ params = linphone_call_get_current_params(call);
+ if (!params)
+ return 0.0;
+ /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
+ if (stats->sent_rtcp->b_cont != NULL)
+ msgpullup(stats->sent_rtcp, -1);
+ if (rtcp_is_SR(stats->sent_rtcp))
+ srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
+ else if (rtcp_is_RR(stats->sent_rtcp))
+ srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
+ if (!srb)
+ return 0.0;
+ if (stats->type == LINPHONE_CALL_STATS_AUDIO)
+ pt = linphone_call_params_get_used_audio_codec(params);
+ else
+ pt = linphone_call_params_get_used_video_codec(params);
+ if (!pt || (pt->clock_rate == 0))
+ return 0.0;
+ return (float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate;
+}
+
+/**
+ * Gets the remote reported interarrival jitter
+ * @return The interarrival jitter at last received receiver report
+**/
+float linphone_call_stats_get_receiver_interarrival_jitter(const LinphoneCallStats *stats, LinphoneCall *call) {
+ const LinphoneCallParams *params;
+ const PayloadType *pt;
+ const report_block_t *rrb = NULL;
+
+ if (!stats || !call || !stats->received_rtcp)
+ return 0.0;
+ params = linphone_call_get_current_params(call);
+ if (!params)
+ return 0.0;
+ /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
+ if (stats->received_rtcp->b_cont != NULL)
+ msgpullup(stats->received_rtcp, -1);
+ if (rtcp_is_SR(stats->received_rtcp))
+ rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
+ else if (rtcp_is_RR(stats->received_rtcp))
+ rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
+ if (!rrb)
+ return 0.0;
+ if (stats->type == LINPHONE_CALL_STATS_AUDIO)
+ pt = linphone_call_params_get_used_audio_codec(params);
+ else
+ pt = linphone_call_params_get_used_video_codec(params);
+ if (!pt || (pt->clock_rate == 0))
+ return 0.0;
+ return (float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate;
+}
+
+rtp_stats_t linphone_call_stats_get_rtp_stats(const LinphoneCallStats *stats) {
+ rtp_stats_t rtp_stats;
+ memset(&rtp_stats, 0, sizeof(rtp_stats));
+
+ if (stats) {
+ memcpy(&rtp_stats, &stats->rtp_stats, sizeof(stats->rtp_stats));
+ }
+
+ return rtp_stats;
+}
+
+/**
+ * Gets the cumulative number of late packets
+ * @return The cumulative number of late packets
+**/
+uint64_t linphone_call_stats_get_late_packets_cumulative_number(const LinphoneCallStats *stats, LinphoneCall *call) {
+ return linphone_call_stats_get_rtp_stats(stats).outoftime;
+}
+
+/**
+ * Get the bandwidth measurement of the received stream, expressed in kbit/s, including IP/UDP/RTP headers.
+ * @param[in] stats LinphoneCallStats object
+ * @return The bandwidth measurement of the received stream in kbit/s.
+ */
+float linphone_call_stats_get_download_bandwidth(const LinphoneCallStats *stats) {
+ return stats->download_bandwidth;
+}
+
+/**
+ * Get the bandwidth measurement of the sent stream, expressed in kbit/s, including IP/UDP/RTP headers.
+ * @param[in] stats LinphoneCallStats object
+ * @return The bandwidth measurement of the sent stream in kbit/s.
+ */
+float linphone_call_stats_get_upload_bandwidth(const LinphoneCallStats *stats) {
+ return stats->upload_bandwidth;
+}
+
+/**
+ * Get the state of ICE processing.
+ * @param[in] stats LinphoneCallStats object
+ * @return The state of ICE processing.
+ */
+LinphoneIceState linphone_call_stats_get_ice_state(const LinphoneCallStats *stats) {
+ return stats->ice_state;
+}
+
+/**
+ * Get the state of uPnP processing.
+ * @param[in] stats LinphoneCallStats object
+ * @return The state of uPnP processing.
+ */
+LinphoneUpnpState linphone_call_stats_get_upnp_state(const LinphoneCallStats *stats) {
+ return stats->upnp_state;
}
/**
@@ -2083,11 +3580,11 @@ const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp){
* The output file where audio is recorded must be previously specified with linphone_call_params_set_record_file().
**/
void linphone_call_start_recording(LinphoneCall *call){
- if (!call->params.record_file){
+ if (!call->params->record_file){
ms_error("linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().");
return;
}
- if (call->audiostream && !call->params.in_conference){
+ if (call->audiostream && !call->params->in_conference){
audio_stream_mixed_record_start(call->audiostream);
}
call->record_active=TRUE;
@@ -2097,7 +3594,7 @@ void linphone_call_start_recording(LinphoneCall *call){
* Stop call recording.
**/
void linphone_call_stop_recording(LinphoneCall *call){
- if (call->audiostream && !call->params.in_conference){
+ if (call->audiostream && !call->params->in_conference){
audio_stream_mixed_record_stop(call->audiostream);
}
call->record_active=FALSE;
@@ -2107,36 +3604,86 @@ void linphone_call_stop_recording(LinphoneCall *call){
* @}
**/
-static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){
- call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0;
- call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0;
- call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0;
- call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0;
- ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
- call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
- call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth ,
- call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
- call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth
+static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *vs){
+ bool_t as_active = as ? (media_stream_get_state(as) == MSStreamStarted) : FALSE;
+ bool_t vs_active = vs ? (media_stream_get_state(vs) == MSStreamStarted) : FALSE;
+
+ call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as_active) ? (media_stream_get_down_bw(as)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as_active) ? (media_stream_get_up_bw(as)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs_active) ? (media_stream_get_down_bw(vs)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs_active) ? (media_stream_get_up_bw(vs)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_download_bandwidth=(as_active) ? (media_stream_get_rtcp_down_bw(as)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_upload_bandwidth=(as_active) ? (media_stream_get_rtcp_up_bw(as)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_download_bandwidth=(vs_active) ? (media_stream_get_rtcp_down_bw(vs)*1e-3) : 0;
+ call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_upload_bandwidth=(vs_active) ? (media_stream_get_rtcp_up_bw(vs)*1e-3) : 0;
+ if (as_active) {
+ call->stats[LINPHONE_CALL_STATS_AUDIO].updated|=LINPHONE_CALL_STATS_PERIODICAL_UPDATE;
+ linphone_core_notify_call_stats_updated(call->core, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
+ call->stats[LINPHONE_CALL_STATS_AUDIO].updated=0;
+ update_local_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], as);
+ }
+ if (vs_active) {
+ call->stats[LINPHONE_CALL_STATS_VIDEO].updated|=LINPHONE_CALL_STATS_PERIODICAL_UPDATE;
+ linphone_core_notify_call_stats_updated(call->core, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
+ call->stats[LINPHONE_CALL_STATS_VIDEO].updated=0;
+ update_local_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], vs);
+ }
+
+ ms_message( "Bandwidth usage for call [%p]:\n"
+ "\tRTP audio=[d=%5.1f,u=%5.1f], video=[d=%5.1f,u=%5.1f] kbits/sec\n"
+ "\tRTCP audio=[d=%5.1f,u=%5.1f], video=[d=%5.1f,u=%5.1f] kbits/sec",
+ call,
+ call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_download_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_upload_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_download_bandwidth,
+ call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_upload_bandwidth
);
+
}
static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
- char temp[256];
+ char temp[256]={0};
char *from=NULL;
- if(call)
- from = linphone_call_get_remote_address_as_string(call);
- if (from)
- {
- snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
- free(from);
- }
- else
- {
- snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
- }
- if (lc->vtable.display_warning!=NULL)
- lc->vtable.display_warning(lc,temp);
+
+ from = linphone_call_get_remote_address_as_string(call);
+ snprintf(temp,sizeof(temp)-1,"Remote end %s seems to have disconnected, the call is going to be closed.",from ? from : "");
+ if (from) ms_free(from);
+
+ ms_message("On call [%p]: %s",call,temp);
+ linphone_core_notify_display_warning(lc,temp);
linphone_core_terminate_call(lc,call);
+ linphone_core_play_named_tone(lc,LinphoneToneCallLost);
+}
+
+static void change_ice_media_destinations(LinphoneCall *call) {
+ const char *rtp_addr;
+ const char *rtcp_addr;
+ int rtp_port;
+ int rtcp_port;
+ bool_t result;
+
+ if (call->audiostream && ice_session_check_list(call->ice_session, 0)) {
+ result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 0), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port);
+ if (result == TRUE) {
+ ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port);
+ rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session, FALSE);
+ rtp_session_set_remote_addr_full(call->audiostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port);
+ }
+ }
+#ifdef VIDEO_ENABLED
+ if (call->videostream && ice_session_check_list(call->ice_session, 1)) {
+ result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 1), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port);
+ if (result == TRUE) {
+ ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port);
+ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session, FALSE);
+ rtp_session_set_remote_addr_full(call->videostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port);
+ }
+ }
+#endif
}
static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
@@ -2145,19 +3692,37 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
int ping_time;
if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) {
+ LinphoneCallParams *params = linphone_call_params_copy(call->current_params);
+ switch (call->params->media_encryption) {
+ case LinphoneMediaEncryptionZRTP:
+ case LinphoneMediaEncryptionDTLS:
+ /* preserve media encryption param because at that time ZRTP/SRTP-DTLS negociation may still be ongoing*/
+ params->media_encryption=call->params->media_encryption;
+ break;
+ case LinphoneMediaEncryptionSRTP:
+ case LinphoneMediaEncryptionNone:
+ /*keep all values to make sure a warning will be generated by compiler if new enum value is added*/
+ break;
+ }
+
switch (ice_session_state(call->ice_session)) {
case IS_Completed:
ice_session_select_candidates(call->ice_session);
- if (ice_session_role(call->ice_session) == IR_Controlling) {
- linphone_core_update_call(call->core, call, &call->current_params);
+ if (ice_session_role(call->ice_session) == IR_Controlling
+ && lp_config_get_int(call->core->config, "sip", "update_call_when_ice_completed", TRUE)) {
+ params->internal_call_update = TRUE;
+ linphone_core_update_call(call->core, call, params);
}
+ change_ice_media_destinations(call);
+ start_dtls_on_all_streams(call);
break;
case IS_Failed:
if (ice_session_has_completed_check_list(call->ice_session) == TRUE) {
ice_session_select_candidates(call->ice_session);
if (ice_session_role(call->ice_session) == IR_Controlling) {
/* At least one ICE session has succeeded, so perform a call update. */
- linphone_core_update_call(call->core, call, &call->current_params);
+ params->internal_call_update = TRUE;
+ linphone_core_update_call(call->core, call, params);
}
}
break;
@@ -2165,6 +3730,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
break;
}
linphone_core_update_ice_state_in_call_stats(call);
+ linphone_call_params_unref(params);
} else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) {
if (evd->info.ice_processing_successful==TRUE) {
@@ -2184,7 +3750,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
linphone_core_start_update_call(call->core, call);
break;
case LinphoneCallUpdatedByRemote:
- linphone_core_start_accept_call_update(call->core, call);
+ linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate));
break;
case LinphoneCallOutgoingInit:
linphone_call_stop_media_streams_for_ice_gathering(call);
@@ -2192,130 +3758,168 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
break;
case LinphoneCallIdle:
linphone_call_stop_media_streams_for_ice_gathering(call);
+ linphone_call_update_local_media_description_from_ice_or_upnp(call);
+ sal_call_set_local_media_description(call->op,call->localdesc);
linphone_core_notify_incoming_call(call->core, call);
break;
default:
break;
}
} else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
- linphone_core_start_accept_call_update(call->core, call);
- linphone_core_update_ice_state_in_call_stats(call);
+ if (call->state==LinphoneCallUpdatedByRemote){
+ linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate));
+ linphone_core_update_ice_state_in_call_stats(call);
+ }
} else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
ice_session_restart(call->ice_session);
ice_session_set_role(call->ice_session, IR_Controlling);
- linphone_core_update_call(call->core, call, &call->current_params);
+ linphone_core_update_call(call->core, call, call->current_params);
+ }
+}
+
+
+/*do not change the prototype of this function, it is also used internally in linphone-daemon.*/
+void linphone_call_stats_fill(LinphoneCallStats *stats, MediaStream *ms, OrtpEvent *ev){
+ OrtpEventType evt=ortp_event_get_type(ev);
+ OrtpEventData *evd=ortp_event_get_data(ev);
+
+ if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
+ stats->round_trip_delay = rtp_session_get_round_trip_propagation(ms->sessions.rtp_session);
+ if(stats->received_rtcp != NULL)
+ freemsg(stats->received_rtcp);
+ stats->received_rtcp = evd->packet;
+ evd->packet = NULL;
+ stats->updated = LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE;
+ update_local_stats(stats,ms);
+ } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
+ memcpy(&stats->jitter_stats, rtp_session_get_jitter_stats(ms->sessions.rtp_session), sizeof(jitter_stats_t));
+ if (stats->sent_rtcp != NULL)
+ freemsg(stats->sent_rtcp);
+ stats->sent_rtcp = evd->packet;
+ evd->packet = NULL;
+ stats->updated = LINPHONE_CALL_STATS_SENT_RTCP_UPDATE;
+ update_local_stats(stats,ms);
+ }
+}
+
+void linphone_call_stats_uninit(LinphoneCallStats *stats){
+ if (stats->received_rtcp) {
+ freemsg(stats->received_rtcp);
+ stats->received_rtcp=NULL;
+ }
+ if (stats->sent_rtcp){
+ freemsg(stats->sent_rtcp);
+ stats->sent_rtcp=NULL;
+ }
+}
+
+void linphone_call_notify_stats_updated(LinphoneCall *call, int stream_index){
+ LinphoneCallStats *stats=&call->stats[stream_index];
+ LinphoneCore *lc=call->core;
+ if (stats->updated){
+ switch(stats->updated) {
+ case LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE:
+ case LINPHONE_CALL_STATS_SENT_RTCP_UPDATE:
+ linphone_reporting_on_rtcp_update(call, stream_index);
+ break;
+ default:break;
+ }
+ linphone_core_notify_call_stats_updated(lc, call, stats);
+ stats->updated = 0;
+ }
+}
+
+void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){
+ MediaStream *ms=stream_index==0 ? (MediaStream *)call->audiostream : (MediaStream *)call->videostream; /*assumption to remove*/
+ OrtpEvQueue *evq;
+ OrtpEvent *ev;
+
+ if (ms==NULL) return;
+ /* Ensure there is no dangling ICE check list. */
+ if (call->ice_session == NULL) ms->ice_check_list = NULL;
+
+ switch(ms->type){
+ case MSAudio:
+ audio_stream_iterate((AudioStream*)ms);
+ break;
+ case MSVideo:
+#ifdef VIDEO_ENABLED
+ video_stream_iterate((VideoStream*)ms);
+#endif
+ break;
+ default:
+ ms_error("linphone_call_handle_stream_events(): unsupported stream type.");
+ return;
+ break;
+ }
+ /*yes the event queue has to be taken at each iteration, because ice events may perform operations re-creating the streams*/
+ while ((evq=stream_index==0 ? call->audiostream_app_evq : call->videostream_app_evq) && (NULL != (ev=ortp_ev_queue_get(evq)))){
+ OrtpEventType evt=ortp_event_get_type(ev);
+ OrtpEventData *evd=ortp_event_get_data(ev);
+
+ linphone_call_stats_fill(&call->stats[stream_index],ms,ev);
+ linphone_call_notify_stats_updated(call,stream_index);
+
+ if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
+ if (ms->type==MSAudio)
+ linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
+ else if (ms->type==MSVideo)
+ propagate_encryption_changed(call);
+ } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
+ if (ms->type==MSAudio)
+ linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
+ } else if (evt == ORTP_EVENT_DTLS_ENCRYPTION_CHANGED) {
+ if (ms->type==MSAudio)
+ linphone_call_audiostream_encryption_changed(call, evd->info.dtls_stream_encrypted);
+ else if (ms->type==MSVideo)
+ propagate_encryption_changed(call);
+ }else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
+ || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
+ handle_ice_events(call, ev);
+ } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
+ linphone_core_dtmf_received(call,evd->info.telephone_event);
+ }
+ ortp_event_destroy(ev);
}
}
void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
- LinphoneCore* lc = call->core;
int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
bool_t disconnected=FALSE;
- if (call->state==LinphoneCallStreamsRunning && one_second_elapsed){
- RtpSession *as=NULL,*vs=NULL;
- float audio_load=0, video_load=0;
- if (call->audiostream!=NULL){
- as=call->audiostream->ms.session;
- if (call->audiostream->ms.ticker)
- audio_load=ms_ticker_get_average_load(call->audiostream->ms.ticker);
+ switch (call->state) {
+ case LinphoneCallStreamsRunning:
+ case LinphoneCallOutgoingEarlyMedia:
+ case LinphoneCallIncomingEarlyMedia:
+ case LinphoneCallPausedByRemote:
+ case LinphoneCallPaused:
+ if (one_second_elapsed){
+ float audio_load=0, video_load=0;
+ if (call->audiostream!=NULL){
+ if (call->audiostream->ms.sessions.ticker)
+ audio_load=ms_ticker_get_average_load(call->audiostream->ms.sessions.ticker);
+ }
+ if (call->videostream!=NULL){
+ if (call->videostream->ms.sessions.ticker)
+ video_load=ms_ticker_get_average_load(call->videostream->ms.sessions.ticker);
+ }
+ report_bandwidth(call,(MediaStream*)call->audiostream,(MediaStream*)call->videostream);
+ ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
}
- if (call->videostream!=NULL){
- if (call->videostream->ms.ticker)
- video_load=ms_ticker_get_average_load(call->videostream->ms.ticker);
- vs=call->videostream->ms.session;
- }
- report_bandwidth(call,as,vs);
- ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
+ break;
+ default:
+ /*no stats for other states*/
+ break;
}
#ifdef BUILD_UPNP
linphone_upnp_call_process(call);
#endif //BUILD_UPNP
-#ifdef VIDEO_ENABLED
- if (call->videostream!=NULL) {
- OrtpEvent *ev;
-
- /* Ensure there is no dangling ICE check list. */
- if (call->ice_session == NULL) call->videostream->ms.ice_check_list = NULL;
-
- // Beware that the application queue should not depend on treatments fron the
- // mediastreamer queue.
- video_stream_iterate(call->videostream);
-
- while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){
- OrtpEventType evt=ortp_event_get_type(ev);
- OrtpEventData *evd=ortp_event_get_data(ev);
- if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
- linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
- } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
- call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->ms.session);
- if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
- freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
- call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
- evd->packet = NULL;
- if (lc->vtable.call_stats_updated)
- lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
- } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
- memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->ms.session), sizeof(jitter_stats_t));
- if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
- freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
- call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet;
- evd->packet = NULL;
- if (lc->vtable.call_stats_updated)
- lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
- } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
- || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
- handle_ice_events(call, ev);
- }
- ortp_event_destroy(ev);
- }
- }
-#endif
- if (call->audiostream!=NULL) {
- OrtpEvent *ev;
-
- /* Ensure there is no dangling ICE check list. */
- if (call->ice_session == NULL) call->audiostream->ms.ice_check_list = NULL;
-
- // Beware that the application queue should not depend on treatments fron the
- // mediastreamer queue.
- audio_stream_iterate(call->audiostream);
-
- while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){
- OrtpEventType evt=ortp_event_get_type(ev);
- OrtpEventData *evd=ortp_event_get_data(ev);
- if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
- linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
- } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
- linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
- } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
- call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->ms.session);
- if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
- freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
- call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
- evd->packet = NULL;
- if (lc->vtable.call_stats_updated)
- lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
- } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
- memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->ms.session), sizeof(jitter_stats_t));
- if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
- freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
- call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet;
- evd->packet = NULL;
- if (lc->vtable.call_stats_updated)
- lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
- } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
- || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
- handle_ice_events(call, ev);
- } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
- linphone_core_dtmf_received(lc,evd->info.telephone_event);
- }
- ortp_event_destroy(ev);
- }
- }
- if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
+ linphone_call_handle_stream_events(call,0);
+ linphone_call_handle_stream_events(call,1);
+ if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL
+ && call->audiostream->ms.state==MSStreamStarted && disconnect_timeout>0 )
disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
if (disconnected)
linphone_core_disconnected(call->core,call);
@@ -2324,19 +3928,18 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
void linphone_call_log_completed(LinphoneCall *call){
LinphoneCore *lc=call->core;
- call->log->duration=time(NULL)-call->start_time;
+ call->log->duration=linphone_call_get_duration(call); /*store duration since connected*/
if (call->log->status==LinphoneCallMissed){
char *info;
lc->missed_calls++;
info=ortp_strdup_printf(ngettext("You have missed %i call.",
- "You have missed %i calls.", lc->missed_calls),
- lc->missed_calls);
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,info);
+ "You have missed %i calls.", lc->missed_calls),
+ lc->missed_calls);
+ linphone_core_notify_display_status(lc,info);
ms_free(info);
}
- lc->call_logs=ms_list_prepend(lc->call_logs,(void *)call->log);
+ lc->call_logs=ms_list_prepend(lc->call_logs,linphone_call_log_ref(call->log));
if (ms_list_size(lc->call_logs)>lc->max_call_logs){
MSList *elem,*prevelem=NULL;
/*find the last element*/
@@ -2344,15 +3947,17 @@ void linphone_call_log_completed(LinphoneCall *call){
prevelem=elem;
}
elem=prevelem;
- linphone_call_log_destroy((LinphoneCallLog*)elem->data);
+ linphone_call_log_unref((LinphoneCallLog*)elem->data);
lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
}
- if (lc->vtable.call_log_updated!=NULL){
- lc->vtable.call_log_updated(lc,call->log);
- }
+ linphone_core_notify_call_log_updated(lc,call->log);
call_logs_write_to_config_file(lc);
}
+/**
+ * Returns the current transfer state, if a transfer has been initiated from this call.
+ * @see linphone_core_transfer_call() , linphone_core_transfer_call_to_another()
+**/
LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
return call->transfer_state;
}
@@ -2360,36 +3965,33 @@ LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
if (state != call->transfer_state) {
LinphoneCore* lc = call->core;
+ ms_message("Transfer state for call [%p] changed from [%s] to [%s]",call
+ ,linphone_call_state_to_string(call->transfer_state)
+ ,linphone_call_state_to_string(state));
call->transfer_state = state;
- if (lc->vtable.transfer_state_changed)
- lc->vtable.transfer_state_changed(lc, call, state);
+ linphone_core_notify_transfer_state_changed(lc, call, state);
}
}
-/**
- * Returns true if the call is part of the conference.
- * @ingroup conferencing
-**/
bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
- return call->params.in_conference;
+ return call->params->in_conference;
}
-
/**
* Perform a zoom of the video displayed during a call.
* @param call the call.
* @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied.
* @param cx a floating point number pointing the horizontal center of the zoom to be applied. This value should be between 0.0 and 1.0.
* @param cy a floating point number pointing the vertical center of the zoom to be applied. This value should be between 0.0 and 1.0.
- *
- * cx and cy are updated in return in case their coordinates were to excentrated for the requested zoom factor. The zoom ensures that all the screen is fullfilled with the video.
+ *
+ * cx and cy are updated in return in case their coordinates were too excentrated for the requested zoom factor. The zoom ensures that all the screen is fullfilled with the video.
**/
void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) {
VideoStream* vstream = call->videostream;
if (vstream && vstream->output) {
float zoom[3];
float halfsize;
-
+
if (zoom_factor < 1)
zoom_factor = 1;
halfsize = 0.5 * 1.0 / zoom_factor;
@@ -2402,11 +4004,227 @@ void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx,
*cy = 0 + halfsize;
if ((*cy + halfsize) > 1)
*cy = 1 - halfsize;
-
+
zoom[0] = zoom_factor;
zoom[1] = *cx;
zoom[2] = *cy;
- ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
+ ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
}else ms_warning("Could not apply zoom: video output wasn't activated.");
}
+static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){
+ LinphoneAddress *ctt=NULL;
+ LinphoneAddress *ret=NULL;
+ //const char *localip=call->localip;
+
+ /* first use user's supplied ip address if asked*/
+ if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){
+ ctt=linphone_core_get_primary_contact_parsed(lc);
+ linphone_address_set_domain(ctt,linphone_core_get_nat_address_resolved(lc));
+ ret=ctt;
+ } else if (call->op && sal_op_get_contact_address(call->op)!=NULL){
+ /* if already choosed, don't change it */
+ return NULL;
+ } else if (call->ping_op && sal_op_get_contact_address(call->ping_op)) {
+ /* if the ping OPTIONS request succeeded use the contact guessed from the
+ received, rport*/
+ ms_message("Contact has been fixed using OPTIONS"/* to %s",guessed*/);
+ ret=linphone_address_clone(sal_op_get_contact_address(call->ping_op));;
+ } else if (dest_proxy && dest_proxy->op && sal_op_get_contact_address(dest_proxy->op)){
+ /*if using a proxy, use the contact address as guessed with the REGISTERs*/
+ ms_message("Contact has been fixed using proxy" /*to %s",fixed_contact*/);
+ ret=linphone_address_clone(sal_op_get_contact_address(dest_proxy->op));
+ } else {
+ ctt=linphone_core_get_primary_contact_parsed(lc);
+ if (ctt!=NULL){
+ /*otherwise use supplied localip*/
+ linphone_address_set_domain(ctt,NULL/*localip*/);
+ linphone_address_set_port(ctt,-1/*linphone_core_get_sip_port(lc)*/);
+ ms_message("Contact has not been fixed stack will do"/* to %s",ret*/);
+ ret=ctt;
+ }
+ }
+ return ret;
+}
+
+void linphone_call_set_contact_op(LinphoneCall* call) {
+ LinphoneAddress *contact;
+
+ if (call->dest_proxy == NULL) {
+ /* Try to define the destination proxy if it has not already been done to have a correct contact field in the SIP messages */
+ call->dest_proxy = linphone_core_lookup_known_proxy(call->core, call->log->to);
+ }
+
+ contact=get_fixed_contact(call->core,call,call->dest_proxy);
+ if (contact){
+ SalTransport tport=sal_address_get_transport((SalAddress*)contact);
+ sal_address_clean((SalAddress*)contact); /* clean out contact_params that come from proxy config*/
+ sal_address_set_transport((SalAddress*)contact,tport);
+ sal_op_set_contact_address(call->op, contact);
+ linphone_address_destroy(contact);
+ }
+}
+
+LinphonePlayer *linphone_call_get_player(LinphoneCall *call){
+ if (call->player==NULL)
+ call->player=linphone_call_build_player(call);
+ return call->player;
+}
+
+void linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *params){
+ LinphoneCallParams *cp=NULL;
+ if (params) cp=linphone_call_params_copy(params);
+ if (call->params) linphone_call_params_unref(call->params);
+ call->params=cp;
+}
+
+static int send_dtmf_handler(void *data, unsigned int revents){
+ LinphoneCall *call = (LinphoneCall*)data;
+ /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/
+ if (linphone_core_get_use_rfc2833_for_dtmf(call->core)!=0 || linphone_core_get_use_info_for_dtmf(call->core)==0)
+ {
+ /* In Band DTMF */
+ if (call->audiostream!=NULL){
+ audio_stream_send_dtmf(call->audiostream,*call->dtmf_sequence);
+ }
+ else
+ {
+ ms_error("Cannot send RFC2833 DTMF when we are not in communication.");
+ return FALSE;
+ }
+ }
+ if (linphone_core_get_use_info_for_dtmf(call->core)!=0){
+ /* Out of Band DTMF (use INFO method) */
+ sal_call_send_dtmf(call->op,*call->dtmf_sequence);
+ }
+
+ /*this check is needed because linphone_call_send_dtmf does not set the timer since its a single character*/
+ if (call->dtmfs_timer!=NULL) {
+ memmove(call->dtmf_sequence, call->dtmf_sequence+1, strlen(call->dtmf_sequence));
+ }
+ /* continue only if the dtmf sequence is not empty*/
+ if (call->dtmf_sequence!=NULL&&*call->dtmf_sequence!='\0') {
+ return TRUE;
+ } else {
+ linphone_call_cancel_dtmfs(call);
+ return FALSE;
+ }
+}
+int linphone_call_send_dtmf(LinphoneCall *call,char dtmf){
+ if (call==NULL){
+ ms_warning("linphone_call_send_dtmf(): invalid call, canceling DTMF.");
+ return -1;
+ }
+ call->dtmf_sequence = &dtmf;
+ send_dtmf_handler(call,0);
+ call->dtmf_sequence = NULL;
+ return 0;
+}
+
+int linphone_call_send_dtmfs(LinphoneCall *call,char *dtmfs) {
+ if (call==NULL){
+ ms_warning("linphone_call_send_dtmfs(): invalid call, canceling DTMF sequence.");
+ return -1;
+ }
+ if (call->dtmfs_timer!=NULL){
+ ms_warning("linphone_call_send_dtmfs(): a DTMF sequence is already in place, canceling DTMF sequence.");
+ return -2;
+ }
+ if (dtmfs != NULL) {
+ int delay_ms = lp_config_get_int(call->core->config,"net","dtmf_delay_ms",200);
+ call->dtmf_sequence = ms_strdup(dtmfs);
+ call->dtmfs_timer = sal_create_timer(call->core->sal, send_dtmf_handler, call, delay_ms, "DTMF sequence timer");
+ }
+ return 0;
+}
+
+void linphone_call_cancel_dtmfs(LinphoneCall *call) {
+ /*nothing to do*/
+ if (!call || !call->dtmfs_timer) return;
+
+ sal_cancel_timer(call->core->sal, call->dtmfs_timer);
+ belle_sip_object_unref(call->dtmfs_timer);
+ call->dtmfs_timer = NULL;
+ if (call->dtmf_sequence != NULL) {
+ ms_free(call->dtmf_sequence);
+ call->dtmf_sequence = NULL;
+ }
+}
+
+void * linphone_call_get_native_video_window_id(const LinphoneCall *call) {
+ if (call->video_window_id) {
+ /* The video id was previously set by the app. */
+ return call->video_window_id;
+ }
+#ifdef VIDEO_ENABLED
+ else if (call->videostream) {
+ /* It was not set but we want to get the one automatically created by mediastreamer2 (desktop versions only). */
+ return video_stream_get_native_window_id(call->videostream);
+ }
+#endif
+ return 0;
+}
+
+void linphone_call_set_native_video_window_id(LinphoneCall *call, void *id) {
+ call->video_window_id = id;
+#ifdef VIDEO_ENABLED
+ if (call->videostream) {
+ video_stream_set_native_window_id(call->videostream, id);
+ }
+#endif
+}
+
+MSWebCam *linphone_call_get_video_device(const LinphoneCall *call) {
+ if (call->all_muted || call->camera_enabled == FALSE)
+ return get_nowebcam_device();
+ else
+ return call->core->video_conf.device;
+}
+
+
+void linphone_call_set_audio_route(LinphoneCall *call, LinphoneAudioRoute route) {
+ if (call != NULL && call->audiostream != NULL){
+ audio_stream_set_audio_route(call->audiostream, (MSAudioRoute) route);
+ }
+}
+
+int linphone_call_get_stream_count(LinphoneCall *call) {
+ // Revisit when multiple media streams will be implemented
+#ifdef VIDEO_ENABLED
+ return 2;
+#else
+ return 1;
+#endif
+}
+
+MSFormatType linphone_call_get_stream_type(LinphoneCall *call, int stream_index) {
+ // Revisit when multiple media streams will be implemented
+ if (stream_index == 0) {
+ return MSAudio;
+ }
+ return MSVideo;
+}
+
+RtpTransport* linphone_call_get_meta_rtp_transport(LinphoneCall *call, int stream_index) {
+ RtpTransport *meta_rtp;
+ RtpTransport *meta_rtcp;
+
+ if (!call || stream_index < 0 || stream_index >= linphone_call_get_stream_count(call)) {
+ return NULL;
+ }
+
+ rtp_session_get_transports(call->sessions[stream_index].rtp_session, &meta_rtp, &meta_rtcp);
+ return meta_rtp;
+}
+
+RtpTransport* linphone_call_get_meta_rtcp_transport(LinphoneCall *call, int stream_index) {
+ RtpTransport *meta_rtp;
+ RtpTransport *meta_rtcp;
+
+ if (!call || stream_index < 0 || stream_index >= linphone_call_get_stream_count(call)) {
+ return NULL;
+ }
+
+ rtp_session_get_transports(call->sessions[stream_index].rtp_session, &meta_rtp, &meta_rtcp);
+ return meta_rtcp;
+}
\ No newline at end of file
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 1e1a7a64b..3d124d147 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -1,6 +1,7 @@
/*
linphone
Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
+Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -17,16 +18,19 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#define _GNU_SOURCE
-
#include "linphonecore.h"
#include "sipsetup.h"
#include "lpconfig.h"
#include "private.h"
+#include "quality_reporting.h"
+#include "lime.h"
#include
+#include
+#include
#include
-#include
+#include
+#include
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/mseventqueue.h"
#include "mediastreamer2/msvolume.h"
@@ -34,20 +38,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/dtmfgen.h"
#ifdef INET6
-#ifndef WIN32
+#ifndef _WIN32
#include
#endif
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
-#include "liblinphone_gitversion.h"
+#ifndef ANDROID /*on Android LIBLINPHONE version is passed from root Makefile*/
+ #include "liblinphone_gitversion.h"
+#endif
#else
#ifndef LIBLINPHONE_GIT_VERSION
#define LIBLINPHONE_GIT_VERSION "unknown"
#endif
#endif
+#ifdef __APPLE__
+#include "TargetConditionals.h"
+#endif
+
+#ifdef HAVE_ZLIB
+#define COMPRESSED_LOG_COLLECTION_EXTENSION "gz"
+#ifdef _WIN32
+#include
+#include
+#ifndef fileno
+#define fileno _fileno
+#endif
+#define unlink _unlink
+#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+#define SET_BINARY_MODE(file)
+#endif
+#include
+#else
+#define COMPRESSED_LOG_COLLECTION_EXTENSION "txt"
+#endif
+#define LOG_COLLECTION_DEFAULT_PATH "."
+#define LOG_COLLECTION_DEFAULT_PREFIX "linphone"
+#define LOG_COLLECTION_DEFAULT_MAX_FILE_SIZE (10 * 1024 * 1024)
+
/*#define UNSTANDART_GSM_11K 1*/
@@ -60,20 +91,33 @@ static const char *liblinphone_version=
LIBLINPHONE_VERSION
#endif
;
+static OrtpLogFunc liblinphone_log_func = NULL;
+static LinphoneLogCollectionState liblinphone_log_collection_state = LinphoneLogCollectionDisabled;
+static char * liblinphone_log_collection_path = NULL;
+static char * liblinphone_log_collection_prefix = NULL;
+static int liblinphone_log_collection_max_file_size = LOG_COLLECTION_DEFAULT_MAX_FILE_SIZE;
+static ortp_mutex_t liblinphone_log_collection_mutex;
+static bool_t liblinphone_serialize_logs = FALSE;
static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime);
static void linphone_core_run_hooks(LinphoneCore *lc);
-static void linphone_core_free_hooks(LinphoneCore *lc);
#include "enum.h"
+#include "contact_providers_priv.h"
+
const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
static void toggle_video_preview(LinphoneCore *lc, bool_t val);
+#if defined(LINPHONE_WINDOWS_PHONE) || defined(LINPHONE_WINDOWS_UNIVERSAL)
+#define SOUNDS_PREFIX "Assets/Sounds/"
+#else
+#define SOUNDS_PREFIX
+#endif
/* relative path where is stored local ring*/
-#define LOCAL_RING "rings/oldphone.wav"
+#define LOCAL_RING SOUNDS_PREFIX "rings/oldphone.wav"
/* same for remote ring (ringback)*/
-#define REMOTE_RING "ringback.wav"
-#define HOLD_MUSIC "rings/toy-mono.wav"
+#define REMOTE_RING SOUNDS_PREFIX "ringback.wav"
+#define HOLD_MUSIC SOUNDS_PREFIX "rings/toy-mono.wav"
extern SalCallbacks linphone_sal_callbacks;
@@ -90,293 +134,6 @@ int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){
}
-/*prevent a gcc bug with %c*/
-static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){
- return strftime(s, max, fmt, tm);
-}
-
-static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
- struct tm loctime;
-#ifdef WIN32
-#if !defined(_WIN32_WCE)
- loctime=*localtime(&start_time);
- /*FIXME*/
-#endif /*_WIN32_WCE*/
-#else
- localtime_r(&start_time,&loctime);
-#endif
- my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime);
-}
-
-LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
- LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1);
- cl->dir=call->dir;
- cl->start_date_time=call->start_time;
- set_call_log_date(cl,cl->start_date_time);
- cl->from=from;
- cl->to=to;
- cl->status=LinphoneCallAborted; /*default status*/
- return cl;
-}
-
-void call_logs_write_to_config_file(LinphoneCore *lc){
- MSList *elem;
- char logsection[32];
- int i;
- char *tmp;
- LpConfig *cfg=lc->config;
-
- if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
-
- for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){
- LinphoneCallLog *cl=(LinphoneCallLog*)elem->data;
- snprintf(logsection,sizeof(logsection),"call_log_%i",i);
- lp_config_clean_section(cfg,logsection);
- lp_config_set_int(cfg,logsection,"dir",cl->dir);
- lp_config_set_int(cfg,logsection,"status",cl->status);
- tmp=linphone_address_as_string(cl->from);
- lp_config_set_string(cfg,logsection,"from",tmp);
- ms_free(tmp);
- tmp=linphone_address_as_string(cl->to);
- lp_config_set_string(cfg,logsection,"to",tmp);
- ms_free(tmp);
- if (cl->start_date_time)
- lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time);
- else lp_config_set_string(cfg,logsection,"start_date",cl->start_date);
- lp_config_set_int(cfg,logsection,"duration",cl->duration);
- if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
- lp_config_set_float(cfg,logsection,"quality",cl->quality);
- lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled);
- lp_config_set_string(cfg,logsection,"call_id",cl->call_id);
- }
- for(;imax_call_logs;++i){
- snprintf(logsection,sizeof(logsection),"call_log_%i",i);
- lp_config_clean_section(cfg,logsection);
- }
-}
-
-static time_t string_to_time(const char *date){
-#ifndef WIN32
- struct tm tmtime={0};
- strptime(date,"%c",&tmtime);
- return mktime(&tmtime);
-#else
- return 0;
-#endif
-}
-
-static void call_logs_read_from_config_file(LinphoneCore *lc){
- char logsection[32];
- int i;
- const char *tmp;
- uint64_t sec;
- LpConfig *cfg=lc->config;
- for(i=0;;++i){
- snprintf(logsection,sizeof(logsection),"call_log_%i",i);
- if (lp_config_has_section(cfg,logsection)){
- LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1);
- cl->dir=lp_config_get_int(cfg,logsection,"dir",0);
- cl->status=lp_config_get_int(cfg,logsection,"status",0);
- tmp=lp_config_get_string(cfg,logsection,"from",NULL);
- if (tmp) cl->from=linphone_address_new(tmp);
- tmp=lp_config_get_string(cfg,logsection,"to",NULL);
- if (tmp) cl->to=linphone_address_new(tmp);
- sec=lp_config_get_int64(cfg,logsection,"start_date_time",0);
- if (sec) {
- /*new call log format with date expressed in seconds */
- cl->start_date_time=(time_t)sec;
- set_call_log_date(cl,cl->start_date_time);
- }else{
- tmp=lp_config_get_string(cfg,logsection,"start_date",NULL);
- if (tmp) {
- strncpy(cl->start_date,tmp,sizeof(cl->start_date));
- cl->start_date_time=string_to_time(cl->start_date);
- }
- }
- cl->duration=lp_config_get_int(cfg,logsection,"duration",0);
- tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
- if (tmp) cl->refkey=ms_strdup(tmp);
- cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
- cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0);
- tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
- if (tmp) cl->call_id=ms_strdup(tmp);
- lc->call_logs=ms_list_append(lc->call_logs,cl);
- }else break;
- }
-}
-
-
-
-/**
- * @addtogroup call_logs
- * @{
-**/
-
-/**
- * Returns a human readable string describing the call.
- *
- * @note: the returned char* must be freed by the application (use ms_free()).
-**/
-char * linphone_call_log_to_str(LinphoneCallLog *cl){
- char *status;
- char *tmp;
- char *from=linphone_address_as_string (cl->from);
- char *to=linphone_address_as_string (cl->to);
- switch(cl->status){
- case LinphoneCallAborted:
- status=_("aborted");
- break;
- case LinphoneCallSuccess:
- status=_("completed");
- break;
- case LinphoneCallMissed:
- status=_("missed");
- break;
- default:
- status="unknown";
- }
- tmp=ortp_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
- (cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
- cl->start_date,
- from,
- to,
- status,
- cl->duration/60,
- cl->duration%60);
- ms_free(from);
- ms_free(to);
- return tmp;
-}
-
-/**
- * Returns RTP statistics computed locally regarding the call.
- *
-**/
-const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){
- return &cl->local_stats;
-}
-
-/**
- * Returns RTP statistics computed by remote end and sent back via RTCP.
- *
- * @note Not implemented yet.
-**/
-const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){
- return &cl->remote_stats;
-}
-
-const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
- return cl->call_id;
-}
-
-/**
- * Assign a user pointer to the call log.
-**/
-void linphone_call_log_set_user_pointer(LinphoneCallLog *cl, void *up){
- cl->user_pointer=up;
-}
-
-/**
- * Returns the user pointer associated with the call log.
-**/
-void *linphone_call_log_get_user_pointer(const LinphoneCallLog *cl){
- return cl->user_pointer;
-}
-
-
-
-/**
- * Associate a persistent reference key to the call log.
- *
- * The reference key can be for example an id to an external database.
- * It is stored in the config file, thus can survive to process exits/restarts.
- *
-**/
-void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){
- if (cl->refkey!=NULL){
- ms_free(cl->refkey);
- cl->refkey=NULL;
- }
- if (refkey) cl->refkey=ms_strdup(refkey);
-}
-
-/**
- * Get the persistent reference key associated to the call log.
- *
- * The reference key can be for example an id to an external database.
- * It is stored in the config file, thus can survive to process exits/restarts.
- *
-**/
-const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){
- return cl->refkey;
-}
-
-/**
- * Returns origin (ie from) address of the call.
-**/
-LinphoneAddress *linphone_call_log_get_from(LinphoneCallLog *cl){
- return cl->from;
-}
-
-/**
- * Returns destination address (ie to) of the call.
-**/
-LinphoneAddress *linphone_call_log_get_to(LinphoneCallLog *cl){
- return cl->to;
-}
-
-/**
- * Returns remote address (that is from or to depending on call direction).
-**/
-LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl){
- return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to;
-}
-
-/**
- * Returns the direction of the call.
-**/
-LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl){
- return cl->dir;
-}
-
-/**
- * Returns the status of the call.
-**/
-LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl){
- return cl->status;
-}
-
-/**
- * Returns the start date of the call, expressed as a POSIX time_t.
-**/
-time_t linphone_call_log_get_start_date(LinphoneCallLog *cl){
- return cl->start_date_time;
-}
-
-/**
- * Returns duration of the call.
-**/
-int linphone_call_log_get_duration(LinphoneCallLog *cl){
- return cl->duration;
-}
-
-/**
- * Returns overall quality indication of the call.
-**/
-float linphone_call_log_get_quality(LinphoneCallLog *cl){
- return cl->quality;
-}
-
-/** @} */
-
-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);
- ms_free(cl);
-}
-
/**
* Returns TRUE if the LinphoneCall asked to autoanswer
*
@@ -401,10 +158,523 @@ const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _Lin
return linphone_call_get_remote_address(call);
}
+static void linphone_core_log_collection_handler(OrtpLogLevel level, const char *fmt, va_list args);
+
+void linphone_core_set_log_handler(OrtpLogFunc logfunc) {
+ if (ortp_logv_out == linphone_core_log_collection_handler) {
+ ms_message("There is already a log collection handler, keep it");
+ liblinphone_log_func = logfunc;
+ } else
+ ortp_set_log_handler(logfunc);
+}
+
+void linphone_core_set_log_file(FILE *file) {
+ if (file == NULL) file = stdout;
+ ortp_set_log_file(file);
+}
+
+void linphone_core_set_log_level(OrtpLogLevel loglevel) {
+ OrtpLogLevel mask = loglevel;
+ switch (loglevel) {
+ case ORTP_TRACE:
+ case ORTP_DEBUG:
+ mask |= ORTP_DEBUG;
+ case ORTP_MESSAGE:
+ mask |= ORTP_MESSAGE;
+ case ORTP_WARNING:
+ mask |= ORTP_WARNING;
+ case ORTP_ERROR:
+ mask |= ORTP_ERROR;
+ case ORTP_FATAL:
+ mask |= ORTP_FATAL;
+ break;
+ case ORTP_LOGLEV_END:
+ break;
+ }
+ linphone_core_set_log_level_mask(mask);
+}
+
+void linphone_core_set_log_level_mask(OrtpLogLevel loglevel) {
+ ortp_set_log_level_mask(loglevel);
+ if (loglevel == 0) {
+ sal_disable_log();
+ } else {
+ sal_enable_log();
+ }
+}
+
+static void linphone_core_log_collection_handler(OrtpLogLevel level, const char *fmt, va_list args) {
+ const char *lname="undef";
+ char *msg;
+ char *log_filename1;
+ char *log_filename2;
+ FILE *log_file;
+ struct timeval tp;
+ struct tm *lt;
+ time_t tt;
+ struct stat statbuf;
+
+ if (liblinphone_log_func != NULL) {
+#ifndef _WIN32
+ va_list args_copy;
+ va_copy(args_copy, args);
+ liblinphone_log_func(level, fmt, args_copy);
+ va_end(args_copy);
+#else
+ /* This works on 32 bits, luckily. */
+ /* TODO: va_copy is available in Visual Studio 2013. */
+ liblinphone_log_func(level, fmt, args);
+#endif
+ }
+
+ ortp_gettimeofday(&tp, NULL);
+ tt = (time_t)tp.tv_sec;
+ lt = localtime((const time_t*)&tt);
+ switch(level){
+ case ORTP_DEBUG:
+ lname = "DEBUG";
+ break;
+ case ORTP_MESSAGE:
+ lname = "MESSAGE";
+ break;
+ case ORTP_WARNING:
+ lname = "WARNING";
+ break;
+ case ORTP_ERROR:
+ lname = "ERROR";
+ break;
+ case ORTP_FATAL:
+ lname = "FATAL";
+ break;
+ default:
+ ortp_fatal("Bad level !");
+ }
+ msg = ortp_strdup_vprintf(fmt, args);
+
+ log_filename1 = ortp_strdup_printf("%s/%s1.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ log_filename2 = ortp_strdup_printf("%s/%s2.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ ortp_mutex_lock(&liblinphone_log_collection_mutex);
+ log_file = fopen(log_filename1, "a");
+ fstat(fileno(log_file), &statbuf);
+ if (statbuf.st_size > liblinphone_log_collection_max_file_size) {
+ fclose(log_file);
+ log_file = fopen(log_filename2, "a");
+ fstat(fileno(log_file), &statbuf);
+ if (statbuf.st_size > liblinphone_log_collection_max_file_size) {
+ fclose(log_file);
+ unlink(log_filename1);
+ rename(log_filename2, log_filename1);
+ log_file = fopen(log_filename2, "a");
+ }
+ }
+ fprintf(log_file,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s %s\n",
+ 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, (int)(tp.tv_usec / 1000), lname, msg);
+ fflush(log_file);
+ fclose(log_file);
+ ortp_mutex_unlock(&liblinphone_log_collection_mutex);
+
+ ortp_free(log_filename1);
+ ortp_free(log_filename2);
+ ortp_free(msg);
+}
+
+const char * linphone_core_get_log_collection_path(void) {
+ if (liblinphone_log_collection_path != NULL) {
+ return liblinphone_log_collection_path;
+ }
+ return LOG_COLLECTION_DEFAULT_PATH;
+}
+
+void linphone_core_set_log_collection_path(const char *path) {
+ if (liblinphone_log_collection_path != NULL) {
+ ms_free(liblinphone_log_collection_path);
+ liblinphone_log_collection_path = NULL;
+ }
+ if (path != NULL) {
+ liblinphone_log_collection_path = ms_strdup(path);
+ }
+}
+
+const char * linphone_core_get_log_collection_prefix(void) {
+ if (liblinphone_log_collection_prefix != NULL) {
+ return liblinphone_log_collection_prefix;
+ }
+ return LOG_COLLECTION_DEFAULT_PREFIX;
+}
+
+void linphone_core_set_log_collection_prefix(const char *prefix) {
+ if (liblinphone_log_collection_prefix != NULL) {
+ ms_free(liblinphone_log_collection_prefix);
+ liblinphone_log_collection_prefix = NULL;
+ }
+ if (prefix != NULL) {
+ liblinphone_log_collection_prefix = ms_strdup(prefix);
+ }
+}
+
+int linphone_core_get_log_collection_max_file_size(void) {
+ return liblinphone_log_collection_max_file_size;
+}
+
+void linphone_core_set_log_collection_max_file_size(int size) {
+ liblinphone_log_collection_max_file_size = size;
+}
+
+const char *linphone_core_get_log_collection_upload_server_url(LinphoneCore *core) {
+ return lp_config_get_string(core->config, "misc", "log_collection_upload_server_url", NULL);
+}
+
+void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, const char *server_url) {
+ lp_config_set_string(core->config, "misc", "log_collection_upload_server_url", server_url);
+}
+
+LinphoneLogCollectionState linphone_core_log_collection_enabled(void) {
+ return liblinphone_log_collection_state;
+}
+
+void linphone_core_enable_log_collection(LinphoneLogCollectionState state) {
+ if (liblinphone_log_collection_state == state) return;
+
+ /* at first call of this function, set liblinphone_log_func to the current
+ * ortp log function */
+ if( liblinphone_log_func == NULL ){
+ liblinphone_log_func = ortp_logv_out;
+ }
+ liblinphone_log_collection_state = state;
+ if (state != LinphoneLogCollectionDisabled) {
+ ortp_mutex_init(&liblinphone_log_collection_mutex, NULL);
+ if (state == LinphoneLogCollectionEnabledWithoutPreviousLogHandler) {
+ liblinphone_log_func = NULL;
+ } else {
+ liblinphone_log_func = ortp_logv_out;
+ }
+ ortp_set_log_handler(linphone_core_log_collection_handler);
+ } else {
+ ortp_set_log_handler(liblinphone_log_func);
+ }
+}
+
+static void clean_log_collection_upload_context(LinphoneCore *lc) {
+ char *filename = ms_strdup_printf("%s/%s_log.%s",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
+ COMPRESSED_LOG_COLLECTION_EXTENSION);
+ unlink(filename);
+ ms_free(filename);
+ if (lc && lc->log_collection_upload_information) {
+ ms_free(lc->log_collection_upload_information);
+ lc->log_collection_upload_information=NULL;
+ }
+}
+
+static void process_io_error_upload_log_collection(void *data, const belle_sip_io_error_event_t *event) {
+ LinphoneCore *core = (LinphoneCore *)data;
+ ms_error("I/O Error during log collection upload to %s", linphone_core_get_log_collection_upload_server_url(core));
+ linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "I/O Error");
+ clean_log_collection_upload_context(core);
+}
+
+static void process_auth_requested_upload_log_collection(void *data, belle_sip_auth_event_t *event) {
+ LinphoneCore *core = (LinphoneCore *)data;
+ ms_error("Error during log collection upload: auth requested to connect %s", linphone_core_get_log_collection_upload_server_url(core));
+ linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "Auth requested");
+ clean_log_collection_upload_context(core);
+}
+
+/**
+ * Callback called when posting a log collection file to server (following rcs5.1 recommendation)
+ *
+ * @param[in] bh The body handler
+ * @param[in] msg The belle sip message
+ * @param[in] data The user data associated with the handler, contains the LinphoneCore object
+ * @param[in] offset The current position in the input buffer
+ * @param[in] buffer The ouput buffer where to copy the data to be uploaded
+ * @param[in,out] size The size in byte of the data requested, as output it will contain the effective copied size
+ *
+ */
+static int log_collection_upload_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) {
+ LinphoneCore *core = (LinphoneCore *)data;
+
+ /* If we've not reach the end of file yet, fill the buffer with more data */
+ if (offset < linphone_content_get_size(core->log_collection_upload_information)) {
+ char *log_filename = ms_strdup_printf("%s/%s_log.%s",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
+ COMPRESSED_LOG_COLLECTION_EXTENSION);
+#ifdef HAVE_ZLIB
+ FILE *log_file = fopen(log_filename, "rb");
+#else
+ FILE *log_file = fopen(log_filename, "r");
+#endif
+ if (fseek(log_file, offset, SEEK_SET)) {
+ ms_error("Cannot seek file [%s] at position [%lu] errno [%s]",log_filename,(unsigned long)offset,strerror(errno));
+
+ } else {
+ *size = fread(buffer, 1, *size, log_file);
+ }
+ fclose(log_file);
+ ms_free(log_filename);
+ return BELLE_SIP_CONTINUE;
+ } else {
+ *size=0;
+ return BELLE_SIP_STOP;
+ }
+
+
+}
+
+/**
+ * Callback called during upload of a log collection to server.
+ * It is just forwarding the call and some parameters to the vtable defined callback.
+ */
+static void log_collection_upload_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total) {
+ LinphoneCore *core = (LinphoneCore *)data;
+ linphone_core_notify_log_collection_upload_progress_indication(core, offset, total);
+}
+
+/**
+ * Callback function called when we have a response from server during the upload of the log collection to the server (rcs5.1 recommandation)
+ * Note: The first post is empty and the server shall reply a 204 (No content) message, this will trigger a new post request to the server
+ * to upload the file. The server response to this second post is processed by this same function
+ *
+ * @param[in] data The user-defined pointer associated with the request, it contains the LinphoneCore object
+ * @param[in] event The response from server
+ */
+static void process_response_from_post_file_log_collection(void *data, const belle_http_response_event_t *event) {
+ LinphoneCore *core = (LinphoneCore *)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 file */
+ /* Start uploading the file */
+ belle_http_request_listener_callbacks_t cbs = { 0 };
+ belle_http_request_listener_t *l;
+ belle_generic_uri_t *uri;
+ belle_http_request_t *req;
+ belle_sip_multipart_body_handler_t *bh;
+ char* ua;
+ char *first_part_header;
+ belle_sip_user_body_handler_t *first_part_bh;
+
+ linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateInProgress, NULL);
+
+ /* 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(core->log_collection_upload_information));
+
+ /* Create a user body handler to take care of the file and add the content disposition and content-type headers */
+ first_part_bh = belle_sip_user_body_handler_new(linphone_content_get_size(core->log_collection_upload_information), NULL, NULL, log_collection_upload_on_send_body, core);
+ belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, belle_sip_header_create("Content-disposition", first_part_header));
+ belle_sip_free(first_part_header);
+ belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh,
+ (belle_sip_header_t *)belle_sip_header_content_type_create(linphone_content_get_type(core->log_collection_upload_information), linphone_content_get_subtype(core->log_collection_upload_information)));
+
+ /* Insert it in a multipart body handler which will manage the boundaries of multipart message */
+ bh = belle_sip_multipart_body_handler_new(log_collection_upload_on_progress, core, (belle_sip_body_handler_t *)first_part_bh);
+ 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_log_collection_upload_server_url(core));
+ req = belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent", ua), NULL);
+ ms_free(ua);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh));
+ cbs.process_response = process_response_from_post_file_log_collection;
+ cbs.process_io_error = process_io_error_upload_log_collection;
+ cbs.process_auth_requested = process_auth_requested_upload_log_collection;
+ l = belle_http_request_listener_create_from_callbacks(&cbs, core);
+ belle_http_provider_send_request(core->http_provider, req, l);
+ }
+ if (code == 200) { /* The file has been uploaded correctly, get the server reply */
+ xmlDocPtr xmlMessageBody;
+ xmlNodePtr cur;
+ xmlChar *file_url = NULL;
+ const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
+ xmlMessageBody = xmlParseDoc((const xmlChar *)body);
+ 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 */
+ cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
+ while (cur != NULL) {
+ if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
+ file_url = xmlGetProp(cur, (const xmlChar *)"url");
+ }
+ cur=cur->next;
+ }
+ xmlFree(typeAttribute);
+ break;
+ }
+ xmlFree(typeAttribute);
+ }
+ cur = cur->next;
+ }
+ }
+ if (file_url != NULL) {
+ linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateDelivered, (const char *)file_url);
+ }
+ clean_log_collection_upload_context(core);
+ }
+ }
+}
+
+#ifdef HAVE_ZLIB
+#define COMPRESS_FILE_PTR gzFile
+#define COMPRESS_OPEN gzopen
+#define COMPRESS_CLOSE gzclose
+#else
+#define COMPRESS_FILE_PTR FILE*
+#define COMPRESS_OPEN fopen
+#define COMPRESS_CLOSE fclose
+#endif
+
+/**
+ * If zlib is not available the two log files are simply concatenated.
+ */
+static int compress_file(FILE *input_file, COMPRESS_FILE_PTR output_file) {
+ char buffer[131072]; /* 128kB */
+ int bytes;
+
+ while ((bytes = fread(buffer, 1, sizeof(buffer), input_file)) > 0) {
+ if (bytes < 0) return bytes;
+#ifdef HAVE_ZLIB
+ bytes = gzwrite(output_file, buffer, bytes);
+#else
+ bytes = fwrite(buffer, 1, bytes, output_file);
+#endif
+ if (bytes < 0) return bytes;
+ }
+ return 0;
+}
+
+static int prepare_log_collection_file_to_upload(const char *filename) {
+ char *input_filename = NULL;
+ char *output_filename = NULL;
+ FILE *input_file = NULL;
+ COMPRESS_FILE_PTR output_file = NULL;
+ int ret = 0;
+
+ ortp_mutex_lock(&liblinphone_log_collection_mutex);
+ output_filename = ms_strdup_printf("%s/%s",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH, filename);
+ output_file = COMPRESS_OPEN(output_filename, "w");
+ if (output_file == NULL) goto error;
+ input_filename = ms_strdup_printf("%s/%s1.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ input_file = fopen(input_filename, "r");
+ if (input_file == NULL) goto error;
+ ret = compress_file(input_file, output_file);
+ if (ret < 0) goto error;
+ fclose(input_file);
+ ms_free(input_filename);
+ input_filename = ms_strdup_printf("%s/%s2.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ input_file = fopen(input_filename, "r");
+ if (input_file != NULL) {
+ ret = compress_file(input_file, output_file);
+ if (ret < 0) goto error;
+ }
+
+error:
+ if (input_file != NULL) fclose(input_file);
+ if (output_file != NULL) COMPRESS_CLOSE(output_file);
+ if (input_filename != NULL) ms_free(input_filename);
+ if (output_filename != NULL) ms_free(output_filename);
+ ortp_mutex_unlock(&liblinphone_log_collection_mutex);
+ return ret;
+}
+
+static size_t get_size_of_file_to_upload(const char *filename) {
+ struct stat statbuf;
+ char *output_filename = ms_strdup_printf("%s/%s",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH, filename);
+ FILE *output_file = fopen(output_filename, "rb");
+ fstat(fileno(output_file), &statbuf);
+ fclose(output_file);
+ ms_free(output_filename);
+ return statbuf.st_size;
+}
+
+void linphone_core_upload_log_collection(LinphoneCore *core) {
+ if ((core->log_collection_upload_information == NULL) && (linphone_core_get_log_collection_upload_server_url(core) != NULL) && (liblinphone_log_collection_state != LinphoneLogCollectionDisabled)) {
+ /* 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;
+ belle_http_request_t *req;
+ char *name;
+
+ core->log_collection_upload_information = (LinphoneContent *)malloc(sizeof(LinphoneContent));
+ memset(core->log_collection_upload_information, 0, sizeof(LinphoneContent));
+#ifdef HAVE_ZLIB
+ linphone_content_set_type(core->log_collection_upload_information, "application");
+ linphone_content_set_subtype(core->log_collection_upload_information, "gzip");
+#else
+ linphone_content_set_type(core->log_collection_upload_information, "text");
+ linphone_content_set_subtype(core->log_collection_upload_information,"plain");
+#endif
+ name = ms_strdup_printf("%s_log.%s",
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
+ COMPRESSED_LOG_COLLECTION_EXTENSION);
+ linphone_content_set_name(core->log_collection_upload_information, name);
+ if (prepare_log_collection_file_to_upload(name) < 0) return;
+ linphone_content_set_size(core->log_collection_upload_information, get_size_of_file_to_upload(name));
+ uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core));
+ req = belle_http_request_create("POST", uri, NULL, NULL, NULL);
+ cbs.process_response = process_response_from_post_file_log_collection;
+ cbs.process_io_error = process_io_error_upload_log_collection;
+ cbs.process_auth_requested = process_auth_requested_upload_log_collection;
+ l = belle_http_request_listener_create_from_callbacks(&cbs, core);
+ belle_http_provider_send_request(core->http_provider, req, l);
+ ms_free(name);
+ }
+}
+
+char * linphone_core_compress_log_collection() {
+ char *filename = NULL;
+ if (liblinphone_log_collection_state == LinphoneLogCollectionDisabled) return NULL;
+ filename = ms_strdup_printf("%s_log.%s",
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
+ COMPRESSED_LOG_COLLECTION_EXTENSION);
+ if (prepare_log_collection_file_to_upload(filename) < 0) {
+ ms_free(filename);
+ return NULL;
+ }
+ ms_free(filename);
+ return ms_strdup_printf("%s/%s_log.%s",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
+ COMPRESSED_LOG_COLLECTION_EXTENSION);
+}
+
+void linphone_core_reset_log_collection() {
+ char *filename;
+ ortp_mutex_lock(&liblinphone_log_collection_mutex);
+ clean_log_collection_upload_context(NULL);
+ filename = ms_strdup_printf("%s/%s1.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ unlink(filename);
+ ms_free(filename);
+ filename = ms_strdup_printf("%s/%s2.log",
+ liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
+ liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
+ unlink(filename);
+ ms_free(filename);
+ ortp_mutex_unlock(&liblinphone_log_collection_mutex);
+}
+
/**
* Enable logs in supplied FILE*.
*
* @ingroup misc
+ * @deprecated Use #linphone_core_set_log_file and #linphone_core_set_log_level instead.
*
* @param file a C FILE* where to fprintf logs. If null stdout is used.
*
@@ -412,30 +682,36 @@ const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _Lin
void linphone_core_enable_logs(FILE *file){
if (file==NULL) file=stdout;
ortp_set_log_file(file);
- ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
+ linphone_core_set_log_level(ORTP_MESSAGE);
}
/**
* Enable logs through the user's supplied log callback.
*
* @ingroup misc
+ * @deprecated Use #linphone_core_set_log_handler and #linphone_core_set_log_level instead.
*
* @param logfunc The address of a OrtpLogFunc callback whose protoype is
* typedef void (*OrtpLogFunc)(OrtpLogLevel lev, const char *fmt, va_list args);
*
**/
void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){
- ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
- ortp_set_log_handler(logfunc);
+ linphone_core_set_log_level(ORTP_MESSAGE);
+ linphone_core_set_log_handler(logfunc);
}
/**
* Entirely disable logging.
*
* @ingroup misc
+ * @deprecated Use #linphone_core_set_log_level instead.
**/
-void linphone_core_disable_logs(){
- ortp_set_log_level_mask(ORTP_ERROR|ORTP_FATAL);
+void linphone_core_disable_logs(void){
+ linphone_core_set_log_level(ORTP_ERROR);
+}
+
+void linphone_core_serialize_logs(void) {
+ liblinphone_serialize_logs = TRUE;
}
@@ -454,15 +730,20 @@ static void net_config_read (LinphoneCore *lc)
tmpstr=lp_config_get_string(lc->config,"net","nat_address",NULL);
if (tmpstr!=NULL && (strlen(tmpstr)<1)) tmpstr=NULL;
linphone_core_set_nat_address(lc,tmpstr);
- tmp=lp_config_get_int(lc->config,"net","firewall_policy",0);
- linphone_core_set_firewall_policy(lc,tmp);
tmp=lp_config_get_int(lc->config,"net","nat_sdp_only",0);
lc->net_conf.nat_sdp_only=tmp;
- tmp=lp_config_get_int(lc->config,"net","mtu",0);
+ tmp=lp_config_get_int(lc->config,"net","mtu",1300);
linphone_core_set_mtu(lc,tmp);
- tmp=lp_config_get_int(lc->config,"net","download_ptime",0);
- linphone_core_set_download_ptime(lc,tmp);
+ tmp=lp_config_get_int(lc->config,"net","download_ptime",-1);
+ if (tmp !=-1 && linphone_core_get_download_ptime(lc) !=0) {
+ /*legacy parameter*/
+ linphone_core_set_download_ptime(lc,tmp);
+ }
+ tmp = lp_config_get_int(lc->config, "net", "dns_srv_enabled", 1);
+ linphone_core_enable_dns_srv(lc, tmp);
+ /* This is to filter out unsupported firewall policies */
+ linphone_core_set_firewall_policy(lc, linphone_core_get_firewall_policy(lc));
}
static void build_sound_devices_table(LinphoneCore *lc){
@@ -491,8 +772,25 @@ static void sound_config_read(LinphoneCore *lc)
/*alsadev let the user use custom alsa device within linphone*/
devid=lp_config_get_string(lc->config,"sound","alsadev",NULL);
if (devid){
- MSSndCard *card=ms_alsa_card_new_custom(devid,devid);
- ms_snd_card_manager_add_card(ms_snd_card_manager_get(),card);
+ MSSndCard* card;
+ const char* delim=",";
+ size_t l=strlen(devid);
+ char* d=malloc(l+1);
+ char* i;
+ memcpy(d,devid,l+1);
+ for (l=0,i=strpbrk(d+l,delim);i;i=strpbrk(d+l,delim)){
+ char s=*i;
+ *i='\0';
+ card=ms_alsa_card_new_custom(d+l,d+l);
+ ms_snd_card_manager_add_card(ms_snd_card_manager_get(),card);
+ *i=s;
+ l=i-d+1;
+ }
+ if(d[l]!='\0') {
+ card=ms_alsa_card_new_custom(d+l,d+l);
+ ms_snd_card_manager_add_card(ms_snd_card_manager_get(),card);
+ }
+ free(d);
}
tmp=lp_config_get_int(lc->config,"sound","alsa_forced_rate",-1);
if (tmp>0) ms_alsa_card_set_forced_sample_rate(tmp);
@@ -544,9 +842,8 @@ static void sound_config_read(LinphoneCore *lc)
linphone_core_set_ringback(lc,tmpbuf);
linphone_core_set_play_file(lc,lp_config_get_string(lc->config,"sound","hold_music",PACKAGE_SOUND_DIR "/" HOLD_MUSIC));
- check_sound_device(lc);
lc->sound_conf.latency=0;
-#ifndef __ios
+#ifndef __ios
tmp=TRUE;
#else
tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
@@ -565,6 +862,27 @@ static void sound_config_read(LinphoneCore *lc)
/*just parse requested stream feature once at start to print out eventual errors*/
linphone_core_get_audio_features(lc);
+
+ _linphone_core_set_tone(lc,LinphoneReasonBusy,LinphoneToneBusy,NULL);
+}
+
+static void certificates_config_read(LinphoneCore *lc)
+{
+ const char *rootca;
+#ifdef __linux
+ struct stat sb;
+ rootca=lp_config_get_string(lc->config,"sip","root_ca", "/etc/ssl/certs");
+ if (stat("/etc/ssl/certs", &sb) != 0 || !S_ISDIR(sb.st_mode))
+ {
+ ms_warning("/etc/ssl/certs not found, using %s instead", ROOT_CA_FILE);
+ rootca=lp_config_get_string(lc->config,"sip","root_ca", ROOT_CA_FILE);
+ }
+#else
+ rootca=lp_config_get_string(lc->config,"sip","root_ca", ROOT_CA_FILE);
+#endif
+ linphone_core_set_root_ca(lc,rootca);
+ linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
+ linphone_core_verify_server_cn(lc,lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
}
static void sip_config_read(LinphoneCore *lc)
@@ -574,22 +892,13 @@ static void sip_config_read(LinphoneCore *lc)
LCSipTransports tr;
int i,tmp;
int ipv6;
- int random_port;
-
- tmp=lp_config_get_int(lc->config,"sip","use_info",0);
- linphone_core_set_use_info_for_dtmf(lc,tmp);
if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){
sal_use_session_timers(lc->sal,200);
}
+ sal_use_no_initial_route(lc->sal,lp_config_get_int(lc->config,"sip","use_no_initial_route",0));
sal_use_rport(lc->sal,lp_config_get_int(lc->config,"sip","use_rport",1));
- sal_use_101(lc->sal,lp_config_get_int(lc->config,"sip","use_101",1));
- sal_reuse_authorization(lc->sal, lp_config_get_int(lc->config,"sip","reuse_authorization",0));
- sal_expire_old_registration_contacts(lc->sal,lp_config_get_int(lc->config,"sip","expire_old_registration_contacts",0));
-
- tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",1);
- linphone_core_set_use_rfc2833_for_dtmf(lc,tmp);
ipv6=lp_config_get_int(lc->config,"sip","use_ipv6",-1);
if (ipv6==-1){
@@ -597,41 +906,17 @@ static void sip_config_read(LinphoneCore *lc)
}
linphone_core_enable_ipv6(lc,ipv6);
memset(&tr,0,sizeof(tr));
-
- tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",0);
- tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",0);
- tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",0);
-
- if (lp_config_get_int(lc->config,"sip","sip_random_port",0)==1)
- random_port=(0xDFFF&random())+1024;
- else random_port=0;
-
- if (tr.udp_port==0 && tr.tcp_port==0 && tr.tls_port==0){
- tr.udp_port=5060;
- }
-
- if (tr.udp_port>0 && random_port){
- tr.udp_port=random_port;
- tr.tls_port=tr.tcp_port=0; /*make sure only one transport is active at a time*/
- }else if (tr.tcp_port>0 && random_port){
- tr.tcp_port=random_port;
- tr.tls_port=tr.udp_port=0; /*make sure only one transport is active at a time*/
- }else if (tr.tls_port>0 && random_port){
- tr.tls_port=random_port;
- tr.udp_port=tr.tcp_port=0; /*make sure only one transport is active at a time*/
- }
-#ifdef __linux
- sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", "/etc/ssl/certs"));
-#else
- sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", ROOT_CA_FILE));
-#endif
- linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
- linphone_core_verify_server_cn(lc,lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
+ tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",5060);
+ tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",5060);
+ /*we are not listening inbound connection for tls, port has no meaning*/
+ tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",LC_SIP_TRANSPORT_RANDOM);
+
+ certificates_config_read(lc);
/*setting the dscp must be done before starting the transports, otherwise it is not taken into effect*/
sal_set_dscp(lc->sal,linphone_core_get_sip_dscp(lc));
/*start listening on ports*/
- linphone_core_set_sip_transports(lc,&tr);
+ linphone_core_set_sip_transports(lc,&tr);
tmpstr=lp_config_get_string(lc->config,"sip","contact",NULL);
if (tmpstr==NULL || linphone_core_set_primary_contact(lc,tmpstr)==-1) {
@@ -655,21 +940,24 @@ static void sip_config_read(LinphoneCore *lc)
tmp=lp_config_get_int(lc->config,"sip","guess_hostname",1);
linphone_core_set_guess_hostname(lc,tmp);
+ tmp=lp_config_get_int(lc->config,"sip","lime",FALSE);
+ linphone_core_enable_lime(lc,tmp);
tmp=lp_config_get_int(lc->config,"sip","inc_timeout",30);
linphone_core_set_inc_timeout(lc,tmp);
tmp=lp_config_get_int(lc->config,"sip","in_call_timeout",0);
linphone_core_set_in_call_timeout(lc,tmp);
-
+
tmp=lp_config_get_int(lc->config,"sip","delayed_timeout",4);
linphone_core_set_delayed_timeout(lc,tmp);
/* get proxies config */
for(i=0;; i++){
- LinphoneProxyConfig *cfg=linphone_proxy_config_new_from_config_file(lc->config,i);
+ LinphoneProxyConfig *cfg=linphone_proxy_config_new_from_config_file(lc,i);
if (cfg!=NULL){
linphone_core_add_proxy_config(lc,cfg);
+ linphone_proxy_config_unref(cfg);
}else{
break;
}
@@ -697,14 +985,18 @@ static void sip_config_read(LinphoneCore *lc)
lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1);
lc->sip_conf.register_only_when_upnp_is_ok=
lp_config_get_int(lc->config,"sip","register_only_when_upnp_is_ok",1);
- lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1);
+ lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",0);
lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1);
lc->sip_conf.keepalive_period=lp_config_get_int(lc->config,"sip","keepalive_period",10000);
lc->sip_conf.tcp_tls_keepalive=lp_config_get_int(lc->config,"sip","tcp_tls_keepalive",0);
linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0));
sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0));
- sal_use_double_registrations(lc->sal,lp_config_get_int(lc->config,"sip","use_double_registrations",1));
sal_use_dates(lc->sal,lp_config_get_int(lc->config,"sip","put_date",0));
+ sal_enable_sip_update_method(lc->sal,lp_config_get_int(lc->config,"sip","sip_update",1));
+ lc->sip_conf.vfu_with_info=lp_config_get_int(lc->config,"sip","vfu_with_info",1);
+ linphone_core_set_sip_transport_timeout(lc, lp_config_get_int(lc->config, "sip", "transport_timeout", 63000));
+ sal_set_supported_tags(lc->sal,lp_config_get_string(lc->config,"sip","supported","replaces, outbound"));
+ lc->sip_conf.save_auth_info = lp_config_get_int(lc->config, "sip", "save_auth_info", 1);
}
static void rtp_config_read(LinphoneCore *lc)
@@ -714,6 +1006,8 @@ static void rtp_config_read(LinphoneCore *lc)
int nortp_timeout;
bool_t rtp_no_xmit_on_audio_mute;
bool_t adaptive_jitt_comp_enabled;
+ const char* tmp;
+ int tmp_int;
if (lp_config_get_range(lc->config, "rtp", "audio_rtp_port", &min_port, &max_port, 7078, 7078) == TRUE) {
if (min_port <= 0) min_port = 1;
@@ -746,14 +1040,37 @@ static void rtp_config_read(LinphoneCore *lc)
linphone_core_enable_audio_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled);
adaptive_jitt_comp_enabled = lp_config_get_int(lc->config, "rtp", "video_adaptive_jitt_comp_enabled", TRUE);
linphone_core_enable_video_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled);
+ lc->rtp_conf.disable_upnp = lp_config_get_int(lc->config, "rtp", "disable_upnp", FALSE);
+ linphone_core_set_avpf_mode(lc,lp_config_get_int(lc->config,"rtp","avpf",0));
+ if ((tmp=lp_config_get_string(lc->config,"rtp","audio_multicast_addr",NULL)))
+ linphone_core_set_audio_multicast_addr(lc,tmp);
+ else
+ lc->rtp_conf.audio_multicast_addr=ms_strdup("224.1.2.3");
+ if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_enabled",-1)) >-1)
+ linphone_core_enable_audio_multicast(lc,tmp_int);
+ if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_ttl",-1))>0)
+ linphone_core_set_audio_multicast_ttl(lc,tmp_int);
+ else
+ lc->rtp_conf.audio_multicast_ttl=1;/*local network*/
+ if ((tmp=lp_config_get_string(lc->config,"rtp","video_multicast_addr",NULL)))
+ linphone_core_set_video_multicast_addr(lc,tmp);
+ else
+ lc->rtp_conf.video_multicast_addr=ms_strdup("224.1.2.3");
+ if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_ttl",-1))>-1)
+ linphone_core_set_video_multicast_ttl(lc,tmp_int);
+ else
+ lc->rtp_conf.video_multicast_ttl=1;/*local network*/
+ if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_enabled",-1)) >0)
+ linphone_core_enable_video_multicast(lc,tmp_int);
}
-static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){
+static PayloadType * find_payload(const MSList *default_list, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){
PayloadType *candidate=NULL;
- int i;
PayloadType *it;
- for(i=0;inext){
+ it=(PayloadType*)elem->data;
if (it!=NULL && strcasecmp(mime_type,it->mime_type)==0
&& (clock_rate==it->clock_rate || clock_rate<=0)
&& (channels==it->channels || channels<=0) ){
@@ -775,7 +1092,28 @@ static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int c
return candidate;
}
-static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadType **ret){
+static PayloadType* find_payload_type_from_list(const char* type, int rate, int channels, const MSList* from) {
+ const MSList *elem;
+ for(elem=from;elem!=NULL;elem=elem->next){
+ PayloadType *pt=(PayloadType*)elem->data;
+ if ((strcasecmp(type, payload_type_get_mime(pt)) == 0)
+ && (rate == LINPHONE_FIND_PAYLOAD_IGNORE_RATE || rate==pt->clock_rate)
+ && (channels == LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS || channels==pt->channels)) {
+ return pt;
+ }
+ }
+ return NULL;
+}
+
+static bool_t linphone_core_codec_supported(LinphoneCore *lc, SalStreamType type, const char *mime){
+ if (type == SalVideo && lp_config_get_int(lc->config, "video", "rtp_io", FALSE)){
+ return TRUE; /*in rtp io mode, we don't transcode video, thus we can support a format for which we have no encoder nor decoder.*/
+ }
+ return ms_filter_codec_supported(mime);
+}
+
+
+static bool_t get_codec(LinphoneCore *lc, SalStreamType type, int index, PayloadType **ret){
char codeckey[50];
const char *mime,*fmtp;
int rate,channels,enabled;
@@ -783,7 +1121,7 @@ static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadTy
LpConfig *config=lc->config;
*ret=NULL;
- snprintf(codeckey,50,"%s_%i",type,index);
+ snprintf(codeckey,50,"%s_codec_%i",type==SalAudio ? "audio" : "video", index);
mime=lp_config_get_string(config,codeckey,"mime",NULL);
if (mime==NULL || strlen(mime)==0 ) return FALSE;
@@ -791,77 +1129,57 @@ static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadTy
fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL);
channels=lp_config_get_int(config,codeckey,"channels",0);
enabled=lp_config_get_int(config,codeckey,"enabled",1);
- pt=find_payload(lc->default_profile,mime,rate,channels,fmtp);
- if (pt && enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED;
- //ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate);
- if (pt==NULL) ms_warning("Ignoring codec config %s/%i with fmtp=%s because unsupported",
- mime,rate,fmtp ? fmtp : "");
+ if (!linphone_core_codec_supported(lc, type, mime)){
+ ms_warning("Codec %s/%i read from conf is not supported by mediastreamer2, ignored.",mime,rate);
+ return TRUE;
+ }
+ pt=find_payload(type==SalAudio ? lc->default_audio_codecs : lc->default_video_codecs,mime,rate,channels,fmtp);
+ if (!pt){
+ MSList **default_list=(type==SalAudio) ? &lc->default_audio_codecs : &lc->default_video_codecs;
+ if (type==SalAudio)
+ ms_warning("Codec %s/%i/%i read from conf is not in the default list.",mime,rate,channels);
+ else
+ ms_warning("Codec %s/%i read from conf is not in the default list.",mime,rate);
+ pt=payload_type_new();
+ pt->type=(type==SalAudio) ? PAYLOAD_AUDIO_PACKETIZED : PAYLOAD_VIDEO;
+ pt->mime_type=ortp_strdup(mime);
+ pt->clock_rate=rate;
+ pt->channels=channels;
+ payload_type_set_number(pt,-1); /*dynamic assignment*/
+ payload_type_set_recv_fmtp(pt,fmtp);
+ *default_list=ms_list_append(*default_list, pt);
+ }
+ if (enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED;
+ else pt->flags&=~PAYLOAD_TYPE_ENABLED;
*ret=pt;
return TRUE;
}
-#define RANK_END 10000
+/*this function merges the payload types from the codec default list with the list read from configuration file.
+ * If a new codec becomes supported in Liblinphone or if the list from configuration file is empty or incomplete, all the supported codecs are added
+ * automatically. This 'l' list is entirely destroyed and rewritten.*/
+static MSList *add_missing_codecs(const MSList *default_list, MSList *l){
+ const MSList *elem;
+ MSList *newlist;
-typedef struct codec_desc{
- const char *name;
- int rate;
-}codec_desc_t;
-
-static codec_desc_t codec_pref_order[]={
- {"SILK", 16000},
- {"speex", 16000},
- {"speex", 8000},
- {"pcmu",8000},
- {"pcma",8000},
- {"VP8",90000},
- {"H264",90000},
- {"MP4V-ES",90000},
- {NULL,0}
-};
-
-static int find_codec_rank(const char *mime, int clock_rate){
- int i;
- for(i=0;codec_pref_order[i].name!=NULL;++i){
- if (strcasecmp(codec_pref_order[i].name,mime)==0 && clock_rate==codec_pref_order[i].rate)
- return i;
- }
- return RANK_END;
-}
-
-static int codec_compare(const PayloadType *a, const PayloadType *b){
- int ra,rb;
- ra=find_codec_rank(a->mime_type,a->clock_rate);
- rb=find_codec_rank(b->mime_type,b->clock_rate);
- if (ra>rb) return 1;
- if (radefault_profile,i);
- if (pt){
- if (mtype==SalVideo && pt->type!=PAYLOAD_VIDEO)
- pt=NULL;
- else if (mtype==SalAudio && (pt->type!=PAYLOAD_AUDIO_PACKETIZED
- && pt->type!=PAYLOAD_AUDIO_CONTINUOUS)){
- pt=NULL;
- }
- if (pt && ms_filter_codec_supported(pt->mime_type)){
- if (ms_list_find(l,pt)==NULL){
- /*unranked codecs are disabled by default*/
- if (find_codec_rank(pt->mime_type, pt->clock_rate)!=RANK_END){
- payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED);
- }
- ms_message("Adding new codec %s/%i with fmtp %s",
- pt->mime_type,pt->clock_rate,pt->recv_fmtp ? pt->recv_fmtp : "");
- l=ms_list_insert_sorted(l,pt,(int (*)(const void *, const void *))codec_compare);
- }
+ for(elem=default_list; elem!=NULL; elem=elem->next){
+ MSList *elem2=ms_list_find(l,elem->data);
+ if (!elem2){
+ PayloadType *pt=(PayloadType*)elem->data;
+ /*this codec from default list should be inserted in the list*/
+ if (!elem->prev){
+ l=ms_list_prepend(l,pt);
+ }else{
+ const MSList *after=ms_list_find(l,elem->prev->data);
+ l=ms_list_insert(l, after->next, pt);
}
+ ms_message("Supported codec %s/%i fmtp=%s automatically added to codec list.", pt->mime_type,
+ pt->clock_rate, pt->recv_fmtp ? pt->recv_fmtp : "");
}
}
- return l;
+ newlist=ms_list_copy_with_data(l,(void *(*)(void*))payload_type_clone);
+ ms_list_free(l);
+ return newlist;
}
static MSList *codec_append_if_new(MSList *l, PayloadType *pt){
@@ -881,22 +1199,27 @@ static void codecs_config_read(LinphoneCore *lc)
PayloadType *pt;
MSList *audio_codecs=NULL;
MSList *video_codecs=NULL;
- for (i=0;get_codec(lc,"audio_codec",i,&pt);i++){
+
+ lc->codecs_conf.dyn_pt=96;
+ lc->codecs_conf.telephone_event_pt=lp_config_get_int(lc->config,"misc","telephone_event_pt",101);
+
+ for (i=0;get_codec(lc,SalAudio,i,&pt);i++){
if (pt){
- if (!ms_filter_codec_supported(pt->mime_type)){
- ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type);
- }else audio_codecs=codec_append_if_new(audio_codecs,pt);
+ audio_codecs=codec_append_if_new(audio_codecs, pt);
}
}
- audio_codecs=add_missing_codecs(lc,SalAudio,audio_codecs);
- for (i=0;get_codec(lc,"video_codec",i,&pt);i++){
+ if( lp_config_get_int(lc->config, "misc", "add_missing_audio_codecs", 1) == 1 ){
+ audio_codecs=add_missing_codecs(lc->default_audio_codecs,audio_codecs);
+ }
+
+ for (i=0;get_codec(lc,SalVideo,i,&pt);i++){
if (pt){
- if (!ms_filter_codec_supported(pt->mime_type)){
- ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type);
- }else video_codecs=codec_append_if_new(video_codecs,(void *)pt);
+ video_codecs=codec_append_if_new(video_codecs, pt);
}
}
- video_codecs=add_missing_codecs(lc,SalVideo,video_codecs);
+ if( lp_config_get_int(lc->config, "misc", "add_missing_video_codecs", 1) == 1 ){
+ video_codecs=add_missing_codecs(lc->default_video_codecs,video_codecs);
+ }
linphone_core_set_audio_codecs(lc,audio_codecs);
linphone_core_set_video_codecs(lc,video_codecs);
linphone_core_update_allocated_audio_bandwidth(lc);
@@ -922,10 +1245,11 @@ static void build_video_devices_table(LinphoneCore *lc){
static void video_config_read(LinphoneCore *lc){
#ifdef VIDEO_ENABLED
- int capture, display, self_view;
+ int capture, display, self_view, reuse_source;
+ int automatic_video=1;
#endif
- const char *str;
-#ifdef VIDEO_ENABLED
+ const char *str;
+#ifdef VIDEO_ENABLED
LinphoneVideoPolicy vpol;
memset(&vpol, 0, sizeof(LinphoneVideoPolicy));
#endif
@@ -938,19 +1262,26 @@ static void video_config_read(LinphoneCore *lc){
linphone_core_set_preferred_video_size_by_name(lc,
lp_config_get_string(lc->config,"video","size","cif"));
+ linphone_core_set_preview_video_size_by_name(lc,
+ lp_config_get_string(lc->config,"video","preview_size",NULL));
+
+ linphone_core_set_preferred_framerate(lc,lp_config_get_float(lc->config,"video","framerate",0));
+
#ifdef VIDEO_ENABLED
+#if defined(ANDROID) || defined(__ios)
+ automatic_video=0;
+#endif
capture=lp_config_get_int(lc->config,"video","capture",1);
display=lp_config_get_int(lc->config,"video","display",1);
self_view=lp_config_get_int(lc->config,"video","self_view",1);
- vpol.automatically_initiate=lp_config_get_int(lc->config,"video","automatically_initiate",1);
- vpol.automatically_accept=lp_config_get_int(lc->config,"video","automatically_accept",1);
- lc->video_conf.displaytype=lp_config_get_string(lc->config,"video","displaytype",NULL);
- if(lc->video_conf.displaytype)
- ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype);
-
- linphone_core_enable_video(lc,capture,display);
+ reuse_source=lp_config_get_int(lc->config,"video","reuse_source",0);
+ vpol.automatically_initiate=lp_config_get_int(lc->config,"video","automatically_initiate",automatic_video);
+ vpol.automatically_accept=lp_config_get_int(lc->config,"video","automatically_accept",automatic_video);
+ linphone_core_enable_video_capture(lc, capture);
+ linphone_core_enable_video_display(lc, display);
linphone_core_enable_video_preview(lc,lp_config_get_int(lc->config,"video","show_local",0));
linphone_core_enable_self_view(lc,self_view);
+ linphone_core_enable_video_source_reuse(lc, reuse_source);
linphone_core_set_video_policy(lc,&vpol);
#endif
}
@@ -961,6 +1292,7 @@ static void ui_config_read(LinphoneCore *lc)
int i;
for (i=0;(lf=linphone_friend_new_from_config_file(lc,i))!=NULL;i++){
linphone_core_add_friend(lc,lf);
+ linphone_friend_unref(lf);
}
call_logs_read_from_config_file(lc);
}
@@ -986,400 +1318,363 @@ bool_t linphone_core_tunnel_available(void){
#endif
}
-/**
- * Enable adaptive rate control.
- *
- * @ingroup media_parameters
- *
- * Adaptive rate control consists in using RTCP feedback provided information to dynamically
- * control the output bitrate of the audio and video encoders, so that we can adapt to the network conditions and
- * available bandwidth. Control of the audio encoder is done in case of audio-only call, and control of the video encoder is done for audio & video calls.
- * Adaptive rate control feature is enabled by default.
-**/
void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled){
lp_config_set_int(lc->config,"net","adaptive_rate_control",(int)enabled);
}
-/**
- * Returns whether adaptive rate control is enabled.
- *
- * @ingroup media_parameters
- *
- * See linphone_core_enable_adaptive_rate_control().
-**/
bool_t linphone_core_adaptive_rate_control_enabled(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"net","adaptive_rate_control",TRUE);
}
+void linphone_core_set_adaptive_rate_algorithm(LinphoneCore *lc, const char* algorithm){
+ lp_config_set_string(lc->config,"net","adaptive_rate_algorithm",algorithm);
+}
+
+const char * linphone_core_get_adaptive_rate_algorithm(const LinphoneCore *lc){
+ return lp_config_get_string(lc->config, "net", "adaptive_rate_algorithm", "Simple");
+}
+
bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"rtp","rtcp_enabled",TRUE);
}
-/**
- * Sets maximum available download bandwidth
- *
- * @ingroup media_parameters
- *
- * This is IP bandwidth, in kbit/s.
- * This information is used signaled to other parties during
- * calls (within SDP messages) so that the remote end can have
- * sufficient knowledge to properly configure its audio & video
- * codec output bitrate to not overflow available bandwidth.
- *
- * @param lc the LinphoneCore object
- * @param bw the bandwidth in kbits/s, 0 for infinite
- */
void linphone_core_set_download_bandwidth(LinphoneCore *lc, int bw){
lc->net_conf.download_bw=bw;
+ linphone_core_update_allocated_audio_bandwidth(lc);
if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"net","download_bw",bw);
}
-/**
- * Sets maximum available upload bandwidth
- *
- * @ingroup media_parameters
- *
- * This is IP bandwidth, in kbit/s.
- * This information is used by liblinphone together with remote
- * side available bandwidth signaled in SDP messages to properly
- * configure audio & video codec's output bitrate.
- *
- * @param lc the LinphoneCore object
- * @param bw the bandwidth in kbits/s, 0 for infinite
- */
void linphone_core_set_upload_bandwidth(LinphoneCore *lc, int bw){
lc->net_conf.upload_bw=bw;
+ linphone_core_update_allocated_audio_bandwidth(lc);
if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"net","upload_bw",bw);
}
-/**
- * Retrieve the maximum available download bandwidth.
- *
- * @ingroup media_parameters
- *
- * This value was set by linphone_core_set_download_bandwidth().
- *
-**/
+void linphone_core_set_sip_transport_timeout(LinphoneCore *lc, int timeout_ms) {
+ sal_set_transport_timeout(lc->sal, timeout_ms);
+ if (linphone_core_ready(lc))
+ lp_config_set_int(lc->config, "sip", "transport_timeout", timeout_ms);
+}
+
+int linphone_core_get_sip_transport_timeout(LinphoneCore *lc) {
+ return sal_get_transport_timeout(lc->sal);
+}
+
+void linphone_core_enable_dns_srv(LinphoneCore *lc, bool_t enable) {
+ sal_enable_dns_srv(lc->sal, enable);
+ if (linphone_core_ready(lc))
+ lp_config_set_int(lc->config, "net", "dns_srv_enabled", enable ? 1 : 0);
+}
+
+bool_t linphone_core_dns_srv_enabled(const LinphoneCore *lc) {
+ return sal_dns_srv_enabled(lc->sal);
+}
+
int linphone_core_get_download_bandwidth(const LinphoneCore *lc){
return lc->net_conf.download_bw;
}
-/**
- * Retrieve the maximum available upload bandwidth.
- *
- * @ingroup media_parameters
- *
- * This value was set by linphone_core_set_upload_bandwidth().
- *
-**/
int linphone_core_get_upload_bandwidth(const LinphoneCore *lc){
return lc->net_conf.upload_bw;
}
-/**
- * Set audio packetization time linphone expects to receive from peer.
- * A value of zero means that ptime is not specified.
- * @ingroup media_parameters
- */
void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) {
lp_config_set_int(lc->config,"rtp","download_ptime",ptime);
}
-/**
- * Get audio packetization time linphone expects to receive from peer.
- * A value of zero means that ptime is not specified.
- * @ingroup media_parameters
- */
int linphone_core_get_download_ptime(LinphoneCore *lc) {
return lp_config_get_int(lc->config,"rtp","download_ptime",0);
}
-/**
- * Set audio packetization time linphone will send (in absence of requirement from peer)
- * A value of 0 stands for the current codec default packetization time.
- *
- * @ingroup media_parameters
-**/
void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime){
lp_config_set_int(lc->config,"rtp","upload_ptime",ptime);
}
-/**
- * Set audio packetization time linphone will send (in absence of requirement from peer)
- * A value of 0 stands for the current codec default packetization time.
- *
- *
- * @ingroup media_parameters
-**/
int linphone_core_get_upload_ptime(LinphoneCore *lc){
return lp_config_get_int(lc->config,"rtp","upload_ptime",0);
}
-
-
-/**
- * Returns liblinphone's version as a string.
- *
- * @ingroup misc
- *
-**/
const char * linphone_core_get_version(void){
return liblinphone_version;
}
-static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *const_pt, int number, const char *recv_fmtp){
- PayloadType *pt;
- pt=payload_type_clone(const_pt);
- if (number==-1){
- /*look for a free number */
- MSList *elem;
- int i;
- for(i=lc->dyn_pt;ipayload_types;elem!=NULL;elem=elem->next){
- PayloadType *it=(PayloadType*)elem->data;
- if (payload_type_get_number(it)==i){
- already_assigned=TRUE;
- break;
- }
- }
- if (!already_assigned){
- number=i;
- lc->dyn_pt=i+1;
- break;
- }
- }
- if (number==-1){
- ms_fatal("FIXME: too many codecs, no more free numbers.");
- }
+static void linphone_core_register_payload_type(LinphoneCore *lc, const PayloadType *const_pt, const char *recv_fmtp, bool_t enabled){
+ MSList **codec_list=const_pt->type==PAYLOAD_VIDEO ? &lc->default_video_codecs : &lc->default_audio_codecs;
+ if (linphone_core_codec_supported(lc, (const_pt->type == PAYLOAD_VIDEO) ? SalVideo : SalAudio, const_pt->mime_type)){
+ PayloadType *pt=payload_type_clone(const_pt);
+ int number=-1;
+ payload_type_set_enable(pt,enabled);
+ if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp);
+ /*Set a number to the payload type from the statically defined (RFC3551) profile, if not static, -1 is returned
+ and the payload type number will be determined dynamically later, at call time.*/
+ payload_type_set_number(pt,
+ (number=rtp_profile_find_payload_number(&av_profile, pt->mime_type, pt->clock_rate, pt->channels))
+ );
+ ms_message("Codec %s/%i fmtp=[%s] number=%i, enabled=%i) added to default capabilities.", pt->mime_type, pt->clock_rate,
+ pt->recv_fmtp ? pt->recv_fmtp : "", number, (int)payload_type_enabled(pt));
+ *codec_list=ms_list_append(*codec_list,pt);
}
- ms_message("assigning %s/%i payload type number %i",pt->mime_type,pt->clock_rate,number);
- payload_type_set_number(pt,number);
- if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp);
- rtp_profile_set_payload(lc->default_profile,number,pt);
- lc->payload_types=ms_list_append(lc->payload_types,pt);
}
-static void linphone_core_handle_static_payloads(LinphoneCore *lc){
+static void linphone_core_register_static_payloads(LinphoneCore *lc){
RtpProfile *prof=&av_profile;
int i;
for(i=0;itype==PAYLOAD_VIDEO) continue;
+#endif
+ if (find_payload_type_from_list(
+ pt->mime_type, pt->clock_rate, pt->type!=PAYLOAD_VIDEO ? pt->channels : LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS,
+ pt->type==PAYLOAD_VIDEO ? lc->default_video_codecs : lc->default_audio_codecs)==NULL){
+ linphone_core_register_payload_type(lc,pt,NULL,FALSE);
}
}
}
}
static void linphone_core_free_payload_types(LinphoneCore *lc){
- rtp_profile_clear_all(lc->default_profile);
- rtp_profile_destroy(lc->default_profile);
- ms_list_for_each(lc->payload_types,(void (*)(void*))payload_type_destroy);
- ms_list_free(lc->payload_types);
- lc->payload_types=NULL;
+ ms_list_free_with_data(lc->default_audio_codecs, (void (*)(void*))payload_type_destroy);
+ ms_list_free_with_data(lc->default_video_codecs, (void (*)(void*))payload_type_destroy);
}
void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message){
lc->state=gstate;
- if (lc->vtable.global_state_changed){
- lc->vtable.global_state_changed(lc,gstate,message);
- }
+ linphone_core_notify_global_state_changed(lc,gstate,message);
}
-static void misc_config_read (LinphoneCore *lc) {
+
+static void misc_config_read(LinphoneCore *lc) {
LpConfig *config=lc->config;
- lc->max_call_logs=lp_config_get_int(config,"misc","history_max_size",15);
- lc->max_calls=lp_config_get_int(config,"misc","max_calls",NB_MAX_CALLS);
+ const char *uuid;
+
+ lc->max_call_logs=lp_config_get_int(config,"misc","history_max_size",30);
+ lc->max_calls=lp_config_get_int(config,"misc","max_calls",NB_MAX_CALLS);
+
+ uuid=lp_config_get_string(config,"misc","uuid",NULL);
+ if (!uuid){
+ char tmp[64];
+ sal_create_uuid(lc->sal,tmp,sizeof(tmp));
+ lp_config_set_string(config,"misc","uuid",tmp);
+ }else if (strcmp(uuid,"0")!=0) /*to allow to disable sip.instance*/
+ sal_set_uuid(lc->sal, uuid);
+
+ lc->user_certificates_path=ms_strdup(lp_config_get_string(config,"misc","user_certificates_path","."));
}
-
-
-
-static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path,
- const char *factory_config_path, void * userdata)
-{
- ms_message("Initializing LinphoneCore %s", linphone_core_get_version());
- memset (lc, 0, sizeof (LinphoneCore));
- lc->data=userdata;
- lc->ringstream_autorelease=TRUE;
-
- memcpy(&lc->vtable,vtable,sizeof(LinphoneCoreVTable));
-
- linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
- ortp_init();
- lc->dyn_pt=96;
- lc->default_profile=rtp_profile_new("default profile");
- linphone_core_assign_payload_type(lc,&payload_type_pcmu8000,0,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_gsm,3,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_pcma8000,8,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_speex_nb,110,"vbr=on");
- linphone_core_assign_payload_type(lc,&payload_type_speex_wb,111,"vbr=on");
- linphone_core_assign_payload_type(lc,&payload_type_speex_uwb,112,"vbr=on");
- linphone_core_assign_payload_type(lc,&payload_type_telephone_event,101,"0-11");
- linphone_core_assign_payload_type(lc,&payload_type_g722,9,NULL);
-
-#if defined(ANDROID) || defined (__IPHONE_OS_VERSION_MIN_REQUIRED)
- /*shorten the DNS lookup time and send more retransmissions on mobiles:
- - to workaround potential packet losses
- - to avoid hanging for 30 seconds when the network doesn't work despite the phone thinks it does.
- */
- _linphone_core_configure_resolver();
-#endif
-
-#ifdef ENABLE_NONSTANDARD_GSM
- {
- PayloadType *pt;
- pt=payload_type_clone(&payload_type_gsm);
- pt->clock_rate=11025;
- linphone_core_assign_payload_type(lc,pt,-1,NULL);
- pt->clock_rate=22050;
- linphone_core_assign_payload_type(lc,pt,-1,NULL);
- payload_type_destroy(pt);
- }
-#endif
-
-#ifdef VIDEO_ENABLED
- linphone_core_assign_payload_type(lc,&payload_type_h263,34,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_h263_1998,98,"CIF=1;QCIF=1");
- linphone_core_assign_payload_type(lc,&payload_type_mp4v,99,"profile-level-id=3");
- linphone_core_assign_payload_type(lc,&payload_type_h264,102,"profile-level-id=428014");
- linphone_core_assign_payload_type(lc,&payload_type_vp8,103,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL);
- /* due to limited space in SDP, we have to disable this h264 line which is normally no more necessary */
- /* linphone_core_assign_payload_type(&payload_type_h264,-1,"packetization-mode=1;profile-level-id=428014");*/
-#endif
-
- /*add all payload type for which we don't care about the number */
- linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30");
- linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1");
- linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1");
- linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_g726_16,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_g726_24,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_g726_32,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_g726_40,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_16,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_24,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_32,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_40,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_silk_nb,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_silk_mb,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL);
- linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no");
- linphone_core_handle_static_payloads(lc);
-
- ms_init();
- /* create a mediastreamer2 event queue and set it as global */
- /* This allows to run event's callback in linphone_core_iterate() */
- lc->msevq=ms_event_queue_new();
- ms_set_global_event_queue(lc->msevq);
-
- lc->config=lp_config_new(config_path);
- if (factory_config_path)
- lp_config_read_file(lc->config,factory_config_path);
-
- lc->sal=sal_init();
- sal_set_user_pointer(lc->sal,lc);
- sal_set_callbacks(lc->sal,&linphone_sal_callbacks);
-
- lc->network_last_check = 0;
- lc->network_last_status = FALSE;
-
+static void linphone_core_start(LinphoneCore * lc) {
sip_setup_register_all();
sound_config_read(lc);
net_config_read(lc);
rtp_config_read(lc);
codecs_config_read(lc);
- sip_config_read(lc); /* this will start eXosip*/
+ sip_config_read(lc);
video_config_read(lc);
//autoreplier_config_init(&lc->autoreplier_conf);
- lc->presence_mode=LinphoneStatusOnline;
+ lc->presence_model=linphone_presence_model_new_with_activity(LinphonePresenceActivityOnline, NULL);
misc_config_read(lc);
ui_config_read(lc);
#ifdef TUNNEL_ENABLED
- lc->tunnel=linphone_core_tunnel_new(lc);
- if (lc->tunnel) linphone_tunnel_configure(lc->tunnel);
+ if (lc->tunnel) {
+ linphone_tunnel_configure(lc->tunnel);
+ }
#endif
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Ready"));
+
+ linphone_core_notify_display_status(lc,_("Ready"));
lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon;
linphone_core_set_state(lc,LinphoneGlobalOn,"Ready");
}
-/**
- * Instanciates a LinphoneCore object.
- * @ingroup initializing
- *
- * The LinphoneCore object is the primary handle for doing all phone actions.
- * It should be unique within your application.
- * @param vtable a LinphoneCoreVTable structure holding your application callbacks
- * @param config_path a path to a config file. If it does not exists it will be created.
- * The config file is used to store all settings, call logs, friends, proxies... so that all these settings
- * become persistent over the life of the LinphoneCore object.
- * It is allowed to set a NULL config file. In that case LinphoneCore will not store any settings.
- * @param factory_config_path a path to a read-only config file that can be used to
- * to store hard-coded preference such as proxy settings or internal preferences.
- * The settings in this factory file always override the one in the normal config file.
- * It is OPTIONAL, use NULL if unneeded.
- * @param userdata an opaque user pointer that can be retrieved at any time (for example in
- * callbacks) using linphone_core_get_user_data().
- *
-**/
+void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState state, const char *message) {
+ linphone_core_notify_configuring_status(lc, state, message);
+
+ if (state == LinphoneConfiguringSuccessful) {
+ if (linphone_core_is_provisioning_transient(lc) == TRUE)
+ linphone_core_set_provisioning_uri(lc, NULL);
+ }
+
+ linphone_core_start(lc);
+}
+
+
+static int linphone_core_serialization_ref = 0;
+
+static void linphone_core_activate_log_serialization_if_needed(void) {
+ if (liblinphone_serialize_logs == TRUE) {
+ linphone_core_serialization_ref++;
+ if (linphone_core_serialization_ref == 1)
+ ortp_set_log_thread_id(ortp_thread_self());
+ }
+}
+
+static void linphone_core_deactivate_log_serialization_if_needed(void) {
+ if (liblinphone_serialize_logs == TRUE) {
+ --linphone_core_serialization_ref;
+ if (linphone_core_serialization_ref == 0)
+ ortp_set_log_thread_id(0);
+ }
+}
+
+static void linphone_core_register_default_codecs(LinphoneCore *lc){
+ const char *aac_fmtp162248, *aac_fmtp3244;
+ bool_t opus_enabled=TRUE;
+ /*default enabled audio codecs, in order of preference*/
+#if defined(__arm__) || defined(_M_ARM)
+ /*hack for opus, that needs to be disabed by default on ARM single processor, otherwise there is no cpu left for video processing*/
+ if (ms_get_cpu_count()==1) opus_enabled=FALSE;
+#endif
+ linphone_core_register_payload_type(lc,&payload_type_opus,"useinbandfec=1",opus_enabled);
+ linphone_core_register_payload_type(lc,&payload_type_silk_wb,NULL,TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_speex_wb,"vbr=on",TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_speex_nb,"vbr=on",TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_pcmu8000,NULL,TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_pcma8000,NULL,TRUE);
+
+ /*other audio codecs, not enabled by default, in order of preference*/
+ linphone_core_register_payload_type(lc,&payload_type_gsm,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g722,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_ilbc,"mode=30",FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_amr,"octet-align=1",FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_amrwb,"octet-align=1",FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g729,"annexb=no",FALSE);
+ /* For AAC, we use a config value to determine if we ought to support SBR. Since it is not offically supported
+ * for the mpeg4-generic mime type, setting this flag to 1 will break compatibility with other clients. */
+ if( lp_config_get_int(lc->config, "misc", "aac_use_sbr", FALSE) ) {
+ ms_message("Using SBR for AAC");
+ aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1";
+ aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1";
+ } else {
+ aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5";
+ aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5";
+ }
+ linphone_core_register_payload_type(lc,&payload_type_aaceld_16k,aac_fmtp162248,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aaceld_22k,aac_fmtp162248,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aaceld_32k,aac_fmtp3244,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aaceld_44k,aac_fmtp3244,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aaceld_48k,aac_fmtp162248,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_isac,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_speex_uwb,"vbr=on",FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_silk_nb,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_silk_mb,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_silk_swb,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g726_16,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g726_24,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g726_32,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_g726_40,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aal2_g726_16,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aal2_g726_24,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aal2_g726_32,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_aal2_g726_40,NULL,FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_codec2,NULL,FALSE);
+
+
+
+
+#ifdef VIDEO_ENABLED
+ /*default enabled video codecs, in order of preference*/
+ linphone_core_register_payload_type(lc,&payload_type_vp8,NULL,TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_h264,"profile-level-id=42801F",TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_mp4v,"profile-level-id=3",TRUE);
+ linphone_core_register_payload_type(lc,&payload_type_h263_1998,"CIF=1;QCIF=1",FALSE);
+ linphone_core_register_payload_type(lc,&payload_type_h263,NULL,FALSE);
+#endif
+ /*register all static payload types declared in av_profile of oRTP, if not already declared above*/
+ linphone_core_register_static_payloads(lc);
+}
+
+static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata){
+ const char *remote_provisioning_uri = NULL;
+ LinphoneCoreVTable* local_vtable= linphone_core_v_table_new();
+ ms_message("Initializing LinphoneCore %s", linphone_core_get_version());
+
+ lc->config=lp_config_ref(config);
+ lc->data=userdata;
+ lc->ringstream_autorelease=TRUE;
+ linphone_task_list_init(&lc->hooks);
+
+ memcpy(local_vtable,vtable,sizeof(LinphoneCoreVTable));
+ _linphone_core_add_listener(lc, local_vtable, TRUE);
+
+ linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
+ ortp_init();
+ linphone_core_activate_log_serialization_if_needed();
+
+ ms_init();
+
+ linphone_core_register_default_codecs(lc);
+ /* Get the mediastreamer2 event queue */
+ /* This allows to run event's callback in linphone_core_iterate() */
+ lc->msevq=ms_factory_get_event_queue(ms_factory_get_fallback());
+
+ lc->sal=sal_init();
+
+ sal_set_user_pointer(lc->sal,lc);
+ sal_set_callbacks(lc->sal,&linphone_sal_callbacks);
+
+#ifdef TUNNEL_ENABLED
+ lc->tunnel=linphone_core_tunnel_new(lc);
+#endif
+
+ lc->network_last_check = 0;
+ lc->network_last_status = FALSE;
+
+ lc->http_provider = belle_sip_stack_create_http_provider(sal_get_belle_sip_stack(lc->sal), "0.0.0.0");
+ lc->http_verify_policy = belle_tls_verify_policy_new();
+ belle_http_provider_set_tls_verify_policy(lc->http_provider,lc->http_verify_policy);
+
+ certificates_config_read(lc);
+
+ remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
+ if (remote_provisioning_uri == NULL) {
+ linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
+ } // else linphone_core_start will be called after the remote provisioning (see linphone_core_iterate)
+}
+
LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable,
const char *config_path, const char *factory_config_path, void * userdata)
{
- LinphoneCore *core=ms_new(LinphoneCore,1);
- linphone_core_init(core,vtable,config_path, factory_config_path, userdata);
+ LinphoneCore *lc;
+ LpConfig *config = lp_config_new_with_factory(config_path, factory_config_path);
+ lc=linphone_core_new_with_config(vtable, config, userdata);
+ lp_config_unref(config);
+ return lc;
+}
+
+LinphoneCore *linphone_core_new_with_config(const LinphoneCoreVTable *vtable, struct _LpConfig *config, void *userdata)
+{
+ LinphoneCore *core = ms_new0(LinphoneCore, 1);
+ linphone_core_init(core, vtable, config, userdata);
return core;
}
-/**
- * Returns the list of available audio codecs.
- *
- * This list is unmodifiable. The ->data field of the MSList points a PayloadType
- * structure holding the codec information.
- * It is possible to make copy of the list with ms_list_copy() in order to modify it
- * (such as the order of codecs).
- * @ingroup media_parameters
-**/
const MSList *linphone_core_get_audio_codecs(const LinphoneCore *lc)
{
return lc->codecs_conf.audio_codecs;
}
-/**
- * Returns the list of available video codecs.
- *
- * This list is unmodifiable. The ->data field of the MSList points a PayloadType
- * structure holding the codec information.
- * It is possible to make copy of the list with ms_list_copy() in order to modify it
- * (such as the order of codecs).
- * @ingroup media_parameters
-**/
const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc)
{
return lc->codecs_conf.video_codecs;
}
-/**
- * Sets the local "from" identity.
- *
- * @ingroup proxies
- * This data is used in absence of any proxy configuration or when no
- * default proxy configuration is set. See LinphoneProxyConfig
-**/
int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact)
{
LinphoneAddress *ctt;
+ if( lc->sip_conf.contact != NULL && strcmp(lc->sip_conf.contact, contact) == 0){
+ /* changing for the same contact: no need to do anything */
+ return 0;
+ }
+
if ((ctt=linphone_address_new(contact))==0) {
ms_error("Bad contact url: %s",contact);
return -1;
}
+
if (lc->sip_conf.contact!=NULL) ms_free(lc->sip_conf.contact);
lc->sip_conf.contact=ms_strdup(contact);
+ lp_config_set_string(lc->config, "sip", "contact", lc->sip_conf.contact);
+
+ /* clean the guessed contact, we have to regenerate it */
if (lc->sip_conf.guessed_contact!=NULL){
ms_free(lc->sip_conf.guessed_contact);
lc->sip_conf.guessed_contact=NULL;
@@ -1389,28 +1684,6 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact)
}
-/*result must be an array of chars at least LINPHONE_IPADDR_SIZE */
-void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){
- const char *ip;
- if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress
- && (ip=linphone_core_get_nat_address_resolved(lc))!=NULL){
- strncpy(result,ip,LINPHONE_IPADDR_SIZE);
- return;
- }
-#ifdef BUILD_UPNP
- else if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp &&
- linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) {
- ip = linphone_upnp_context_get_external_ipaddress(lc->upnp);
- strncpy(result,ip,LINPHONE_IPADDR_SIZE);
- return;
- }
-#endif //BUILD_UPNP
- if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0)
- return;
- /*else fallback to SAL routine that will attempt to find the most realistic interface */
- sal_get_default_local_ip(lc->sal,lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE);
-}
-
static void update_primary_contact(LinphoneCore *lc){
char *guessed=NULL;
char tmp[LINPHONE_IPADDR_SIZE];
@@ -1425,23 +1698,18 @@ static void update_primary_contact(LinphoneCore *lc){
ms_error("Could not parse identity contact !");
url=linphone_address_new("sip:unknown@unkwownhost");
}
- linphone_core_get_local_ip(lc, NULL, tmp);
+ linphone_core_get_local_ip(lc, AF_UNSPEC, NULL, tmp);
if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){
ms_warning("Local loopback network only !");
lc->sip_conf.loopback_only=TRUE;
}else lc->sip_conf.loopback_only=FALSE;
linphone_address_set_domain(url,tmp);
- linphone_address_set_port_int(url,linphone_core_get_sip_port (lc));
+ linphone_address_set_port(url,linphone_core_get_sip_port(lc));
guessed=linphone_address_as_string(url);
lc->sip_conf.guessed_contact=guessed;
linphone_address_destroy(url);
}
-/**
- * Returns the default identity when no proxy configuration is used.
- *
- * @ingroup proxies
-**/
const char *linphone_core_get_primary_contact(LinphoneCore *lc){
char *identity;
@@ -1456,64 +1724,86 @@ const char *linphone_core_get_primary_contact(LinphoneCore *lc){
return identity;
}
-/**
- * Tells LinphoneCore to guess local hostname automatically in primary contact.
- *
- * @ingroup proxies
-**/
void linphone_core_set_guess_hostname(LinphoneCore *lc, bool_t val){
lc->sip_conf.guess_hostname=val;
}
-/**
- * Returns TRUE if hostname part of primary contact is guessed automatically.
- *
- * @ingroup proxies
-**/
bool_t linphone_core_get_guess_hostname(LinphoneCore *lc){
return lc->sip_conf.guess_hostname;
}
-/**
- * Same as linphone_core_get_primary_contact() but the result is a LinphoneAddress object
- * instead of const char*
- *
- * @ingroup proxies
-**/
+void linphone_core_enable_lime(LinphoneCore *lc, bool_t val){
+ if (linphone_core_ready(lc)){
+ lp_config_set_int(lc->config,"sip","lime",val);
+ }
+}
+
+bool_t linphone_core_lime_enabled(const LinphoneCore *lc){
+ return (lp_config_get_int(lc->config,"sip", "lime", FALSE) && lime_is_available());
+}
+
+bool_t linphone_core_lime_for_file_sharing_enabled(const LinphoneCore *lc){
+ return linphone_core_lime_enabled(lc) && (lp_config_get_int(lc->config,"sip", "lime_for_file_sharing", TRUE) && lime_is_available());
+}
+
LinphoneAddress *linphone_core_get_primary_contact_parsed(LinphoneCore *lc){
return linphone_address_new(linphone_core_get_primary_contact(lc));
}
/**
* Sets the list of audio codecs.
+ * @param[in] lc The LinphoneCore object
+ * @param[in] codecs \mslist{PayloadType}
+ * @return 0
*
* @ingroup media_parameters
* The list is taken by the LinphoneCore thus the application should not free it.
* This list is made of struct PayloadType describing the codec parameters.
**/
-int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs)
-{
+int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs){
if (lc->codecs_conf.audio_codecs!=NULL) ms_list_free(lc->codecs_conf.audio_codecs);
lc->codecs_conf.audio_codecs=codecs;
_linphone_core_codec_config_write(lc);
+ linphone_core_update_allocated_audio_bandwidth(lc);
return 0;
}
/**
* Sets the list of video codecs.
+ * @param[in] lc The LinphoneCore object
+ * @param[in] codecs \mslist{PayloadType}
+ * @return 0
*
* @ingroup media_parameters
* The list is taken by the LinphoneCore thus the application should not free it.
* This list is made of struct PayloadType describing the codec parameters.
**/
-int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs)
-{
+int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs){
if (lc->codecs_conf.video_codecs!=NULL) ms_list_free(lc->codecs_conf.video_codecs);
lc->codecs_conf.video_codecs=codecs;
_linphone_core_codec_config_write(lc);
return 0;
}
+/**
+ * Enable RFC3389 generic confort noise algorithm (CN payload type).
+ * It is disabled by default, because this algorithm is only relevant for legacy codecs (PCMU, PCMA, G722).
+ * @param lc the LinphoneCore
+ * @param enabled TRUE if enabled, FALSE otherwise.
+**/
+void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled){
+ lp_config_set_int(lc->config, "misc", "use_cn", enabled);
+}
+
+/**
+ * Returns enablement of RFC3389 generic confort noise algorithm.
+ * @param lc the LinphoneCore
+ * @return TRUE or FALSE.
+**/
+bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc){
+ return lp_config_get_int(lc->config, "misc", "use_cn", FALSE);
+}
+
const MSList * linphone_core_get_friend_list(const LinphoneCore *lc)
{
return lc->friends;
@@ -1618,24 +1908,39 @@ bool_t linphone_core_get_rtp_no_xmit_on_audio_mute(const LinphoneCore *lc){
return lc->rtp_conf.rtp_no_xmit_on_audio_mute;
}
-/**
- * Sets the nominal audio jitter buffer size in milliseconds.
- *
- * @ingroup media_parameters
-**/
-void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int value)
-{
- lc->rtp_conf.audio_jitt_comp=value;
+static void apply_jitter_value(LinphoneCore *lc, int value, MSFormatType stype){
+ LinphoneCall *call;
+ MSList *it;
+ for (it=lc->calls;it!=NULL;it=it->next){
+ MediaStream *ms;
+ call=(LinphoneCall*)it->data;
+ ms = stype==MSAudio ? (MediaStream*)call->audiostream : (MediaStream*)call->videostream;
+ if (ms){
+ RtpSession *s=ms->sessions.rtp_session;
+ if (s){
+ if (value>0){
+ ms_message("Jitter buffer size set to [%i] ms on call [%p]",value,call);
+ rtp_session_set_jitter_compensation(s,value);
+ rtp_session_enable_jitter_buffer(s,TRUE);
+ }else if (value==0){
+ ms_warning("Jitter buffer is disabled per application request on call [%p]",call);
+ rtp_session_enable_jitter_buffer(s,FALSE);
+ }
+ }
+ }
+ }
}
-/**
- * Sets the nominal video jitter buffer size in milliseconds.
- *
- * @ingroup media_parameters
-**/
-void linphone_core_set_video_jittcomp(LinphoneCore *lc, int value)
+void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int milliseconds)
{
- lc->rtp_conf.video_jitt_comp=value;
+ lc->rtp_conf.audio_jitt_comp=milliseconds;
+ apply_jitter_value(lc, milliseconds, MSAudio);
+}
+
+void linphone_core_set_video_jittcomp(LinphoneCore *lc, int milliseconds)
+{
+ lc->rtp_conf.video_jitt_comp=milliseconds;
+ apply_jitter_value(lc, milliseconds, MSVideo);
}
void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc,bool_t rtp_no_xmit_on_audio_mute){
@@ -1644,6 +1949,8 @@ void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc,bool_t rtp_no_
/**
* Sets the UDP port used for audio streaming.
+ * A value if -1 will request the system to allocate the local port randomly.
+ * This is recommended in order to avoid firewall warnings.
*
* @ingroup network_parameters
**/
@@ -1664,6 +1971,8 @@ void linphone_core_set_audio_port_range(LinphoneCore *lc, int min_port, int max_
/**
* Sets the UDP port used for video streaming.
+ * A value if -1 will request the system to allocate the local port randomly.
+ * This is recommended in order to avoid firewall warnings.
*
* @ingroup network_parameters
**/
@@ -1698,7 +2007,7 @@ void linphone_core_set_nortp_timeout(LinphoneCore *lc, int nortp_timeout){
**/
bool_t linphone_core_get_use_info_for_dtmf(LinphoneCore *lc)
{
- return lc->sip_conf.use_info;
+ return lp_config_get_int(lc->config, "sip", "use_info", 0);
}
/**
@@ -1708,7 +2017,9 @@ bool_t linphone_core_get_use_info_for_dtmf(LinphoneCore *lc)
**/
void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc,bool_t use_info)
{
- lc->sip_conf.use_info=use_info;
+ if (linphone_core_ready(lc)) {
+ lp_config_set_int(lc->config, "sip", "use_info", use_info);
+ }
}
/**
@@ -1718,7 +2029,7 @@ void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc,bool_t use_info)
**/
bool_t linphone_core_get_use_rfc2833_for_dtmf(LinphoneCore *lc)
{
- return lc->sip_conf.use_rfc2833;
+ return lp_config_get_int(lc->config, "sip", "use_rfc2833", 1);
}
/**
@@ -1728,39 +2039,25 @@ bool_t linphone_core_get_use_rfc2833_for_dtmf(LinphoneCore *lc)
**/
void linphone_core_set_use_rfc2833_for_dtmf(LinphoneCore *lc,bool_t use_rfc2833)
{
- lc->sip_conf.use_rfc2833=use_rfc2833;
+ if (linphone_core_ready(lc)) {
+ lp_config_set_int(lc->config, "sip", "use_rfc2833", use_rfc2833);
+ }
}
/**
* Returns the UDP port used by SIP.
*
- * Deprecated: use linphone_core_get_sip_transports() instead.
+ * @deprecated use linphone_core_get_sip_transports() instead.
* @ingroup network_parameters
**/
-int linphone_core_get_sip_port(LinphoneCore *lc)
-{
- LCSipTransports *tr=&lc->sip_conf.transports;
- return tr->udp_port>0 ? tr->udp_port : (tr->tcp_port > 0 ? tr->tcp_port : tr->tls_port);
+int linphone_core_get_sip_port(LinphoneCore *lc){
+ LCSipTransports tr;
+ linphone_core_get_sip_transports_used(lc,&tr);
+ return tr.udp_port>0 ? tr.udp_port : (tr.tcp_port > 0 ? tr.tcp_port : tr.tls_port);
}
static char _ua_name[64]="Linphone";
-static char _ua_version[64]=LINPHONE_VERSION;
-
-#ifdef HAVE_EXOSIP_GET_VERSION
-extern const char *eXosip_get_version();
-#endif
-
-static void apply_user_agent(LinphoneCore *lc){
- char ua_string[256];
- snprintf(ua_string,sizeof(ua_string)-1,"%s/%s (eXosip2/%s)",_ua_name,_ua_version,
-#ifdef HAVE_EXOSIP_GET_VERSION
- eXosip_get_version()
-#else
- "unknown"
-#endif
- );
- if (lc->sal) sal_set_user_agent(lc->sal,ua_string);
-}
+static char _ua_version[64]=LIBLINPHONE_VERSION;
/**
* Sets the user agent string used in SIP messages.
@@ -1768,9 +2065,15 @@ static void apply_user_agent(LinphoneCore *lc){
* @ingroup misc
**/
void linphone_core_set_user_agent(LinphoneCore *lc, const char *name, const char *ver){
- strncpy(_ua_name,name,sizeof(_ua_name)-1);
- strncpy(_ua_version,ver,sizeof(_ua_version));
- apply_user_agent(lc);
+ char ua_string[256];
+ snprintf(ua_string, sizeof(ua_string) - 1, "%s/%s", name?name:"", ver?ver:"");
+ if (lc->sal) {
+ sal_set_user_agent(lc->sal, ua_string);
+ sal_append_stack_string_to_user_agent(lc->sal);
+ }
+}
+const char *linphone_core_get_user_agent(LinphoneCore *lc){
+ return sal_get_user_agent(lc->sal);
}
const char *linphone_core_get_user_agent_name(void){
@@ -1784,8 +2087,7 @@ const char *linphone_core_get_user_agent_version(void){
static void transport_error(LinphoneCore *lc, const char* transport, int port){
char *msg=ortp_strdup_printf("Could not start %s transport on port %i, maybe this port is already used.",transport,port);
ms_warning("%s",msg);
- if (lc->vtable.display_warning)
- lc->vtable.display_warning(lc,msg);
+ linphone_core_notify_display_warning(lc,msg);
ms_free(msg);
}
@@ -1797,68 +2099,102 @@ static bool_t transports_unchanged(const LCSipTransports * tr1, const LCSipTrans
tr2->tls_port==tr1->tls_port;
}
-static int apply_transports(LinphoneCore *lc){
+int _linphone_core_apply_transports(LinphoneCore *lc){
Sal *sal=lc->sal;
const char *anyaddr;
LCSipTransports *tr=&lc->sip_conf.transports;
-
+ const char* listening_address;
/*first of all invalidate all current registrations so that we can register again with new transports*/
__linphone_core_invalidate_registers(lc);
-
+
if (lc->sip_conf.ipv6_enabled)
anyaddr="::0";
else
anyaddr="0.0.0.0";
sal_unlisten_ports(sal);
- if (tr->udp_port>0){
- if (sal_listen_port (sal,anyaddr,tr->udp_port,SalTransportUDP,FALSE)!=0){
- transport_error(lc,"udp",tr->udp_port);
- return -1;
+
+ listening_address = lp_config_get_string(lc->config,"sip","bind_address",anyaddr);
+
+ if (lc->tunnel && linphone_tunnel_sip_enabled(lc->tunnel) && linphone_tunnel_get_activated(lc->tunnel)){
+ if (sal_listen_port(sal,anyaddr,tr->udp_port,SalTransportUDP,TRUE)!=0){
+ transport_error(lc,"udp+tunnel",tr->udp_port);
+ }
+ }else{
+ if (tr->udp_port!=0){
+ if (sal_listen_port(sal,listening_address,tr->udp_port,SalTransportUDP,FALSE)!=0){
+ transport_error(lc,"udp",tr->udp_port);
+ }
+ }
+ if (tr->tcp_port!=0){
+ if (sal_listen_port (sal,listening_address,tr->tcp_port,SalTransportTCP,FALSE)!=0){
+ transport_error(lc,"tcp",tr->tcp_port);
+ }
+ }
+ if (linphone_core_sip_transport_supported(lc,LinphoneTransportTls)){
+ if (tr->tls_port!=0){
+ if (sal_listen_port (sal,listening_address,tr->tls_port,SalTransportTLS,FALSE)!=0){
+ transport_error(lc,"tls",tr->tls_port);
+ }
+ }
}
}
- if (tr->tcp_port>0){
- if (sal_listen_port (sal,anyaddr,tr->tcp_port,SalTransportTCP,FALSE)!=0){
- transport_error(lc,"tcp",tr->tcp_port);
- }
- }
- if (tr->tls_port>0){
- if (sal_listen_port (sal,anyaddr,tr->tls_port,SalTransportTLS,TRUE)!=0){
- transport_error(lc,"tls",tr->tls_port);
- }
- }
- apply_user_agent(lc);
return 0;
}
+/**
+ * Returns TRUE if given transport type is supported by the library, FALSE otherwise.
+**/
+bool_t linphone_core_sip_transport_supported(const LinphoneCore *lc, LinphoneTransportType tp){
+ return sal_transport_available(lc->sal,(SalTransport)tp);
+}
+
/**
* Sets the ports to be used for each of transport (UDP or TCP)
*
* A zero value port for a given transport means the transport
- * is not used.
+ * is not used. A value of LC_SIP_TRANSPORT_RANDOM (-1) means the port is to be choosen randomly by the system.
*
* @ingroup network_parameters
**/
-int linphone_core_set_sip_transports(LinphoneCore *lc, const LCSipTransports * tr){
+int linphone_core_set_sip_transports(LinphoneCore *lc, const LCSipTransports * tr_config /*config to be saved*/){
+ LCSipTransports tr=*tr_config;
- if (transports_unchanged(tr,&lc->sip_conf.transports))
+ if (lp_config_get_int(lc->config,"sip","sip_random_port",0)==1) {
+ /*legacy random mode*/
+ if (tr.udp_port>0){
+ tr.udp_port=LC_SIP_TRANSPORT_RANDOM;
+ }
+ if (tr.tcp_port>0){
+ tr.tcp_port=LC_SIP_TRANSPORT_RANDOM;
+ }
+ if (tr.tls_port>0){
+ tr.tls_port=LC_SIP_TRANSPORT_RANDOM;
+ }
+ }
+
+ if (tr.udp_port==0 && tr.tcp_port==0 && tr.tls_port==0){
+ tr.udp_port=5060;
+ }
+
+ if (transports_unchanged(&tr,&lc->sip_conf.transports))
return 0;
- memcpy(&lc->sip_conf.transports,tr,sizeof(*tr));
+ memcpy(&lc->sip_conf.transports,&tr,sizeof(tr));
if (linphone_core_ready(lc)){
- lp_config_set_int(lc->config,"sip","sip_port",tr->udp_port);
- lp_config_set_int(lc->config,"sip","sip_tcp_port",tr->tcp_port);
- lp_config_set_int(lc->config,"sip","sip_tls_port",tr->tls_port);
+ lp_config_set_int(lc->config,"sip","sip_port",tr_config->udp_port);
+ lp_config_set_int(lc->config,"sip","sip_tcp_port",tr_config->tcp_port);
+ lp_config_set_int(lc->config,"sip","sip_tls_port",tr_config->tls_port);
}
if (lc->sal==NULL) return 0;
- return apply_transports(lc);
+ return _linphone_core_apply_transports(lc);
}
/**
- * Retrieves the ports used for each transport (udp, tcp).
+ * Retrieves the port configuration used for each transport (udp, tcp, tls).
* A zero value port for a given transport means the transport
- * is not used.
+ * is not used. A value of LC_SIP_TRANSPORT_RANDOM (-1) means the port is to be chosen randomly by the system.
* @ingroup network_parameters
**/
int linphone_core_get_sip_transports(LinphoneCore *lc, LCSipTransports *tr){
@@ -1866,10 +2202,23 @@ int linphone_core_get_sip_transports(LinphoneCore *lc, LCSipTransports *tr){
return 0;
}
+/**
+ * Retrieves the real port number assigned for each sip transport (udp, tcp, tls).
+ * A zero value means that the transport is not activated.
+ * If LC_SIP_TRANSPORT_RANDOM was passed to linphone_core_set_sip_transports(), the random port choosed by the system is returned.
+ * @ingroup network_parameters
+ * @param lc the LinphoneCore
+ * @param tr a LCSipTransports structure.
+**/
+void linphone_core_get_sip_transports_used(LinphoneCore *lc, LCSipTransports *tr){
+ tr->udp_port=sal_get_listening_port(lc->sal,SalTransportUDP);
+ tr->tcp_port=sal_get_listening_port(lc->sal,SalTransportTCP);
+ tr->tls_port=sal_get_listening_port(lc->sal,SalTransportTLS);
+}
/**
* Sets the UDP port to be used by SIP.
*
- * Deprecated: use linphone_core_set_sip_transports() instead.
+ * @deprecated use linphone_core_set_sip_transports() instead.
* @ingroup network_parameters
**/
void linphone_core_set_sip_port(LinphoneCore *lc,int port)
@@ -1903,31 +2252,45 @@ void linphone_core_enable_ipv6(LinphoneCore *lc, bool_t val){
if (lc->sip_conf.ipv6_enabled!=val){
lc->sip_conf.ipv6_enabled=val;
if (lc->sal){
- /* we need to restart eXosip */
- apply_transports(lc);
+ /* we need to update the sip stack */
+ _linphone_core_apply_transports(lc);
+ }
+ /*update the localip immediately for the network monitor to avoid to "discover" later that we switched to ipv6*/
+ linphone_core_get_local_ip(lc,AF_UNSPEC,NULL,lc->localip);
+ if (linphone_core_ready(lc)){
+ lp_config_set_int(lc->config,"sip","use_ipv6",(int)val);
}
}
}
static void monitor_network_state(LinphoneCore *lc, time_t curtime){
- char result[LINPHONE_IPADDR_SIZE];
bool_t new_status=lc->network_last_status;
+ char newip[LINPHONE_IPADDR_SIZE];
/* only do the network up checking every five seconds */
if (lc->network_last_check==0 || (curtime-lc->network_last_check)>=5){
- linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,NULL,result);
- if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){
+ linphone_core_get_local_ip(lc,AF_UNSPEC,NULL,newip);
+ if (strcmp(newip,"::1")!=0 && strcmp(newip,"127.0.0.1")!=0){
new_status=TRUE;
- }else new_status=FALSE;
- lc->network_last_check=curtime;
+ }else new_status=FALSE; /*no network*/
+
+ if (new_status==lc->network_last_status && new_status==TRUE && strcmp(newip,lc->localip)!=0){
+ /*IP address change detected*/
+ ms_message("IP address change detected.");
+ set_network_reachable(lc,FALSE,curtime);
+ lc->network_last_status=FALSE;
+ }
+ strncpy(lc->localip,newip,sizeof(lc->localip));
+
if (new_status!=lc->network_last_status) {
if (new_status){
- ms_message("New local ip address is %s",result);
+ ms_message("New local ip address is %s",lc->localip);
}
set_network_reachable(lc,new_status, curtime);
lc->network_last_status=new_status;
}
+ lc->network_last_check=curtime;
}
}
@@ -1937,10 +2300,11 @@ static void proxy_update(LinphoneCore *lc){
for(elem=lc->sip_conf.deleted_proxies;elem!=NULL;elem=next){
LinphoneProxyConfig* cfg = (LinphoneProxyConfig*)elem->data;
next=elem->next;
- if (ms_time(NULL) - cfg->deletion_date > 5) {
+ if (ms_time(NULL) - cfg->deletion_date > 32) {
lc->sip_conf.deleted_proxies =ms_list_remove_link(lc->sip_conf.deleted_proxies,elem);
- ms_message("clearing proxy config for [%s]",linphone_proxy_config_get_addr(cfg));
- linphone_proxy_config_destroy(cfg);
+ ms_message("Proxy config for [%s] is definitely removed from core.",linphone_proxy_config_get_addr(cfg));
+ _linphone_proxy_config_release_ops(cfg);
+ linphone_proxy_config_unref(cfg);
}
}
}
@@ -1950,8 +2314,7 @@ static void assign_buddy_info(LinphoneCore *lc, BuddyInfo *info){
if (lf!=NULL){
lf->info=info;
ms_message("%s has a BuddyInfo assigned with image %p",info->sip_uri, info->image_data);
- if (lc->vtable.buddy_info_updated)
- lc->vtable.buddy_info_updated(lc,lf);
+ linphone_core_notify_buddy_info_updated(lc,lf);
}else{
ms_warning("Could not any friend with uri %s",info->sip_uri);
}
@@ -2036,6 +2399,29 @@ void linphone_core_iterate(LinphoneCore *lc){
time_t curtime=time(NULL);
int elapsed;
bool_t one_second_elapsed=FALSE;
+ const char *remote_provisioning_uri = NULL;
+ if (lc->network_reachable_to_be_notified) {
+ lc->network_reachable_to_be_notified=FALSE;
+ linphone_core_notify_network_reachable(lc,lc->network_reachable);
+ }
+ if (linphone_core_get_global_state(lc) == LinphoneGlobalStartup) {
+ if (sal_get_root_ca(lc->sal)) {
+ belle_tls_verify_policy_t *tls_policy = belle_tls_verify_policy_new();
+ belle_tls_verify_policy_set_root_ca(tls_policy, sal_get_root_ca(lc->sal));
+ belle_http_provider_set_tls_verify_policy(lc->http_provider, tls_policy);
+ }
+
+ linphone_core_notify_display_status(lc, _("Configuring"));
+ linphone_core_set_state(lc, LinphoneGlobalConfiguring, "Configuring");
+
+ remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
+ if (remote_provisioning_uri) {
+ int err = linphone_remote_provisioning_download_and_apply(lc, remote_provisioning_uri);
+ if (err == -1) {
+ linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "Bad URI");
+ }
+ } // else linphone_configuring_terminated has already been called in linphone_core_init
+ }
if (curtime-lc->prevtime>=1){
lc->prevtime=curtime;
@@ -2050,7 +2436,7 @@ void linphone_core_iterate(LinphoneCore *lc){
if (ecs==LinphoneEcCalibratorDone){
int len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
int margin=len/2;
-
+
lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-margin,0));
} else if (ecs == LinphoneEcCalibratorFailed) {
lp_config_set_int(lc->config, "sound", "ec_delay", -1);/*use default value from soundcard*/
@@ -2070,10 +2456,16 @@ void linphone_core_iterate(LinphoneCore *lc){
}
if (lc->ringstream && lc->ringstream_autorelease && lc->dmfs_playing_start_time!=0
- && (curtime-lc->dmfs_playing_start_time)>5){
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- lc->dmfs_playing_start_time=0;
+ && (curtime-lc->dmfs_playing_start_time)>5){
+ MSPlayerState state;
+ bool_t stop=TRUE;
+ if (lc->ringstream->source && ms_filter_call_method(lc->ringstream->source,MS_PLAYER_GET_STATE,&state)==0){
+ if (state==MSPlayerPlaying) stop=FALSE;
+ }
+ if (stop) {
+ ms_message("Releasing inactive tone player.");
+ linphone_core_stop_dtmf_stream(lc);
+ }
}
sal_iterate(lc->sal);
@@ -2086,7 +2478,7 @@ void linphone_core_iterate(LinphoneCore *lc){
calls= lc->calls;
while(calls!= NULL){
call = (LinphoneCall *)calls->data;
- elapsed = curtime-call->start_time;
+ elapsed = curtime-call->log->start_date_time;
/* get immediately a reference to next one in case the one
we are going to examine is destroy and removed during
linphone_core_start_invite() */
@@ -2106,23 +2498,28 @@ void linphone_core_iterate(LinphoneCore *lc){
linphone_call_delete_upnp_session(call);
}
#endif //BUILD_UPNP
- linphone_core_start_invite(lc,call);
+ linphone_core_start_invite(lc,call, NULL);
}
- if (call->state==LinphoneCallIncomingReceived){
- ms_message("incoming call ringing for %i seconds",elapsed);
+ if (call->state==LinphoneCallIncomingReceived || call->state==LinphoneCallIncomingEarlyMedia){
+ if (one_second_elapsed) ms_message("incoming call ringing for %i seconds",elapsed);
if (elapsed>lc->sip_conf.inc_timeout){
+ LinphoneReason decline_reason;
ms_message("incoming call timeout (%i)",lc->sip_conf.inc_timeout);
+ decline_reason=lc->current_call ? LinphoneReasonBusy : LinphoneReasonDeclined;
call->log->status=LinphoneCallMissed;
- call->reason=LinphoneReasonNotAnswered;
- linphone_core_terminate_call(lc,call);
+ sal_error_info_set(&call->non_op_error,SalReasonRequestTimeout,408,"Not answered",NULL);
+ linphone_core_decline_call(lc,call,decline_reason);
}
}
- if (lc->sip_conf.in_call_timeout > 0 && elapsed>lc->sip_conf.in_call_timeout) {
+ if ( (lc->sip_conf.in_call_timeout > 0)
+ && (call->log->connected_date_time != 0)
+ && ((curtime - call->log->connected_date_time) > lc->sip_conf.in_call_timeout))
+ {
ms_message("in call timeout (%i)",lc->sip_conf.in_call_timeout);
linphone_core_terminate_call(lc,call);
}
}
-
+
if (linphone_core_video_preview_enabled(lc)){
if (lc->previewstream==NULL && lc->calls==NULL)
toggle_video_preview(lc,TRUE);
@@ -2137,92 +2534,25 @@ void linphone_core_iterate(LinphoneCore *lc){
linphone_core_run_hooks(lc);
linphone_core_do_plugin_tasks(lc);
- if (lc->initial_subscribes_sent==FALSE && lc->netup_time!=0 &&
- (curtime-lc->netup_time)>3){
+ if (lc->network_reachable && lc->netup_time!=0 && (curtime-lc->netup_time)>3){
+ /*not do that immediately, take your time.*/
linphone_core_send_initial_subscribes(lc);
- lc->initial_subscribes_sent=TRUE;
}
- if (one_second_elapsed && lp_config_needs_commit(lc->config)){
- lp_config_sync(lc->config);
+ if (one_second_elapsed) {
+ if (lp_config_needs_commit(lc->config)) {
+ lp_config_sync(lc->config);
+ }
+ }
+
+ if (liblinphone_serialize_logs == TRUE) {
+ ortp_logv_flush();
}
}
-/**
- * Interpret a call destination as supplied by the user, and returns a fully qualified
- * LinphoneAddress.
- *
- * @ingroup call_control
- *
- * A sip address should look like DisplayName .
- * Basically this function performs the following tasks
- * - if a phone number is entered, prepend country prefix of the default proxy
- * configuration, eventually escape the '+' by 00.
- * - if no domain part is supplied, append the domain name of the default proxy
- * - if no sip: is present, prepend it
- *
- * The result is a syntaxically correct SIP address.
-**/
-
LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url){
- enum_lookup_res_t *enumres=NULL;
- char *enum_domain=NULL;
- LinphoneProxyConfig *proxy=lc->default_proxy;
- char *tmpurl;
- LinphoneAddress *uri;
-
- if (is_enum(url,&enum_domain)){
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Looking for telephone number destination..."));
- if (enum_lookup(enum_domain,&enumres)<0){
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Could not resolve this number."));
- ms_free(enum_domain);
- return NULL;
- }
- ms_free(enum_domain);
- tmpurl=enumres->sip_address[0];
- uri=linphone_address_new(tmpurl);
- enum_lookup_res_free(enumres);
- return uri;
- }
- /* check if we have a "sip:" */
- if (strstr(url,"sip:")==NULL){
- /* this doesn't look like a true sip uri */
- if (strchr(url,'@')!=NULL){
- /* seems like sip: is missing !*/
- tmpurl=ms_strdup_printf("sip:%s",url);
- uri=linphone_address_new(tmpurl);
- ms_free(tmpurl);
- if (uri){
- return uri;
- }
- }
-
- if (proxy!=NULL){
- /* append the proxy domain suffix */
- const char *identity=linphone_proxy_config_get_identity(proxy);
- char normalized_username[128];
- uri=linphone_address_new(identity);
- if (uri==NULL){
- return NULL;
- }
- linphone_address_set_display_name(uri,NULL);
- linphone_proxy_config_normalize_number(proxy,url,normalized_username,
- sizeof(normalized_username));
- linphone_address_set_username(uri,normalized_username);
- return uri;
- }else return NULL;
- }
- uri=linphone_address_new(url);
- if (uri!=NULL){
- return uri;
- }
- /* else we could not do anything with url given by user, so display an error */
- if (lc->vtable.display_warning!=NULL){
- lc->vtable.display_warning(lc,_("Could not parse given sip address. A sip url usually looks like sip:user@domain"));
- }
- return NULL;
+ LinphoneProxyConfig *proxy = linphone_core_get_default_proxy_config(lc);
+ return linphone_proxy_config_normalize_sip_uri(proxy, url);
}
/**
@@ -2255,18 +2585,39 @@ const char * linphone_core_get_route(LinphoneCore *lc){
return route;
}
-void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){
- if (call->refer_pending){
- LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc);
- LinphoneCall *newcall;
- cp->has_video &= !!lc->video_policy.automatically_initiate;
- cp->referer=call;
- ms_message("Starting new call to refered address %s",call->refer_to);
- call->refer_pending=FALSE;
- newcall=linphone_core_invite_with_params(lc,call->refer_to,cp);
- linphone_call_params_destroy(cp);
- if (newcall) linphone_core_notify_refer_state(lc,call,newcall);
+/**
+ * Start a new call as a consequence of a transfer request received from a call.
+ * This function is for advanced usage: the execution of transfers is automatically managed by the LinphoneCore. However if an application
+ * wants to have control over the call parameters for the new call, it should call this function immediately during the LinphoneCallRefered notification.
+ * @see LinphoneCoreVTable::call_state_changed
+ * @param lc the LinphoneCore
+ * @param call a call that has just been notified about LinphoneCallRefered state event.
+ * @param params the call parameters to be applied to the new call.
+ * @return a LinphoneCall corresponding to the new call that is attempted to the transfer destination.
+**/
+LinphoneCall * linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
+ LinphoneCallParams *cp=params ? linphone_call_params_copy(params) : linphone_core_create_default_call_parameters(lc);
+ LinphoneCall *newcall;
+
+ if (call->state!=LinphoneCallPaused){
+ ms_message("Automatically pausing current call to accept transfer.");
+ _linphone_core_pause_call(lc,call);
+ call->was_automatically_paused=TRUE;
}
+
+ if (!params){
+ cp->has_video = call->current_params->has_video; /*start the call to refer-target with video enabled if original call had video*/
+ }
+ cp->referer=call;
+ ms_message("Starting new call to refered address %s",call->refer_to);
+ call->refer_pending=FALSE;
+ newcall=linphone_core_invite_with_params(lc,call->refer_to,cp);
+ linphone_call_params_destroy(cp);
+ if (newcall) {
+ call->transfer_target=linphone_call_ref(newcall);
+ linphone_core_notify_refer_state(lc,call,newcall);
+ }
+ return newcall;
}
void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall){
@@ -2275,89 +2626,99 @@ void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, L
}
}
+/* returns the ideal route set for making an operation through this proxy.
+ * The list must be freed as well as the SalAddress content*/
+
+ /*
+* rfc3608
+6.1. Procedures at the UA
+
+ /.../
+ For example, some devices will use locally-configured
+ explicit loose routing to reach a next-hop proxy, and others will use
+ a default outbound-proxy routing rule. However, for the result to
+ function, the combination MUST provide valid routing in the local
+ environment. In general, the service route set is appended to any
+ locally configured route needed to egress the access proxy chain.
+ Systems designers must match the service routing policy of their
+ nodes with the basic SIP routing policy in order to get a workable
+ system.
+*/
+
+static MSList *make_routes_for_proxy(LinphoneProxyConfig *proxy, const LinphoneAddress *dest){
+ MSList *ret=NULL;
+ const char *local_route=linphone_proxy_config_get_route(proxy);
+ const LinphoneAddress *srv_route=linphone_proxy_config_get_service_route(proxy);
+ if (local_route){
+ ret=ms_list_append(ret,sal_address_new(local_route));
+ }
+ if (srv_route){
+ ret=ms_list_append(ret,sal_address_clone((SalAddress*)srv_route));
+ }
+ if (ret==NULL){
+ /*if the proxy address matches the domain part of the destination, then use the same transport
+ * as the one used for registration. This is done by forcing a route to this proxy.*/
+ SalAddress *proxy_addr=sal_address_new(linphone_proxy_config_get_addr(proxy));
+ if (strcmp(sal_address_get_domain(proxy_addr),linphone_address_get_domain(dest))==0){
+ ret=ms_list_append(ret,proxy_addr);
+ }else sal_address_destroy(proxy_addr);
+ }
+ return ret;
+}
+
LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri){
const MSList *elem;
LinphoneProxyConfig *found_cfg=NULL;
+ LinphoneProxyConfig *found_reg_cfg=NULL;
+ LinphoneProxyConfig *found_noreg_cfg=NULL;
LinphoneProxyConfig *default_cfg=lc->default_proxy;
- /*always prefer the default proxy if it is matching the destination uri*/
+ if (linphone_address_get_domain(uri) == NULL) {
+ ms_message("cannot seach for proxy for uri [%p] if no domain set. returning default",uri);
+ return default_cfg;
+ }
+ /*return default proxy if it is matching the destination uri*/
if (default_cfg){
const char *domain=linphone_proxy_config_get_domain(default_cfg);
- if (strcmp(domain,linphone_address_get_domain(uri))==0)
- return default_cfg;
+ if (strcmp(domain,linphone_address_get_domain(uri))==0){
+ found_cfg=default_cfg;
+ goto end;
+ }
}
- /*otherwise iterate through the other proxy config and return the first matching*/
+ /*otherwise return first registered, then first registering matching, otherwise first matching */
for (elem=linphone_core_get_proxy_config_list(lc);elem!=NULL;elem=elem->next){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
const char *domain=linphone_proxy_config_get_domain(cfg);
if (domain!=NULL && strcmp(domain,linphone_address_get_domain(uri))==0){
- found_cfg=cfg;
- break;
+ if (linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk ){
+ found_cfg=cfg;
+ break;
+ } else if (!found_reg_cfg && linphone_proxy_config_register_enabled(cfg)) {
+ found_reg_cfg=cfg;
+ } else if (!found_noreg_cfg){
+ found_noreg_cfg=cfg;
+ }
}
}
+end:
+ if ( !found_cfg && found_reg_cfg) found_cfg = found_reg_cfg;
+ else if( !found_cfg && found_noreg_cfg ) found_cfg = found_noreg_cfg;
+
+ if (found_cfg && found_cfg!=default_cfg){
+ ms_debug("Overriding default proxy setting for this call/message/subscribe operation.");
+ }else if (!found_cfg) found_cfg=default_cfg; /*when no matching proxy config is found, use the default proxy config*/
return found_cfg;
}
-const char *linphone_core_find_best_identity(LinphoneCore *lc, const LinphoneAddress *to, const char **route){
+const char *linphone_core_find_best_identity(LinphoneCore *lc, const LinphoneAddress *to){
LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(lc,to);
- if (cfg==NULL)
- linphone_core_get_default_proxy (lc,&cfg);
if (cfg!=NULL){
- if (route) *route=linphone_proxy_config_get_route(cfg);
return linphone_proxy_config_get_identity (cfg);
}
- return linphone_core_get_primary_contact (lc);
+ return linphone_core_get_primary_contact(lc);
}
-static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){
- LinphoneAddress *ctt;
- const char *localip=call->localip;
-
- /* first use user's supplied ip address if asked*/
- if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){
- ctt=linphone_core_get_primary_contact_parsed(lc);
- return ms_strdup_printf("sip:%s@%s",linphone_address_get_username(ctt),
- linphone_core_get_nat_address_resolved(lc));
- }
-
- /* if already choosed, don't change it */
- if (call->op && sal_op_get_contact(call->op)!=NULL){
- return NULL;
- }
- /* if the ping OPTIONS request succeeded use the contact guessed from the
- received, rport*/
- if (call->ping_op){
- const char *guessed=sal_op_get_contact(call->ping_op);
- if (guessed){
- ms_message("Contact has been fixed using OPTIONS to %s",guessed);
- return ms_strdup(guessed);
- }
- }
-
- /*if using a proxy, use the contact address as guessed with the REGISTERs*/
- if (dest_proxy && dest_proxy->op){
- const char *fixed_contact=sal_op_get_contact(dest_proxy->op);
- if (fixed_contact) {
- ms_message("Contact has been fixed using proxy to %s",fixed_contact);
- return ms_strdup(fixed_contact);
- }
- }
-
- ctt=linphone_core_get_primary_contact_parsed(lc);
-
- if (ctt!=NULL){
- char *ret;
- /*otherwise use supllied localip*/
- linphone_address_set_domain(ctt,localip);
- linphone_address_set_port_int(ctt,linphone_core_get_sip_port(lc));
- ret=linphone_address_as_string_uri_only(ctt);
- linphone_address_destroy(ctt);
- ms_message("Contact has been fixed using local ip to %s",ret);
- return ret;
- }
- return NULL;
-}
int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){
bool_t ice_ready = FALSE;
@@ -2385,50 +2746,62 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c
}
if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) {
- return linphone_core_start_invite(lc, call);
+ return linphone_core_start_invite(lc, call, NULL);
}
return 0;
}
-int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call){
+int linphone_core_restart_invite(LinphoneCore *lc, LinphoneCall *call){
+ linphone_call_create_op(call);
+ linphone_call_stop_media_streams(call);
+ ms_media_stream_sessions_uninit(&call->sessions[0]);
+ ms_media_stream_sessions_uninit(&call->sessions[1]);
+ linphone_call_init_media_streams(call);
+ return linphone_core_start_invite(lc,call, NULL);
+}
+
+int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination /* = NULL if to be taken from the call log */){
int err;
- char *contact;
char *real_url,*barmsg;
char *from;
- LinphoneProxyConfig *dest_proxy=call->dest_proxy;
-
/*try to be best-effort in giving real local or routable contact address */
- contact=get_fixed_contact(lc,call,dest_proxy);
- if (contact){
- sal_op_set_contact(call->op, contact);
- ms_free(contact);
- }
+ linphone_call_set_contact_op(call);
+
linphone_core_stop_dtmf_stream(lc);
- linphone_call_init_media_streams(call);
- if (lc->ringstream==NULL)
- audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
- linphone_call_make_local_media_description(lc,call);
+ linphone_call_make_local_media_description(call);
+
+ if (lc->ringstream==NULL) {
+ if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){
+ /*give a chance a set card prefered sampling frequency*/
+ if (call->localdesc->streams[0].max_rate>0) {
+ ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
+ }
+ if (!lc->use_files)
+ audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
+ }
+ }
+ real_url=linphone_address_as_string( destination ? destination : call->log->to);
+ from=linphone_address_as_string(call->log->from);
+
if (!lc->sip_conf.sdp_200_ack){
- call->media_pending=TRUE;
+ /*we are offering, set local media description before sending the call*/
sal_call_set_local_media_description(call->op,call->localdesc);
}
- real_url=linphone_address_as_string(call->log->to);
- from=linphone_address_as_string(call->log->from);
err=sal_call(call->op,from,real_url);
+ if (lc->sip_conf.sdp_200_ack){
+ /*we are NOT offering, set local media description after sending the call so that we are ready to
+ process the remote offer when it will arrive*/
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
+
call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/
- if (lc->sip_conf.sdp_200_ack){
- call->media_pending=TRUE;
- sal_call_set_local_media_description(call->op,call->localdesc);
- }
barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,barmsg);
+ linphone_core_notify_display_status(lc,barmsg);
ms_free(barmsg);
if (err<0){
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Could not call"));
+ linphone_core_notify_display_status(lc,_("Could not call"));
linphone_call_stop_media_streams(call);
linphone_call_set_state(call,LinphoneCallError,"Call failed");
}else {
@@ -2508,90 +2881,106 @@ LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddr
return call;
}
+static void linphone_transfer_routes_to_op(MSList *routes, SalOp *op){
+ MSList *it;
+ for(it=routes;it!=NULL;it=it->next){
+ SalAddress *addr=(SalAddress*)it->data;
+ sal_op_add_route_address(op,addr);
+ sal_address_destroy(addr);
+ }
+ ms_list_free(routes);
+}
+
+void linphone_configure_op(LinphoneCore *lc, SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact){
+ MSList *routes=NULL;
+ LinphoneProxyConfig *proxy=linphone_core_lookup_known_proxy(lc,dest);
+ const char *identity;
+ if (proxy){
+ identity=linphone_proxy_config_get_identity(proxy);
+ if (linphone_proxy_config_get_privacy(proxy)!=LinphonePrivacyDefault) {
+ sal_op_set_privacy(op,linphone_proxy_config_get_privacy(proxy));
+ }
+ }else identity=linphone_core_get_primary_contact(lc);
+ /*sending out of calls*/
+ if (proxy){
+ routes=make_routes_for_proxy(proxy,dest);
+ linphone_transfer_routes_to_op(routes,op);
+ }
+ sal_op_set_to_address(op,dest);
+ sal_op_set_from(op,identity);
+ sal_op_set_sent_custom_header(op,headers);
+ sal_op_set_realm(op,linphone_proxy_config_get_realm(proxy));
+ if (with_contact && proxy && proxy->op){
+ const SalAddress *contact;
+ if ((contact=sal_op_get_contact_address(proxy->op))){
+ SalTransport tport=sal_address_get_transport((SalAddress*)contact);
+ SalAddress *new_contact=sal_address_clone(contact);
+ sal_address_clean(new_contact); /* clean out contact_params that come from proxy config*/
+ sal_address_set_transport(new_contact,tport);
+ sal_op_set_contact_address(op,new_contact);
+ sal_address_destroy(new_contact);
+ }
+ }
+ sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0)); /*also set in linphone_call_new_incoming*/
+}
-/**
- * Initiates an outgoing call given a destination LinphoneAddress
- *
- * @ingroup call_control
- * @param lc the LinphoneCore object
- * @param addr the destination of the call (sip address).
- @param params call parameters
- *
- * The LinphoneAddress can be constructed directly using linphone_address_new(), or
- * created by linphone_core_interpret_url().
- * The application doesn't own a reference to the returned LinphoneCall object.
- * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application.
- *
- * @return a LinphoneCall object or NULL in case of failure
-**/
LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params)
{
- const char *route=NULL;
const char *from=NULL;
- LinphoneProxyConfig *proxy=NULL,*dest_proxy=NULL;
+ LinphoneProxyConfig *proxy=NULL;
LinphoneAddress *parsed_url2=NULL;
char *real_url=NULL;
LinphoneCall *call;
bool_t defer = FALSE;
+ LinphoneCallParams *cp = linphone_call_params_copy(params);
linphone_core_preempt_sound_resources(lc);
-
+
if(!linphone_core_can_we_add_call(lc)){
- if (lc->vtable.display_warning)
- lc->vtable.display_warning(lc,_("Sorry, we have reached the maximum number of simultaneous calls"));
+ linphone_core_notify_display_warning(lc,_("Sorry, we have reached the maximum number of simultaneous calls"));
return NULL;
}
- linphone_core_get_default_proxy(lc,&proxy);
- route=linphone_core_get_route(lc);
real_url=linphone_address_as_string(addr);
- dest_proxy=linphone_core_lookup_known_proxy(lc,addr);
+ proxy=linphone_core_lookup_known_proxy(lc,addr);
- if (proxy!=dest_proxy && dest_proxy!=NULL) {
- ms_message("Overriding default proxy setting for this call:");
- ms_message("The used identity will be %s",linphone_proxy_config_get_identity(dest_proxy));
- }
-
- if (dest_proxy!=NULL)
- from=linphone_proxy_config_get_identity(dest_proxy);
- else if (proxy!=NULL)
+ if (proxy!=NULL) {
from=linphone_proxy_config_get_identity(proxy);
+ cp->avpf_enabled = linphone_proxy_config_avpf_enabled(proxy);
+ cp->avpf_rr_interval = linphone_proxy_config_get_avpf_rr_interval(proxy) * 1000;
+ }else{
+ cp->avpf_enabled=linphone_core_get_avpf_mode(lc)==LinphoneAVPFEnabled;
+ if (cp->avpf_enabled) cp->avpf_rr_interval=linphone_core_get_avpf_rr_interval(lc) * 1000;
+ }
/* if no proxy or no identity defined for this proxy, default to primary contact*/
if (from==NULL) from=linphone_core_get_primary_contact(lc);
parsed_url2=linphone_address_new(from);
- call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params);
- call->dest_proxy=dest_proxy;
- sal_op_set_route(call->op,route);
+ call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),cp,proxy);
if(linphone_core_add_call(lc,call)!= 0)
{
ms_warning("we had a problem in adding the call into the invite ... weird");
linphone_call_unref(call);
+ linphone_call_params_destroy(cp);
return NULL;
}
+
/* this call becomes now the current one*/
lc->current_call=call;
linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call");
+ call->log->start_date_time=ms_time(NULL);
+ linphone_call_init_media_streams(call);
+
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
/* Defer the start of the call after the ICE gathering process. */
- linphone_call_init_media_streams(call);
- linphone_call_start_media_streams_for_ice_gathering(call);
- call->start_time=time(NULL);
- if (linphone_core_gather_ice_candidates(lc,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- linphone_call_stop_media_streams_for_ice_gathering(call);
- } else {
- defer = TRUE;
- }
+ if (linphone_call_prepare_ice(call,FALSE)==1)
+ defer=TRUE;
}
else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
#ifdef BUILD_UPNP
- linphone_call_init_media_streams(call);
- call->start_time=time(NULL);
if (linphone_core_update_upnp(lc,call)<0) {
/* uPnP port mappings failed, proceed with the call anyway. */
linphone_call_delete_upnp_session(call);
@@ -2613,14 +3002,14 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
call->ping_op=sal_op_new(lc->sal);
sal_ping(call->ping_op,from,real_url);
sal_op_set_user_pointer(call->ping_op,call);
- call->start_time=time(NULL);
defer = TRUE;
}
}
-
- if (defer==FALSE) linphone_core_start_invite(lc,call);
+
+ if (defer==FALSE) linphone_core_start_invite(lc,call,NULL);
if (real_url!=NULL) ms_free(real_url);
+ linphone_call_params_destroy(cp);
return call;
}
@@ -2630,6 +3019,10 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
* @ingroup call_control
* The remote endpoint is expected to issue a new call to the specified destination.
* The current call remains active and thus can be later paused or terminated.
+ *
+ * It is possible to follow the progress of the transfer provided that transferee sends notification about it.
+ * In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party.
+ * The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallConnected.
**/
int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *url)
{
@@ -2658,7 +3051,7 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char
* @param lc linphone core object
* @param call a running call you want to transfer
* @param dest a running call whose remote person will receive the transfer
- *
+ *
* @ingroup call_control
*
* The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately.
@@ -2666,6 +3059,10 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char
* This method will send a transfer request to the transfered person. The phone of the transfered is then
* expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically
* close the call with us (the 'dest' call).
+ *
+ * It is possible to follow the progress of the transfer provided that transferee sends notification about it.
+ * In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party.
+ * The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallConnected.
**/
int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){
int result = sal_call_refer_with_replaces (call->op,dest->op);
@@ -2673,7 +3070,7 @@ int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call,
return result;
}
-bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
+bool_t linphone_core_is_incoming_invite_pending(LinphoneCore*lc){
LinphoneCall *call = linphone_core_get_current_call(lc);
if(call != NULL)
{
@@ -2685,54 +3082,29 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
}
bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md){
- if (linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP){
- int i;
- for(i=0;in_active_streams;i++){
- SalStreamDescription *sd=&md->streams[i];
- if (sd->proto!=SalProtoRtpSavp){
- return TRUE;
- }
- }
- }
- return FALSE;
+ return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !sal_media_description_has_srtp(md);
}
void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
char *barmesg;
char *tmp;
LinphoneAddress *from_parsed;
- SalMediaDescription *md;
bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE);
- const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc);
-
- linphone_call_make_local_media_description(lc,call);
- sal_call_set_local_media_description(call->op,call->localdesc);
- md=sal_call_get_final_media_description(call->op);
- if (md){
- if (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md)){
- sal_call_decline(call->op,SalReasonMedia,NULL);
- linphone_call_unref(call);
- return;
- }
- }
from_parsed=linphone_address_new(sal_op_get_from(call->op));
linphone_address_clean(from_parsed);
tmp=linphone_address_as_string(from_parsed);
linphone_address_destroy(from_parsed);
barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"),
- (sal_call_autoanswer_asked(call->op)) ?_(" and asked autoanswer."):_("."));
- if (lc->vtable.show) lc->vtable.show(lc);
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,barmesg);
+ (sal_call_autoanswer_asked(call->op)) ?_(" and asked autoanswer."):".");
+ linphone_core_notify_show_interface(lc);
+ linphone_core_notify_display_status(lc,barmesg);
/* play the ring if this is the only call*/
if (ms_list_size(lc->calls)==1){
lc->current_call=call;
if (lc->ringstream && lc->dmfs_playing_start_time!=0){
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- lc->dmfs_playing_start_time=0;
+ linphone_core_stop_dtmf_stream(lc);
}
if (lc->sound_conf.ring_sndcard!=NULL){
if(lc->ringstream==NULL && lc->sound_conf.local_ring){
@@ -2748,19 +3120,25 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
}else{
/* else play a tone within the context of the current call */
call->ringing_beep=TRUE;
- linphone_core_play_tone(lc);
+ linphone_core_play_named_tone(lc,LinphoneToneCallWaiting);
}
linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call");
+ /*from now on, the application is aware of the call and supposed to take background task or already submitted notification to the user.
+ We can then drop our background task.*/
+ if (call->bg_task_id!=0) {
+ sal_end_background_task(call->bg_task_id);
+ call->bg_task_id=0;
+ }
if (call->state==LinphoneCallIncomingReceived){
- sal_call_notify_ringing(call->op,propose_early_media || ringback_tone!=NULL);
+ /*try to be best-effort in giving real local or routable contact address for 100Rel case*/
+ linphone_call_set_contact_op(call);
+
+ if (propose_early_media){
+ linphone_core_accept_early_media(lc,call);
+ }else sal_call_notify_ringing(call->op,FALSE);
- if (propose_early_media || ringback_tone!=NULL){
- linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
- md=sal_call_get_final_media_description(call->op);
- linphone_core_update_streams(lc,call,md);
- }
if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
linphone_core_accept_call(lc,call);
}
@@ -2771,26 +3149,96 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
ms_free(tmp);
}
+
+/**
+ * When receiving an incoming, accept to start a media session as early-media.
+ * This means the call is not accepted but audio & video streams can be established if the remote party supports early media.
+ * However, unlike after call acceptance, mic and camera input are not sent during early-media, though received audio & video are played normally.
+ * The call can then later be fully accepted using linphone_core_accept_call() or linphone_core_accept_call_with_params().
+ * @param lc the linphonecore
+ * @param call the call
+ * @param params the call params, can be NULL.
+ * @return 0 if successful, -1 otherwise.
+ * @ingroup call_control
+**/
+int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall* call, const LinphoneCallParams* params) {
+ if (call->state==LinphoneCallIncomingReceived){
+ SalMediaDescription* md;
+
+ /*try to be best-effort in giving real local or routable contact address for 100Rel case*/
+ linphone_call_set_contact_op(call);
+
+ // if parameters are passed, update the media description
+ if ( params ) {
+ linphone_call_set_new_params(call,params);
+ linphone_call_make_local_media_description (call);
+ sal_call_set_local_media_description ( call->op,call->localdesc );
+ sal_op_set_sent_custom_header ( call->op,params->custom_headers );
+ }
+
+ sal_call_notify_ringing(call->op, TRUE);
+
+ linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
+ md=sal_call_get_final_media_description(call->op);
+ if (md) linphone_core_update_streams(lc, call, md, call->state);
+ return 0;
+ }else{
+ ms_error("Bad state %s for linphone_core_accept_early_media_with_params()", linphone_call_state_to_string(call->state));
+ }
+
+ return -1;
+}
+
+/**
+ * Accept an early media session for an incoming call.
+ * This is identical as calling linphone_core_accept_early_media_with_params() with NULL call parameters.
+ * @see linphone_core_accept_early_media_with_params()
+ * @param lc the core
+ * @param call the incoming call
+ * @return 0 if successful, -1 otherwise.
+ * @ingroup call_control
+**/
+int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneCall* call){
+ return linphone_core_accept_early_media_with_params(lc, call, NULL);
+}
+
int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
const char *subject;
- call->camera_active=call->params.has_video;
- if (call->ice_session != NULL) {
- linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
- }
+ int err;
+ bool_t no_user_consent=call->params->no_user_consent;
+
+ linphone_call_fill_media_multicast_addr(call);
+
+ if (!no_user_consent) linphone_call_make_local_media_description(call);
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
}
#endif //BUILD_UPNP
- if (call->params.in_conference){
+ if (call->params->in_conference){
subject="Conference";
- }else{
+ }else if (!no_user_consent){
subject="Media change";
+ }else{
+ subject="Refreshing";
}
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Modifying call parameters..."));
- sal_call_set_local_media_description (call->op,call->localdesc);
- return sal_call_update(call->op,subject);
+ linphone_core_notify_display_status(lc,_("Modifying call parameters..."));
+ if (!lc->sip_conf.sdp_200_ack){
+ sal_call_set_local_media_description (call->op,call->localdesc);
+ } else {
+ sal_call_set_local_media_description (call->op,NULL);
+ }
+ if (call->dest_proxy && call->dest_proxy->op){
+ /*give a chance to update the contact address if connectivity has changed*/
+ sal_op_set_contact_address(call->op,sal_op_get_contact_address(call->dest_proxy->op));
+ }else sal_op_set_contact_address(call->op,NULL);
+ err= sal_call_update(call->op,subject,no_user_consent);
+ if (lc->sip_conf.sdp_200_ack){
+ /*we are NOT offering, set local media description after sending the call so that we are ready to
+ process the remote offer when it will arrive*/
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
+ return err;
}
/**
@@ -2809,48 +3257,57 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
**/
int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
int err=0;
+ LinphoneCallState nextstate, initial_state;
+
+#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
+ bool_t has_video = FALSE;
+#endif
+
+ switch(initial_state=call->state){
+ case LinphoneCallIncomingReceived:
+ case LinphoneCallIncomingEarlyMedia:
+ case LinphoneCallOutgoingRinging:
+ case LinphoneCallOutgoingEarlyMedia:
+ nextstate=LinphoneCallEarlyUpdating;
+ break;
+ case LinphoneCallStreamsRunning:
+ case LinphoneCallPaused:
+ case LinphoneCallPausedByRemote:
+ nextstate=LinphoneCallUpdating;
+ break;
+ default:
+ ms_error("linphone_core_update_call() is not allowed in [%s] state",linphone_call_state_to_string(call->state));
+ return -1;
+ }
+
if (params!=NULL){
- linphone_call_set_state(call,LinphoneCallUpdating,"Updating call");
-#ifdef VIDEO_ENABLED
- bool_t has_video = call->params.has_video;
+ linphone_call_set_state(call,nextstate,"Updating call");
+#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
+ has_video = call->params->has_video;
// Video removing
if((call->videostream != NULL) && !params->has_video) {
- if (call->ice_session != NULL) {
- ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list);
- call->videostream->ms.ice_check_list = NULL;
- }
-#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
if (linphone_core_update_upnp(lc, call)<0) {
/* uPnP port mappings failed, proceed with the call anyway. */
linphone_call_delete_upnp_session(call);
}
}
-#endif //BUILD_UPNP
- }
-
- _linphone_call_params_copy(&call->params,params);
- linphone_call_make_local_media_description(lc, call);
+ }
+#endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */
+ linphone_call_set_new_params(call,params);
+ err=linphone_call_prepare_ice(call,FALSE);
+ if (err==1) {
+ ms_message("Defer call update to gather ICE candidates");
+ return 0;
+ }
+
+#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
// Video adding
- if (!has_video && call->params.has_video) {
- if (call->ice_session != NULL) {
- /* Defer call update until the ICE candidates gathering process has finished. */
- ms_message("Defer call update to gather ICE candidates");
- linphone_call_init_video_stream(call);
- video_stream_prepare_video(call->videostream);
- if (linphone_core_gather_ice_candidates(lc,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- } else {
- return err;
- }
- }
-#ifdef BUILD_UPNP
+ if (!has_video && call->params->has_video) {
if(call->upnp_session != NULL) {
ms_message("Defer call update to add uPnP port mappings");
- linphone_call_init_video_stream(call);
video_stream_prepare_video(call->videostream);
if (linphone_core_update_upnp(lc, call)<0) {
/* uPnP port mappings failed, proceed with the call anyway. */
@@ -2859,16 +3316,20 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
return err;
}
}
-#endif //BUILD_UPNP
}
-#endif
- err = linphone_core_start_update_call(lc, call);
+#endif //defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
+ if ((err = linphone_core_start_update_call(lc, call)) && call->state!=initial_state) {
+ /*Restore initial state*/
+ linphone_call_set_state(call,initial_state,NULL);
+ }
+
}else{
#ifdef VIDEO_ENABLED
- if (call->videostream!=NULL){
+ if ((call->videostream != NULL) && (call->state == LinphoneCallStreamsRunning)) {
video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
- if (call->camera_active && call->videostream->cam!=lc->video_conf.device){
- video_stream_change_camera(call->videostream,lc->video_conf.device);
+ video_stream_set_fps(call->videostream, linphone_core_get_preferred_framerate(lc));
+ if (call->camera_enabled && call->videostream->cam!=lc->video_conf.device){
+ video_stream_change_camera(call->videostream, lc->video_conf.device);
}else video_stream_update_video_params(call->videostream);
}
#endif
@@ -2880,127 +3341,135 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
/**
* @ingroup call_control
* When receiving a #LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer.
- *
+ *
* When receiving a #LinphoneCallUpdatedByRemote state notification (ie an incoming reINVITE), the default behaviour of
- * LinphoneCore is to automatically answer the reINIVTE with call parameters unchanged.
+ * LinphoneCore is defined by the "defer_update_default" option of the "sip" section of the config. If this option is 0 (the default)
+ * then the LinphoneCore automatically answers the reINIVTE with call parameters unchanged.
* However when for example when the remote party updated the call to propose a video stream, it can be useful
- * to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during
- * the call state notifiacation, to deactivate the automatic answer that would just confirm the audio but reject the video.
+ * to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during
+ * the call state notification, to deactivate the automatic answer that would just confirm the audio but reject the video.
* Then, when the user responds to dialog prompt, it becomes possible to call linphone_core_accept_call_update() to answer
* the reINVITE, with eventually video enabled in the LinphoneCallParams argument.
*
- * @return 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a #LinphoneCallUpdatedByRemote notification, which is illegal.
+ * The #LinphoneCallUpdatedByRemote notification can also arrive when receiving an INVITE without SDP. In such case, an unchanged offer is made
+ * in the 200Ok, and when the ACK containing the SDP answer is received, #LinphoneCallUpdatedByRemote is triggered to notify the application of possible
+ * changes in the media session. However in such case defering the update has no meaning since we just generating an offer.
+ *
+ * @return 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a valid #LinphoneCallUpdatedByRemote notification.
**/
int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){
if (call->state==LinphoneCallUpdatedByRemote){
+ if (call->expect_media_in_ack){
+ ms_error("linphone_core_defer_call_update() is not possible during a late offer incoming reINVITE (INVITE without SDP)");
+ return -1;
+ }
call->defer_update=TRUE;
return 0;
+ }else{
+ ms_error("linphone_core_defer_call_update() not done in state LinphoneCallUpdatedByRemote");
}
- ms_error("linphone_core_defer_call_update() not done in state LinphoneCallUpdatedByRemote");
return -1;
}
-int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call){
+int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState next_state, const char *state_info){
SalMediaDescription *md;
if (call->ice_session != NULL) {
if (ice_session_nb_losing_pairs(call->ice_session) > 0) {
/* Defer the sending of the answer until there are no losing pairs left. */
return 0;
}
- linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
}
-#ifdef BUILD_UPNP
- if(call->upnp_session != NULL) {
- linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
- }
-#endif //BUILD_UPNP
+ linphone_call_make_local_media_description(call);
+
linphone_call_update_remote_session_id_and_ver(call);
+ linphone_call_stop_ice_for_inactive_streams(call);
sal_call_set_local_media_description(call->op,call->localdesc);
sal_call_accept(call->op);
md=sal_call_get_final_media_description(call->op);
- if (md && !sal_media_description_empty(md))
- linphone_core_update_streams (lc,call,md);
- linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
+ if (md && !sal_media_description_empty(md)){
+ linphone_core_update_streams(lc, call, md, next_state);
+ linphone_call_fix_call_parameters(call);
+ }
+ linphone_call_set_state(call,next_state,state_info);
return 0;
}
/**
* @ingroup call_control
* Accept call modifications initiated by other end.
- *
+ *
* This call may be performed in response to a #LinphoneCallUpdatedByRemote state notification.
* When such notification arrives, the application can decide to call linphone_core_defer_update_call() so that it can
* have the time to prompt the user. linphone_call_get_remote_params() can be used to get information about the call parameters
* requested by the other party, such as whether a video stream is requested.
- *
+ *
* When the user accepts or refuse the change, linphone_core_accept_call_update() can be done to answer to the other party.
* If params is NULL, then the same call parameters established before the update request will continue to be used (no change).
* If params is not NULL, then the update will be accepted according to the parameters passed.
- * Typical example is when a user accepts to start video, then params should indicate that video stream should be used
+ * Typical example is when a user accepts to start video, then params should indicate that video stream should be used
* (see linphone_call_params_enable_video()).
* @param lc the linphone core object.
* @param call the LinphoneCall object
* @param params a LinphoneCallParams object describing the call parameters to accept.
- * @return 0 if sucessful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state).
+ * @return 0 if successful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state).
**/
int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
- SalMediaDescription *remote_desc;
- bool_t keep_sdp_version;
-#ifdef VIDEO_ENABLED
- bool_t old_has_video = call->params.has_video;
-#endif
if (call->state!=LinphoneCallUpdatedByRemote){
ms_error("linphone_core_accept_update(): invalid state %s to call this function.",
- linphone_call_state_to_string(call->state));
+ linphone_call_state_to_string(call->state));
return -1;
}
+ return _linphone_core_accept_call_update(lc, call, params, call->prevstate, linphone_call_state_to_string(call->prevstate));
+}
+
+int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info){
+ SalMediaDescription *remote_desc;
+ bool_t keep_sdp_version;
+#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
+ bool_t old_has_video = call->params->has_video;
+#endif
+
remote_desc = sal_call_get_remote_media_description(call->op);
keep_sdp_version = lp_config_get_int(lc->config, "sip", "keep_sdp_version", 0);
if (keep_sdp_version &&(remote_desc->session_id == call->remote_session_id) && (remote_desc->session_ver == call->remote_session_ver)) {
/* Remote has sent an INVITE with the same SDP as before, so send a 200 OK with the same SDP as before. */
ms_warning("SDP version has not changed, send same SDP as before.");
sal_call_accept(call->op);
- linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
+ linphone_call_set_state(call,next_state,state_info);
return 0;
}
if (params==NULL){
- call->params.has_video=lc->video_policy.automatically_accept || call->current_params.has_video;
- }else
- call->params=*params;
-
- if (call->params.has_video && !linphone_core_video_enabled(lc)){
- ms_warning("linphone_core_accept_call_update(): requested video but video support is globally disabled. Refusing video.");
- call->params.has_video=FALSE;
- }
- if (call->current_params.in_conference) {
- ms_warning("Video isn't supported in conference");
- call->params.has_video = FALSE;
- }
- call->params.has_video &= linphone_core_media_description_contains_video_stream(remote_desc);
- call->camera_active=call->params.has_video;
- linphone_call_make_local_media_description(lc,call);
- if (call->ice_session != NULL) {
- linphone_core_update_ice_from_remote_media_description(call, remote_desc);
-#ifdef VIDEO_ENABLED
- if ((call->ice_session != NULL) &&!ice_session_candidates_gathered(call->ice_session)) {
- if ((call->params.has_video) && (call->params.has_video != old_has_video)) {
- linphone_call_init_video_stream(call);
- video_stream_prepare_video(call->videostream);
- if (linphone_core_gather_ice_candidates(lc,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- } else return 0;
- }
+ linphone_call_params_enable_video(call->params, lc->video_policy.automatically_accept || call->current_params->has_video);
+ if (!sal_call_is_offerer(call->op)) {
+ /*reset call param for multicast because this param is only relevant when offering*/
+ linphone_call_params_enable_audio_multicast(call->params,FALSE);
+ linphone_call_params_enable_video_multicast(call->params,FALSE);
}
-#endif //VIDEO_ENABLED
+ }else
+ linphone_call_set_new_params(call,params);
+
+ if (call->params->has_video && !linphone_core_video_enabled(lc)){
+ ms_warning("linphone_core_accept_call_update(): requested video but video support is globally disabled. Refusing video.");
+ call->params->has_video=FALSE;
+ }
+ if (call->current_params->in_conference) {
+ ms_warning("Video isn't supported in conference");
+ call->params->has_video = FALSE;
+ }
+ /*update multicast params according to call params*/
+ linphone_call_fill_media_multicast_addr(call);
+
+ linphone_call_init_media_streams(call); /*so that video stream is initialized if necessary*/
+ if (call->ice_session != NULL) {
+ if (linphone_call_prepare_ice(call,TRUE)==1)
+ return 0;/*deferred to completion of ICE gathering*/
}
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op));
#ifdef VIDEO_ENABLED
- if ((call->params.has_video) && (call->params.has_video != old_has_video)) {
- linphone_call_init_video_stream(call);
+ if ((call->params->has_video) && (call->params->has_video != old_has_video)) {
video_stream_prepare_video(call->videostream);
if (linphone_core_update_upnp(lc, call)<0) {
/* uPnP update failed, proceed with the call anyway. */
@@ -3011,7 +3480,7 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const
}
#endif //BUILD_UPNP
- linphone_core_start_accept_call_update(lc, call);
+ linphone_core_start_accept_call_update(lc, call, next_state, state_info);
return 0;
}
@@ -3045,13 +3514,11 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){
* @param params the specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters.
*
**/
-int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params)
-{
- LinphoneProxyConfig *cfg=NULL;
- const char *contact=NULL;
+int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
SalOp *replaced;
SalMediaDescription *new_md;
bool_t was_ringing=FALSE;
+ MSList * iterator, *copy;
if (call==NULL){
//if just one call is present answer the only one ...
@@ -3061,18 +3528,44 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
call = (LinphoneCall*)linphone_core_get_calls(lc)->data;
}
- if (call->state==LinphoneCallConnected){
- /*call already accepted*/
- return -1;
+ switch(call->state){
+ case LinphoneCallIncomingReceived:
+ case LinphoneCallIncomingEarlyMedia:
+ break;
+ default:
+ ms_error("linphone_core_accept_call_with_params() call [%p] is in state [%s], operation not permitted.",
+ call, linphone_call_state_to_string(call->state));
+ return -1;
+ break;
}
+
+ for (iterator=copy=ms_list_copy(linphone_core_get_calls(lc));iterator!=NULL;iterator=iterator->next) {
+ LinphoneCall *a_call=(LinphoneCall*)iterator->data;
+ if (a_call==call) continue;
+ switch(a_call->state){
+ case LinphoneCallOutgoingInit:
+ case LinphoneCallOutgoingProgress:
+ case LinphoneCallOutgoingRinging:
+ case LinphoneCallOutgoingEarlyMedia:
+ ms_message("Already existing call [%p] in state [%s], canceling it before accepting new call [%p]",a_call
+ ,linphone_call_state_to_string(a_call->state)
+ ,call);
+ linphone_core_terminate_call(lc,a_call);
+ break;
+ default:
+ break; /*nothing to do*/
+ }
+ }
+ ms_list_free(copy);
+
/* check if this call is supposed to replace an already running one*/
replaced=sal_call_get_replaces(call->op);
if (replaced){
LinphoneCall *rc=(LinphoneCall*)sal_op_get_user_pointer (replaced);
if (rc){
ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.",
- call,rc);
+ call,rc);
linphone_core_terminate_call(lc,rc);
}
}
@@ -3084,9 +3577,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
/*stop ringing */
if (lc->ringstream!=NULL) {
ms_message("stop ringing");
- ring_stop(lc->ringstream);
- ms_message("ring stopped");
- lc->ringstream=NULL;
+ linphone_core_stop_ringing(lc);
was_ringing=TRUE;
}
if (call->ringing_beep){
@@ -3094,47 +3585,47 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
call->ringing_beep=FALSE;
}
- linphone_core_get_default_proxy(lc,&cfg);
- call->dest_proxy=cfg;
- call->dest_proxy=linphone_core_lookup_known_proxy(lc,call->log->to);
-
- if (cfg!=call->dest_proxy && call->dest_proxy!=NULL) {
- ms_message("Overriding default proxy setting for this call:");
- ms_message("The used identity will be %s",linphone_proxy_config_get_identity(call->dest_proxy));
- }
- /*try to be best-effort in giving real local or routable contact address*/
- contact=get_fixed_contact(lc,call,call->dest_proxy);
- if (contact)
- sal_op_set_contact(call->op,contact);
-
+ /*try to be best-effort in giving real local or routable contact address */
+ linphone_call_set_contact_op(call);
if (params){
const SalMediaDescription *md = sal_call_get_remote_media_description(call->op);
- _linphone_call_params_copy(&call->params,params);
+ linphone_call_set_new_params(call,params);
// There might not be a md if the INVITE was lacking an SDP
// In this case we use the parameters as is.
- if (md) call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
- call->camera_active=call->params.has_video;
- linphone_call_make_local_media_description(lc,call);
+ if (md) {
+ linphone_call_set_compatible_incoming_call_parameters(call, md);
+ }
+ linphone_call_prepare_ice(call,TRUE);
+ linphone_call_make_local_media_description(call);
sal_call_set_local_media_description(call->op,call->localdesc);
+ sal_op_set_sent_custom_header(call->op,params->custom_headers);
}
-
- if (call->audiostream==NULL)
- linphone_call_init_media_streams(call);
- if (!was_ringing && call->audiostream->ms.ticker==NULL){
+
+ /*give a chance a set card prefered sampling frequency*/
+ if (call->localdesc->streams[0].max_rate>0) {
+ ms_message ("configuring prefered card sampling rate to [%i]",call->localdesc->streams[0].max_rate);
+ if (lc->sound_conf.play_sndcard)
+ ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
+ if (lc->sound_conf.capt_sndcard)
+ ms_snd_card_set_preferred_sample_rate(lc->sound_conf.capt_sndcard, call->localdesc->streams[0].max_rate);
+ }
+
+ if (!was_ringing && call->audiostream->ms.state==MSStreamInitialized && !lc->use_files){
audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
}
linphone_call_update_remote_session_id_and_ver(call);
+ linphone_call_stop_ice_for_inactive_streams(call);
sal_call_accept(call->op);
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Connected."));
+ linphone_core_notify_display_status(lc,_("Connected."));
lc->current_call=call;
linphone_call_set_state(call,LinphoneCallConnected,"Connected");
new_md=sal_call_get_final_media_description(call->op);
- linphone_core_update_streams(lc, call, new_md);
if (new_md){
+ linphone_core_update_streams(lc, call, new_md, LinphoneCallStreamsRunning);
+ linphone_call_fix_call_parameters(call);
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
- }else call->media_pending=TRUE;
+ }else call->expect_media_in_ack=TRUE;
ms_message("call answered.");
return 0;
@@ -3144,32 +3635,25 @@ int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *e
sal_call_terminate(call->op);
/*stop ringing*/
- if (lc->ringstream!=NULL) {
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- }
+ linphone_core_stop_ringing(lc);
linphone_call_stop_media_streams(call);
#ifdef BUILD_UPNP
linphone_call_delete_upnp_session(call);
#endif //BUILD_UPNP
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Call aborted") );
+ linphone_core_notify_display_status(lc,_("Call aborted") );
linphone_call_set_state(call,LinphoneCallError,error);
return 0;
}
static void terminate_call(LinphoneCore *lc, LinphoneCall *call){
if (call->state==LinphoneCallIncomingReceived){
- if (call->reason!=LinphoneReasonNotAnswered)
- call->reason=LinphoneReasonDeclined;
+ if (call->non_op_error.reason!=SalReasonRequestTimeout)
+ call->non_op_error.reason=SalReasonDeclined;
}
/*stop ringing*/
- if (lc->ringstream!=NULL) {
- ring_stop(lc->ringstream);
- lc->ringstream=NULL;
- }
+ linphone_core_stop_ringing(lc);
linphone_call_stop_media_streams(call);
@@ -3177,20 +3661,19 @@ static void terminate_call(LinphoneCore *lc, LinphoneCall *call){
linphone_call_delete_upnp_session(call);
#endif //BUILD_UPNP
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Call ended") );
+ linphone_core_notify_display_status(lc,_("Call ended") );
linphone_call_set_state(call,LinphoneCallEnd,"Call terminated");
}
int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri){
if (call->state==LinphoneCallIncomingReceived){
sal_call_decline(call->op,SalReasonRedirect,redirect_uri);
- call->reason=LinphoneReasonDeclined;
+ sal_error_info_set(&call->non_op_error,SalReasonRedirect,603,"Call redirected",NULL);
terminate_call(lc,call);
}else{
ms_error("Bad state for call redirection.");
return -1;
- }
+ }
return 0;
}
@@ -3218,39 +3701,44 @@ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call)
{
call = the_call;
}
- sal_call_terminate(call->op);
+ switch (call->state) {
+ case LinphoneCallReleased:
+ case LinphoneCallEnd:
+ ms_warning("No need to terminate a call [%p] in state [%s]",call,linphone_call_state_to_string(call->state));
+ return -1;
+ case LinphoneCallIncomingReceived:
+ case LinphoneCallIncomingEarlyMedia:
+ return linphone_core_decline_call(lc,call,LinphoneReasonDeclined);
+ case LinphoneCallOutgoingInit: {
+ /* In state OutgoingInit, op has to be destroyed */
+ sal_op_release(call->op);
+ call->op = NULL;
+ break;
+ }
+ default:
+ sal_call_terminate(call->op);
+ break;
+ }
terminate_call(lc,call);
return 0;
}
/**
* Decline a pending incoming call, with a reason.
- *
+ *
* @ingroup call_control
- *
+ *
* @param lc the linphone core
* @param call the LinphoneCall, must be in the IncomingReceived state.
* @param reason the reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy
**/
int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall * call, LinphoneReason reason){
- SalReason sal_reason=SalReasonUnknown;
if (call->state!=LinphoneCallIncomingReceived && call->state!=LinphoneCallIncomingEarlyMedia){
ms_error("linphone_core_decline_call(): Cannot decline a call that is in state %s",linphone_call_state_to_string(call->state));
return -1;
}
- switch(reason){
- case LinphoneReasonDeclined:
- sal_reason=SalReasonDeclined;
- break;
- case LinphoneReasonBusy:
- sal_reason=SalReasonBusy;
- break;
- default:
- ms_error("linphone_core_decline_call(): unsupported reason %s",linphone_reason_to_string(reason));
- return -1;
- break;
- }
- sal_call_decline(call->op,sal_reason,NULL);
+
+ sal_call_decline(call->op,linphone_reason_to_sal(reason),NULL);
terminate_call(lc,call);
return 0;
}
@@ -3273,6 +3761,8 @@ int linphone_core_terminate_all_calls(LinphoneCore *lc){
/**
* Returns the current list of calls.
+ * @param[in] lc The LinphoneCore object
+ * @return \mslist{LinphoneCall}
*
* Note that this list is read-only and might be changed by the core after a function call to linphone_core_iterate().
* Similarly the LinphoneCall objects inside it might be destroyed without prior notice.
@@ -3299,8 +3789,7 @@ bool_t linphone_core_in_call(const LinphoneCore *lc){
*
* @ingroup call_control
**/
-LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc)
-{
+LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc){
return lc->current_call;
}
@@ -3310,44 +3799,44 @@ LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc)
*
* @ingroup call_control
**/
-int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call)
-{
+int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call){
+ int err=_linphone_core_pause_call(lc,call);
+ if (err==0) call->paused_by_app=TRUE;
+ return err;
+}
+
+/* Internal version that does not play tone indication*/
+int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call){
const char *subject=NULL;
if (call->state!=LinphoneCallStreamsRunning && call->state!=LinphoneCallPausedByRemote){
ms_warning("Cannot pause this call, it is not active.");
return -1;
}
- linphone_call_make_local_media_description(lc,call);
- if (call->ice_session != NULL) {
- linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+ if (sal_media_description_has_dir(call->resultdesc, SalStreamSendRecv)) {
+ subject = "Call on hold";
+ } else if (sal_media_description_has_dir(call->resultdesc, SalStreamRecvOnly)) {
+ subject = "Call on hold for me too";
+ } else {
+ ms_error("No reason to pause this call, it is already paused or inactive.");
+ return -1;
}
+ linphone_call_set_state(call, LinphoneCallPausing, "Pausing call");
+ linphone_call_make_local_media_description(call);
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
}
#endif //BUILD_UPNP
- if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){
- sal_media_description_set_dir(call->localdesc,SalStreamSendOnly);
- subject="Call on hold";
- }else if (sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)){
- sal_media_description_set_dir(call->localdesc,SalStreamSendOnly);
- subject="Call on hold for me too";
- }else{
- ms_error("No reason to pause this call, it is already paused or inactive.");
- return -1;
- }
sal_call_set_local_media_description(call->op,call->localdesc);
- if (sal_call_update(call->op,subject) != 0){
- if (lc->vtable.display_warning)
- lc->vtable.display_warning(lc,_("Could not pause the call"));
+ if (sal_call_update(call->op,subject,FALSE) != 0){
+ linphone_core_notify_display_warning(lc,_("Could not pause the call"));
}
lc->current_call=NULL;
- linphone_call_set_state(call,LinphoneCallPausing,"Pausing call");
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,_("Pausing the current call..."));
+ linphone_core_notify_display_status(lc,_("Pausing the current call..."));
if (call->audiostream || call->videostream)
linphone_call_stop_media_streams (call);
+ call->paused_by_app=FALSE;
return 0;
}
@@ -3361,7 +3850,7 @@ int linphone_core_pause_all_calls(LinphoneCore *lc){
LinphoneCall *call=(LinphoneCall *)elem->data;
LinphoneCallState cs=linphone_call_get_state(call);
if (cs==LinphoneCallStreamsRunning || cs==LinphoneCallPausedByRemote){
- linphone_core_pause_call(lc,call);
+ _linphone_core_pause_call(lc,call);
}
}
return 0;
@@ -3369,14 +3858,19 @@ int linphone_core_pause_all_calls(LinphoneCore *lc){
void linphone_core_preempt_sound_resources(LinphoneCore *lc){
LinphoneCall *current_call;
+
if (linphone_core_is_in_conference(lc)){
linphone_core_leave_conference(lc);
return;
}
+
current_call=linphone_core_get_current_call(lc);
if(current_call != NULL){
ms_message("Pausing automatically the current call.");
- linphone_core_pause_call(lc,current_call);
+ _linphone_core_pause_call(lc,current_call);
+ }
+ if (lc->ringstream){
+ linphone_core_stop_ringing(lc);
}
}
@@ -3385,44 +3879,56 @@ void linphone_core_preempt_sound_resources(LinphoneCore *lc){
*
* @ingroup call_control
**/
-int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call)
-{
+int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){
char temp[255]={0};
- LinphoneCall *call = the_call;
const char *subject="Call resuming";
-
+
if(call->state!=LinphoneCallPaused ){
ms_warning("we cannot resume a call that has not been established and paused before");
return -1;
}
- if (call->params.in_conference==FALSE){
+ if (call->params->in_conference==FALSE){
+ if (linphone_core_sound_resources_locked(lc)){
+ ms_warning("Cannot resume call %p because another call is locking the sound resources.",call);
+ return -1;
+ }
linphone_core_preempt_sound_resources(lc);
ms_message("Resuming call %p",call);
}
+ call->was_automatically_paused=FALSE;
+
/* Stop playing music immediately. If remote side is a conference it
prevents the participants to hear it while the 200OK comes back.*/
if (call->audiostream) audio_stream_play(call->audiostream, NULL);
- linphone_call_make_local_media_description(lc,the_call);
- if (call->ice_session != NULL) {
- linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
- }
+ linphone_call_make_local_media_description(call);
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
}
#endif //BUILD_UPNP
- sal_call_set_local_media_description(call->op,call->localdesc);
+ if (!lc->sip_conf.sdp_200_ack){
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ } else {
+ sal_call_set_local_media_description(call->op,NULL);
+ }
sal_media_description_set_dir(call->localdesc,SalStreamSendRecv);
- if (call->params.in_conference && !call->current_params.in_conference) subject="Conference";
- if(sal_call_update(call->op,subject) != 0){
+ if (call->params->in_conference && !call->current_params->in_conference) subject="Conference";
+ if ( sal_call_update(call->op,subject,FALSE) != 0){
return -1;
}
- linphone_call_set_state (call,LinphoneCallResuming,"Resuming");
+ linphone_call_set_state(call,LinphoneCallResuming,"Resuming");
+ if (call->params->in_conference==FALSE)
+ lc->current_call=call;
snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call));
- if (lc->vtable.display_status)
- lc->vtable.display_status(lc,temp);
+ linphone_core_notify_display_status(lc,temp);
+
+ if (lc->sip_conf.sdp_200_ack){
+ /*we are NOT offering, set local media description after sending the call so that we are ready to
+ process the remote offer when it will arrive*/
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
return 0;
}
@@ -3436,23 +3942,32 @@ static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *rad
* @param lc
* @param remote_address
* @return the LinphoneCall of the call if found
- *
+ *
* @ingroup call_control
*/
LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){
+ LinphoneCall *call=NULL;
LinphoneAddress *raddr=linphone_address_new(remote_address);
+ if (raddr) {
+ call=linphone_core_get_call_by_remote_address2(lc, raddr);
+ linphone_address_unref(raddr);
+ }
+ return call;
+}
+LinphoneCall *linphone_core_get_call_by_remote_address2(LinphoneCore *lc, const LinphoneAddress *raddr){
MSList *elem=ms_list_find_custom(lc->calls,(int (*)(const void*,const void *))remote_address_compare,raddr);
+
if (elem) return (LinphoneCall*) elem->data;
return NULL;
}
int linphone_core_send_publish(LinphoneCore *lc,
- LinphoneOnlineStatus presence_mode)
+ LinphonePresenceModel *presence)
{
const MSList *elem;
for (elem=linphone_core_get_proxy_config_list(lc);elem!=NULL;elem=ms_list_next(elem)){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
- if (cfg->publish) linphone_proxy_config_send_publish(cfg,presence_mode);
+ if (cfg->publish) linphone_proxy_config_send_publish(cfg,presence);
}
return 0;
}
@@ -3489,6 +4004,9 @@ int linphone_core_get_inc_timeout(LinphoneCore *lc){
**/
void linphone_core_set_in_call_timeout(LinphoneCore *lc, int seconds){
lc->sip_conf.in_call_timeout=seconds;
+ if( linphone_core_ready(lc)){
+ lp_config_set_int(lc->config, "sip", "in_call_timeout", seconds);
+ }
}
/**
@@ -3521,31 +4039,128 @@ void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds){
lc->sip_conf.delayed_timeout=seconds;
}
-void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,
- const char *contact,
- LinphoneOnlineStatus presence_mode)
-{
+void linphone_core_set_presence_info(LinphoneCore *lc, int minutes_away, const char *contact, LinphoneOnlineStatus os) {
+ LinphonePresenceModel *presence = NULL;
+ char *description = NULL;
+ LinphonePresenceActivityType acttype = LinphonePresenceActivityUnknown;
+
if (minutes_away>0) lc->minutes_away=minutes_away;
- if (lc->alt_contact!=NULL) {
- ms_free(lc->alt_contact);
- lc->alt_contact=NULL;
+ switch (os) {
+ case LinphoneStatusOffline:
+ acttype = LinphonePresenceActivityOffline;
+ break;
+ case LinphoneStatusOnline:
+ acttype = LinphonePresenceActivityOnline;
+ break;
+ case LinphoneStatusBusy:
+ acttype = LinphonePresenceActivityBusy;
+ break;
+ case LinphoneStatusBeRightBack:
+ acttype = LinphonePresenceActivityInTransit;
+ break;
+ case LinphoneStatusAway:
+ acttype = LinphonePresenceActivityAway;
+ break;
+ case LinphoneStatusOnThePhone:
+ acttype = LinphonePresenceActivityOnThePhone;
+ break;
+ case LinphoneStatusOutToLunch:
+ acttype = LinphonePresenceActivityLunch;
+ break;
+ case LinphoneStatusDoNotDisturb:
+ acttype = LinphonePresenceActivityBusy;
+ description = "Do not disturb";
+ break;
+ case LinphoneStatusMoved:
+ acttype = LinphonePresenceActivityPermanentAbsence;
+ break;
+ case LinphoneStatusAltService:
+ acttype = LinphonePresenceActivityBusy;
+ description = "Using another messaging service";
+ break;
+ case LinphoneStatusPending:
+ acttype = LinphonePresenceActivityOther;
+ description = "Waiting for user acceptance";
+ break;
+ case LinphoneStatusVacation:
+ acttype = LinphonePresenceActivityVacation;
+ break;
+ case LinphoneStatusEnd:
+ ms_warning("Invalid status LinphoneStatusEnd");
+ return;
}
- if (contact) lc->alt_contact=ms_strdup(contact);
- if (lc->presence_mode!=presence_mode){
- linphone_core_notify_all_friends(lc,presence_mode);
- /*
- Improve the use of all LINPHONE_STATUS available.
- !TODO Do not mix "presence status" with "answer status code"..
- Use correct parameter to follow sip_if_match/sip_etag.
- */
- linphone_core_send_publish(lc,presence_mode);
+ presence = linphone_presence_model_new_with_activity(acttype, description);
+ linphone_presence_model_set_contact(presence, contact);
+ linphone_core_set_presence_model(lc, presence);
+}
+
+void linphone_core_send_presence(LinphoneCore *lc, LinphonePresenceModel *presence){
+ linphone_core_notify_all_friends(lc,presence);
+ linphone_core_send_publish(lc,presence);
+}
+
+void linphone_core_set_presence_model(LinphoneCore *lc, LinphonePresenceModel *presence) {
+ linphone_core_send_presence(lc,presence);
+
+ if ((lc->presence_model != NULL) && (lc->presence_model != presence)) {
+ linphone_presence_model_unref(lc->presence_model);
+ lc->presence_model = presence;
}
- lc->presence_mode=presence_mode;
}
LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc){
- return lc->presence_mode;
+ LinphonePresenceActivity *activity = NULL;
+ const char *description = NULL;
+
+ activity = linphone_presence_model_get_activity(lc->presence_model);
+ description = linphone_presence_activity_get_description(activity);
+ switch (linphone_presence_activity_get_type(activity)) {
+ case LinphonePresenceActivityOffline:
+ return LinphoneStatusOffline;
+ case LinphonePresenceActivityOnline:
+ return LinphoneStatusOnline;
+ case LinphonePresenceActivityBusy:
+ if (description != NULL) {
+ if (strcmp(description, "Do not disturb") == 0)
+ return LinphoneStatusDoNotDisturb;
+ else if (strcmp(description, "Using another messaging service") == 0)
+ return LinphoneStatusAltService;
+ }
+ return LinphoneStatusBusy;
+ case LinphonePresenceActivityInTransit:
+ case LinphonePresenceActivitySteering:
+ return LinphoneStatusBeRightBack;
+ case LinphonePresenceActivityAway:
+ return LinphoneStatusAway;
+ case LinphonePresenceActivityOnThePhone:
+ return LinphoneStatusOnThePhone;
+ case LinphonePresenceActivityBreakfast:
+ case LinphonePresenceActivityDinner:
+ case LinphonePresenceActivityLunch:
+ case LinphonePresenceActivityMeal:
+ return LinphoneStatusOutToLunch;
+ case LinphonePresenceActivityPermanentAbsence:
+ return LinphoneStatusMoved;
+ case LinphonePresenceActivityOther:
+ if (description != NULL) {
+ if (strcmp(description, "Waiting for user acceptance") == 0)
+ return LinphoneStatusPending;
+ }
+ return LinphoneStatusBusy;
+ case LinphonePresenceActivityVacation:
+ return LinphoneStatusVacation;
+ case LinphonePresenceActivityAppointment:
+ case LinphonePresenceActivityMeeting:
+ case LinphonePresenceActivityWorship:
+ return LinphoneStatusDoNotDisturb;
+ default:
+ return LinphoneStatusBusy;
+ }
+}
+
+LinphonePresenceModel * linphone_core_get_presence_model(const LinphoneCore *lc) {
+ return lc->presence_model;
}
/**
@@ -3609,9 +4224,7 @@ void linphone_core_set_mic_gain_db (LinphoneCore *lc, float gaindb){
ms_message("linphone_core_set_mic_gain_db(): no active call.");
return;
}
- if (st->volrecv){
- ms_filter_call_method(st->volsend,MS_VOLUME_SET_DB_GAIN,&gain);
- }else ms_warning("Could not apply gain: gain control wasn't activated.");
+ set_mic_gain_db(st,gain);
}
/**
@@ -3642,9 +4255,7 @@ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){
ms_message("linphone_core_set_playback_gain_db(): no active call.");
return;
}
- if (st->volrecv){
- ms_filter_call_method(st->volrecv,MS_VOLUME_SET_DB_GAIN,&gain);
- }else ms_warning("Could not apply gain: gain control wasn't activated.");
+ set_playback_gain_db(st,gain);
}
/**
@@ -3694,20 +4305,18 @@ static MSSndCard *get_card_from_string_id(const char *devid, unsigned int cap){
}
}
if (sndcard==NULL) {
- /* get a card that has read+write capabilities */
- sndcard=ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
- /* otherwise refine to the first card having the right capability*/
- if (sndcard==NULL){
- const MSList *elem=ms_snd_card_manager_get_list(ms_snd_card_manager_get());
- for(;elem!=NULL;elem=elem->next){
- sndcard=(MSSndCard*)elem->data;
- if (ms_snd_card_get_capabilities(sndcard) & cap) break;
- }
+ if ((cap & MS_SND_CARD_CAP_CAPTURE) && (cap & MS_SND_CARD_CAP_PLAYBACK)){
+ sndcard=ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
+ }else if (cap & MS_SND_CARD_CAP_CAPTURE){
+ sndcard=ms_snd_card_manager_get_default_capture_card(ms_snd_card_manager_get());
+ }
+ else if (cap & MS_SND_CARD_CAP_PLAYBACK){
+ sndcard=ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get());
}
if (sndcard==NULL){/*looks like a bug! take the first one !*/
const MSList *elem=ms_snd_card_manager_get_list(ms_snd_card_manager_get());
if (elem) sndcard=(MSSndCard*)elem->data;
- }
+ }
}
if (sndcard==NULL) ms_error("Could not find a suitable soundcard !");
return sndcard;
@@ -3842,36 +4451,54 @@ const char** linphone_core_get_video_devices(const LinphoneCore *lc){
return lc->video_conf.cams;
}
-/**
- * Update detection of sound devices.
- *
- * Use this function when the application is notified of USB plug events, so that
- * list of available hardwares for sound playback and capture is updated.
- **/
void linphone_core_reload_sound_devices(LinphoneCore *lc){
- const char *ringer,*playback,*capture;
- ringer=linphone_core_get_ringer_device(lc);
- playback=linphone_core_get_playback_device(lc);
- capture=linphone_core_get_capture_device(lc);
+ const char *ringer;
+ const char *playback;
+ const char *capture;
+ char *ringer_copy = NULL;
+ char *playback_copy = NULL;
+ char *capture_copy = NULL;
+
+ ringer = linphone_core_get_ringer_device(lc);
+ if (ringer != NULL) {
+ ringer_copy = ms_strdup(ringer);
+ }
+ playback = linphone_core_get_playback_device(lc);
+ if (playback != NULL) {
+ playback_copy = ms_strdup(playback);
+ }
+ capture = linphone_core_get_capture_device(lc);
+ if (capture != NULL) {
+ capture_copy = ms_strdup(capture);
+ }
ms_snd_card_manager_reload(ms_snd_card_manager_get());
build_sound_devices_table(lc);
- linphone_core_set_ringer_device(lc,ringer);
- linphone_core_set_playback_device(lc,playback);
- linphone_core_set_capture_device(lc,capture);
+ if (ringer_copy != NULL) {
+ linphone_core_set_ringer_device(lc, ringer_copy);
+ ms_free(ringer_copy);
+ }
+ if (playback_copy != NULL) {
+ linphone_core_set_playback_device(lc, playback_copy);
+ ms_free(playback_copy);
+ }
+ if (capture_copy != NULL) {
+ linphone_core_set_capture_device(lc, capture_copy);
+ ms_free(capture_copy);
+ }
}
-/**
- * Update detection of camera devices.
- *
- * Use this function when the application is notified of USB plug events, so that
- * list of available hardwares for video capture is updated.
- **/
void linphone_core_reload_video_devices(LinphoneCore *lc){
- const char *devid;
- devid=linphone_core_get_video_device(lc);
+ char *devid_copy = NULL;
+ const char *devid = linphone_core_get_video_device(lc);
+ if (devid != NULL) {
+ devid_copy = ms_strdup(devid);
+ }
ms_web_cam_manager_reload(ms_web_cam_manager_get());
build_video_devices_table(lc);
- linphone_core_set_video_device(lc,devid);
+ if (devid_copy != NULL) {
+ linphone_core_set_video_device(lc, devid_copy);
+ ms_free(devid_copy);
+ }
}
char linphone_core_get_sound_source(LinphoneCore *lc)
@@ -3935,6 +4562,10 @@ const char *linphone_core_get_ring(const LinphoneCore *lc){
**/
void linphone_core_set_root_ca(LinphoneCore *lc,const char *path){
sal_set_root_ca(lc->sal, path);
+ if (lc->http_verify_policy){
+ belle_tls_verify_policy_set_root_ca(lc->http_verify_policy,path);
+ }
+ lp_config_set_string(lc->config,"sip","root_ca",path);
}
/**
@@ -3945,16 +4576,20 @@ void linphone_core_set_root_ca(LinphoneCore *lc,const char *path){
* @ingroup initializing
**/
const char *linphone_core_get_root_ca(LinphoneCore *lc){
- return sal_get_root_ca(lc->sal);
+ return lp_config_get_string(lc->config,"sip","root_ca",NULL);
}
/**
* Specify whether the tls server certificate must be verified when connecting to a SIP/TLS server.
- *
+ *
* @ingroup initializing
**/
void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno){
sal_verify_server_certificates(lc->sal,yesno);
+ if (lc->http_verify_policy){
+ belle_tls_verify_policy_set_exceptions(lc->http_verify_policy, yesno ? 0 : BELLE_TLS_VERIFY_ANY_REASON);
+ }
+ lp_config_set_int(lc->config,"sip","verify_server_certs",yesno);
}
/**
@@ -3963,11 +4598,17 @@ void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno){
**/
void linphone_core_verify_server_cn(LinphoneCore *lc, bool_t yesno){
sal_verify_server_cn(lc->sal,yesno);
+ if (lc->http_verify_policy){
+ belle_tls_verify_policy_set_exceptions(lc->http_verify_policy, yesno ? 0 : BELLE_TLS_VERIFY_CN_MISMATCH);
+ }
+ lp_config_set_int(lc->config,"sip","verify_server_cn",yesno);
}
static void notify_end_of_ring(void *ud, MSFilter *f, unsigned int event, void *arg){
LinphoneCore *lc=(LinphoneCore*)ud;
- lc->preview_finished=1;
+ if (event==MS_PLAYER_EOF){
+ lc->preview_finished=1;
+ }
}
int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCbFunc func,void * userdata)
@@ -3997,7 +4638,7 @@ void linphone_core_set_ringback(LinphoneCore *lc, const char *path){
if (lc->sound_conf.remote_ring!=0){
ms_free(lc->sound_conf.remote_ring);
}
- lc->sound_conf.remote_ring=ms_strdup(path);
+ lc->sound_conf.remote_ring=path?ms_strdup(path):NULL;
}
/**
@@ -4010,7 +4651,10 @@ const char * linphone_core_get_ringback(const LinphoneCore *lc){
}
/**
- * Enables or disable echo cancellation. Value is saved an used for subsequent calls
+ * Enables or disable echo cancellation. Value is saved and used for subsequent calls.
+ * This actually controls software echo cancellation. If hardware echo cancellation is available, it will be always used and activated for calls, regardless
+ * of the value passed to this function.
+ * When hardware echo cancellation is available, the software one is of course not activated.
*
* @ingroup media_parameters
**/
@@ -4038,36 +4682,35 @@ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){
return lc->sound_conf.ea;
}
-/**
- * Mutes or unmutes the local microphone.
- *
- * @ingroup media_parameters
-**/
-void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){
- LinphoneCall *call=linphone_core_get_current_call(lc);
- AudioStream *st=NULL;
- if (linphone_core_is_in_conference(lc)){
- lc->conf_ctx.local_muted=val;
- st=lc->conf_ctx.local_participant;
- }else if (call==NULL){
- ms_warning("linphone_core_mute_mic(): No current call !");
- return;
- }else{
- st=call->audiostream;
- call->audio_muted=val;
+static void linphone_core_mute_audio_stream(LinphoneCore *lc, AudioStream *st, bool_t val) {
+ if (val) {
+ audio_stream_set_mic_gain(st, 0);
+ } else {
+ audio_stream_set_mic_gain_db(st, lc->sound_conf.soft_mic_lev);
}
- if (st!=NULL){
- audio_stream_set_mic_gain(st,
- (val==TRUE) ? 0 : pow(10,lc->sound_conf.soft_mic_lev/10));
- if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){
- audio_stream_mute_rtp(st,val);
- }
-
+
+ if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){
+ audio_stream_mute_rtp(st,val);
}
}
-/**
- * Returns whether microphone is muted.
-**/
+
+void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){
+ LinphoneCall *call;
+ const MSList *list;
+ const MSList *elem;
+
+ if (linphone_core_is_in_conference(lc)){
+ lc->conf_ctx.local_muted=val;
+ linphone_core_mute_audio_stream(lc, lc->conf_ctx.local_participant, val);
+ }
+ list = linphone_core_get_calls(lc);
+ for (elem = list; elem != NULL; elem = elem->next) {
+ call = (LinphoneCall *)elem->data;
+ call->audio_muted = val;
+ linphone_core_mute_audio_stream(lc, call->audiostream, val);
+ }
+}
+
bool_t linphone_core_is_mic_muted(LinphoneCore *lc) {
LinphoneCall *call=linphone_core_get_current_call(lc);
if (linphone_core_is_in_conference(lc)){
@@ -4079,13 +4722,21 @@ bool_t linphone_core_is_mic_muted(LinphoneCore *lc) {
return call->audio_muted;
}
+void linphone_core_enable_mic(LinphoneCore *lc, bool_t enable) {
+ linphone_core_mute_mic(lc, (enable == TRUE) ? FALSE : TRUE);
+}
+
+bool_t linphone_core_mic_enabled(LinphoneCore *lc) {
+ return (linphone_core_is_mic_muted(lc) == TRUE) ? FALSE : TRUE;
+}
+
// returns rtp transmission status for an active stream
// if audio is muted and config parameter rtp_no_xmit_on_audio_mute
// was set on then rtp transmission is also muted
bool_t linphone_core_is_rtp_muted(LinphoneCore *lc){
LinphoneCall *call=linphone_core_get_current_call(lc);
if (call==NULL){
- ms_warning("linphone_core_is_mic_muted(): No current call !");
+ ms_warning("linphone_core_is_rtp_muted(): No current call !");
return FALSE;
}
if( linphone_core_get_rtp_no_xmit_on_audio_mute(lc)){
@@ -4102,38 +4753,10 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){
return lc->sound_conf.agc;
}
-/**
- * Send the specified dtmf.
- *
- * @ingroup media_parameters
- * This function only works during calls. The dtmf is automatically played to the user.
- * @param lc The LinphoneCore object
- * @param dtmf The dtmf name specified as a char, such as '0', '#' etc...
- *
-**/
void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf)
{
LinphoneCall *call=linphone_core_get_current_call(lc);
- if (call==NULL){
- ms_warning("linphone_core_send_dtmf(): no active call");
- return;
- }
- /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/
- if (linphone_core_get_use_rfc2833_for_dtmf(lc)!=0 || linphone_core_get_use_info_for_dtmf(lc)==0)
- {
- /* In Band DTMF */
- if (call->audiostream!=NULL){
- audio_stream_send_dtmf(call->audiostream,dtmf);
- }
- else
- {
- ms_error("we cannot send RFC2833 dtmf when we are not in communication");
- }
- }
- if (linphone_core_get_use_info_for_dtmf(lc)!=0){
- /* Out of Band DTMF (use INFO method) */
- sal_call_send_dtmf(call->op,dtmf);
- }
+ linphone_call_send_dtmf(call, dtmf);
}
void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){
@@ -4142,6 +4765,17 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){
if (server)
lc->net_conf.stun_server=ms_strdup(server);
else lc->net_conf.stun_server=NULL;
+
+ /* each time the stun server is changed, we must clean the resolved cached addrinfo*/
+ if (lc->net_conf.stun_addrinfo){
+ freeaddrinfo(lc->net_conf.stun_addrinfo);
+ lc->net_conf.stun_addrinfo=NULL;
+ }
+ /*if a stun server is set, we must request asynchronous resolution immediately to be ready for call*/
+ if (lc->net_conf.stun_server){
+ linphone_core_resolve_stun_server(lc);
+ }
+
if (linphone_core_ready(lc))
lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server);
}
@@ -4150,7 +4784,8 @@ const char * linphone_core_get_stun_server(const LinphoneCore *lc){
return lc->net_conf.stun_server;
}
-bool_t linphone_core_upnp_available(const LinphoneCore *lc){
+
+bool_t linphone_core_upnp_available(){
#ifdef BUILD_UPNP
return TRUE;
#else
@@ -4175,21 +4810,6 @@ const char * linphone_core_get_upnp_external_ipaddress(const LinphoneCore *lc){
}
-const char * linphone_core_get_relay_addr(const LinphoneCore *lc){
- return lc->net_conf.relay;
-}
-
-int linphone_core_set_relay_addr(LinphoneCore *lc, const char *addr){
- if (lc->net_conf.relay!=NULL){
- ms_free(lc->net_conf.relay);
- lc->net_conf.relay=NULL;
- }
- if (addr){
- lc->net_conf.relay=ms_strdup(addr);
- }
- return 0;
-}
-
void linphone_core_set_nat_address(LinphoneCore *lc, const char *addr)
{
if (lc->net_conf.nat_address!=NULL){
@@ -4212,8 +4832,8 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc)
char ipstring [INET6_ADDRSTRLEN];
if (lc->net_conf.nat_address==NULL) return NULL;
-
- if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len)<0) {
+
+ if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len, 5060)<0) {
return lc->net_conf.nat_address;
}
@@ -4231,13 +4851,32 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc)
}
void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol){
-#ifndef BUILD_UPNP
- if(pol == LinphonePolicyUseUpnp) {
- ms_warning("UPNP is not available, reset firewall policy to no firewall");
- pol = LinphonePolicyNoFirewall;
- }
+ const char *policy = "none";
+
+ switch (pol) {
+ default:
+ case LinphonePolicyNoFirewall:
+ policy = "none";
+ break;
+ case LinphonePolicyUseNatAddress:
+ policy = "nat_address";
+ break;
+ case LinphonePolicyUseStun:
+ policy = "stun";
+ break;
+ case LinphonePolicyUseIce:
+ policy = "ice";
+ break;
+ case LinphonePolicyUseUpnp:
+#ifdef BUILD_UPNP
+ policy = "upnp";
+#else
+ ms_warning("UPNP is not available, reset firewall policy to no firewall");
+ pol = LinphonePolicyNoFirewall;
+ policy = "none";
#endif //BUILD_UPNP
- lc->net_conf.firewall_policy=pol;
+ break;
+ }
#ifdef BUILD_UPNP
if(pol == LinphonePolicyUseUpnp) {
if(lc->upnp == NULL) {
@@ -4251,73 +4890,86 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy
}
linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0));
#endif //BUILD_UPNP
+ switch(pol) {
+ case LinphonePolicyUseUpnp:
+ sal_nat_helper_enable(lc->sal, FALSE);
+ sal_enable_auto_contacts(lc->sal,FALSE);
+ sal_use_rport(lc->sal, FALSE);
+ break;
+ default:
+ sal_nat_helper_enable(lc->sal, lp_config_get_int(lc->config,"net","enable_nat_helper",1));
+ sal_enable_auto_contacts(lc->sal,TRUE);
+ sal_use_rport(lc->sal, lp_config_get_int(lc->config,"sip","use_rport",1));
+ break;
+ }
if (lc->sip_conf.contact) update_primary_contact(lc);
if (linphone_core_ready(lc))
- lp_config_set_int(lc->config,"net","firewall_policy",pol);
+ lp_config_set_string(lc->config,"net","firewall_policy",policy);
}
-
LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){
- return lc->net_conf.firewall_policy;
+ const char *policy;
+ policy = lp_config_get_string(lc->config, "net", "firewall_policy", NULL);
+ if ((policy == NULL) || (strcmp(policy, "0") == 0))
+ return LinphonePolicyNoFirewall;
+ else if ((strcmp(policy, "nat_address") == 0) || (strcmp(policy, "1") == 0))
+ return LinphonePolicyUseNatAddress;
+ else if ((strcmp(policy, "stun") == 0) || (strcmp(policy, "2") == 0))
+ return LinphonePolicyUseStun;
+ else if ((strcmp(policy, "ice") == 0) || (strcmp(policy, "3") == 0))
+ return LinphonePolicyUseIce;
+ else if ((strcmp(policy, "upnp") == 0) || (strcmp(policy, "4") == 0))
+ return LinphonePolicyUseUpnp;
+ else
+ return LinphonePolicyNoFirewall;
}
-/**
- * Get the list of call logs (past calls).
- *
- * @ingroup call_logs
-**/
+
+
+/*******************************************************************************
+ * Call log related functions *
+ ******************************************************************************/
+
const MSList * linphone_core_get_call_logs(LinphoneCore *lc){
return lc->call_logs;
}
-/**
- * Erase the call log.
- *
- * @ingroup call_logs
-**/
void linphone_core_clear_call_logs(LinphoneCore *lc){
lc->missed_calls=0;
- ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy);
+ ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_unref);
lc->call_logs=ms_list_free(lc->call_logs);
call_logs_write_to_config_file(lc);
}
-/**
- * Returns number of missed calls.
- * Once checked, this counter can be reset with linphone_core_reset_missed_calls_count().
-**/
int linphone_core_get_missed_calls_count(LinphoneCore *lc) {
return lc->missed_calls;
}
-/**
- * Resets the counter of missed calls.
-**/
void linphone_core_reset_missed_calls_count(LinphoneCore *lc) {
lc->missed_calls=0;
}
-/**
- * Remove a specific call log from call history list.
- * This function destroys the call log object. It must not be accessed anymore by the application after calling this function.
- * @param lc the linphone core object
- * @param a LinphoneCallLog object.
-**/
void linphone_core_remove_call_log(LinphoneCore *lc, LinphoneCallLog *cl){
lc->call_logs = ms_list_remove(lc->call_logs, cl);
call_logs_write_to_config_file(lc);
- linphone_call_log_destroy(cl);
+ linphone_call_log_unref(cl);
}
+
+
+
static void toggle_video_preview(LinphoneCore *lc, bool_t val){
#ifdef VIDEO_ENABLED
if (val){
if (lc->previewstream==NULL){
+ const char *display_filter=linphone_core_get_video_display_filter(lc);
+ MSVideoSize vsize=lc->video_conf.preview_vsize.width!=0 ? lc->video_conf.preview_vsize : lc->video_conf.vsize;
lc->previewstream=video_preview_new();
- video_preview_set_size(lc->previewstream,lc->video_conf.vsize);
- if (lc->video_conf.displaytype)
- video_preview_set_display_filter_name(lc->previewstream,lc->video_conf.displaytype);
- if (lc->preview_window_id!=0)
+ video_preview_set_size(lc->previewstream,vsize);
+ if (display_filter)
+ video_preview_set_display_filter_name(lc->previewstream,display_filter);
+ if (lc->preview_window_id != NULL)
video_preview_set_native_window_id(lc->previewstream,lc->preview_window_id);
+ video_preview_set_fps(lc->previewstream,linphone_core_get_preferred_framerate(lc));
video_preview_start(lc->previewstream,lc->video_conf.device);
}
}else{
@@ -4329,37 +4981,6 @@ static void toggle_video_preview(LinphoneCore *lc, bool_t val){
#endif
}
-/**
- * Enables video globally.
- *
- * @ingroup media_parameters
- * This function does not have any effect during calls. It just indicates LinphoneCore to
- * initiate future calls with video or not. The two boolean parameters indicate in which
- * direction video is enabled. Setting both to false disables video entirely.
- *
- * @param lc The LinphoneCore object
- * @param vcap_enabled indicates whether video capture is enabled
- * @param display_enabled indicates whether video display should be shown
- *
-**/
-void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t display_enabled){
-#ifndef VIDEO_ENABLED
- if (vcap_enabled || display_enabled)
- ms_warning("This version of linphone was built without video support.");
-#endif
- lc->video_conf.capture=vcap_enabled;
- lc->video_conf.display=display_enabled;
- if (linphone_core_ready(lc)){
- lp_config_set_int(lc->config,"video","display",lc->video_conf.display);
- lp_config_set_int(lc->config,"video","capture",lc->video_conf.capture);
- }
- /* need to re-apply network bandwidth settings*/
- linphone_core_set_download_bandwidth(lc,
- linphone_core_get_download_bandwidth(lc));
- linphone_core_set_upload_bandwidth(lc,
- linphone_core_get_upload_bandwidth(lc));
-}
-
bool_t linphone_core_video_supported(LinphoneCore *lc){
#ifdef VIDEO_ENABLED
return TRUE;
@@ -4368,14 +4989,68 @@ bool_t linphone_core_video_supported(LinphoneCore *lc){
#endif
}
-/**
- * Returns TRUE if video is enabled, FALSE otherwise.
- * @ingroup media_parameters
-**/
+void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t display_enabled) {
+ linphone_core_enable_video_capture(lc, vcap_enabled);
+ linphone_core_enable_video_display(lc, display_enabled);
+}
+
bool_t linphone_core_video_enabled(LinphoneCore *lc){
return (lc->video_conf.display || lc->video_conf.capture);
}
+static void reapply_network_bandwidth_settings(LinphoneCore *lc) {
+ linphone_core_set_download_bandwidth(lc, linphone_core_get_download_bandwidth(lc));
+ linphone_core_set_upload_bandwidth(lc, linphone_core_get_upload_bandwidth(lc));
+}
+
+void linphone_core_enable_video_capture(LinphoneCore *lc, bool_t enable) {
+#ifndef VIDEO_ENABLED
+ if (enable == TRUE) {
+ ms_warning("Cannot enable video capture, this version of linphone was built without video support.");
+ }
+#endif
+ lc->video_conf.capture = enable;
+ if (linphone_core_ready(lc)) {
+ lp_config_set_int(lc->config, "video", "capture", lc->video_conf.capture);
+ }
+ /* Need to re-apply network bandwidth settings. */
+ reapply_network_bandwidth_settings(lc);
+}
+
+void linphone_core_enable_video_display(LinphoneCore *lc, bool_t enable) {
+#ifndef VIDEO_ENABLED
+ if (enable == TRUE) {
+ ms_warning("Cannot enable video display, this version of linphone was built without video support.");
+ }
+#endif
+ lc->video_conf.display = enable;
+ if (linphone_core_ready(lc)) {
+ lp_config_set_int(lc->config, "video", "display", lc->video_conf.display);
+ }
+ /* Need to re-apply network bandwidth settings. */
+ reapply_network_bandwidth_settings(lc);
+}
+
+void linphone_core_enable_video_source_reuse(LinphoneCore* lc, bool_t enable){
+#ifndef VIDEO_ENABLED
+ if (enable == TRUE) {
+ ms_warning("Cannot enable video display, this version of linphone was built without video support.");
+ }
+#endif
+ lc->video_conf.reuse_preview_source = enable;
+ if( linphone_core_ready(lc) ){
+ lp_config_set_int(lc->config, "video", "reuse_source", lc->video_conf.reuse_preview_source);
+ }
+}
+
+bool_t linphone_core_video_capture_enabled(LinphoneCore *lc) {
+ return lc->video_conf.capture;
+}
+
+bool_t linphone_core_video_display_enabled(LinphoneCore *lc) {
+ return lc->video_conf.display;
+}
+
/**
* Sets the default policy for video.
* This policy defines whether:
@@ -4433,6 +5108,9 @@ void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val){
#ifdef VIDEO_ENABLED
LinphoneCall *call=linphone_core_get_current_call (lc);
lc->video_conf.selfview=val;
+ if (linphone_core_ready(lc)) {
+ lp_config_set_int(lc->config,"video","self_view",linphone_core_self_view_enabled(lc));
+ }
if (call && call->videostream){
video_stream_enable_self_view(call->videostream,val);
}
@@ -4535,7 +5213,7 @@ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) {
const char *linphone_core_get_static_picture(LinphoneCore *lc) {
const char *path=NULL;
#ifdef VIDEO_ENABLED
- path=ms_static_image_get_default_image();
+ path=ms_static_image_get_default_image();
#else
ms_warning("Video support not compiled.");
#endif
@@ -4572,7 +5250,7 @@ float linphone_core_get_static_picture_fps(LinphoneCore *lc) {
if (vs && vs->source) {
if (ms_filter_get_id(vs->source) == MS_STATIC_IMAGE_ID) {
- float fps;
+ float fps;
ms_filter_call_method(vs->source, MS_FILTER_GET_FPS,(void *)&fps);
return fps;
@@ -4589,27 +5267,64 @@ float linphone_core_get_static_picture_fps(LinphoneCore *lc) {
*
* @ingroup media_parameters
**/
-unsigned long linphone_core_get_native_video_window_id(const LinphoneCore *lc){
+void * linphone_core_get_native_video_window_id(const LinphoneCore *lc){
+ if (lc->video_window_id) {
+ /* case where the video id was previously set by the app*/
+ return lc->video_window_id;
+ }else{
#ifdef VIDEO_ENABLED
- LinphoneCall *call=linphone_core_get_current_call (lc);
- if (call && call->videostream)
- return video_stream_get_native_window_id(call->videostream);
- if (lc->previewstream)
- return video_stream_get_native_window_id(lc->previewstream);
+ /*case where it was not set but we want to get the one automatically created by mediastreamer2 (desktop versions only)*/
+ LinphoneCall *call=linphone_core_get_current_call (lc);
+ if (call && call->videostream)
+ return video_stream_get_native_window_id(call->videostream);
#endif
- return lc->video_window_id;
+ }
+ return 0;
}
-/**@ingroup media_parameters
- * Set the native video window id where the video is to be displayed.
- * If not set the core will create its own window.
-**/
-void linphone_core_set_native_video_window_id(LinphoneCore *lc, unsigned long id){
+/* unsets the video id for all calls (indeed it may be kept by filters or videostream object itself by paused calls)*/
+static void unset_video_window_id(LinphoneCore *lc, bool_t preview, void *id){
#ifdef VIDEO_ENABLED
- LinphoneCall *call=linphone_core_get_current_call(lc);
+ LinphoneCall *call;
+ MSList *elem;
+#endif
+
+ if ((id != NULL)
+#ifndef _WIN32
+ && ((unsigned long)id != (unsigned long)-1)
+#endif
+ ){
+ ms_error("Invalid use of unset_video_window_id()");
+ return;
+ }
+#ifdef VIDEO_ENABLED
+ for(elem=lc->calls;elem!=NULL;elem=elem->next){
+ call=(LinphoneCall *) elem->data;
+ if (call->videostream){
+ if (preview)
+ video_stream_set_native_preview_window_id(call->videostream,id);
+ else
+ video_stream_set_native_window_id(call->videostream,id);
+ }
+ }
+#endif
+}
+
+void linphone_core_set_native_video_window_id(LinphoneCore *lc, void *id){
+ if ((id == NULL)
+#ifndef _WIN32
+ || ((unsigned long)id == (unsigned long)-1)
+#endif
+ ){
+ unset_video_window_id(lc,FALSE,id);
+ }
lc->video_window_id=id;
- if (call!=NULL && call->videostream){
- video_stream_set_native_window_id(call->videostream,id);
+#ifdef VIDEO_ENABLED
+ {
+ LinphoneCall *call=linphone_core_get_current_call(lc);
+ if (call!=NULL && call->videostream){
+ video_stream_set_native_window_id(call->videostream,id);
+ }
}
#endif
}
@@ -4619,31 +5334,46 @@ void linphone_core_set_native_video_window_id(LinphoneCore *lc, unsigned long id
*
* @ingroup media_parameters
**/
-unsigned long linphone_core_get_native_preview_window_id(const LinphoneCore *lc){
+void * linphone_core_get_native_preview_window_id(const LinphoneCore *lc){
+ if (lc->preview_window_id){
+ /*case where the id was set by the app previously*/
+ return lc->preview_window_id;
+ }else{
+ /*case where we want the id automatically created by mediastreamer2 (desktop versions only)*/
#ifdef VIDEO_ENABLED
- LinphoneCall *call=linphone_core_get_current_call (lc);
- if (call && call->videostream)
- return video_stream_get_native_preview_window_id(call->videostream);
- if (lc->previewstream)
- return video_preview_get_native_window_id(lc->previewstream);
+ LinphoneCall *call=linphone_core_get_current_call(lc);
+ if (call && call->videostream)
+ return video_stream_get_native_preview_window_id(call->videostream);
+ if (lc->previewstream)
+ return video_preview_get_native_window_id(lc->previewstream);
#endif
- return lc->preview_window_id;
+ }
+ return 0;
}
/**
* @ingroup media_parameters
* Set the native window id where the preview video (local camera) is to be displayed.
* This has to be used in conjonction with linphone_core_use_preview_window().
- * If not set the core will create its own window.
+ * MacOS, Linux, Windows: if not set or zero the core will create its own window, unless the special id -1 is given.
**/
-void linphone_core_set_native_preview_window_id(LinphoneCore *lc, unsigned long id){
+void linphone_core_set_native_preview_window_id(LinphoneCore *lc, void *id){
+ if ((id == NULL)
+#ifndef _WIN32
+ || ((unsigned long)id == (unsigned long)-1)
+#endif
+ ) {
+ unset_video_window_id(lc,TRUE,id);
+ }
lc->preview_window_id=id;
#ifdef VIDEO_ENABLED
- LinphoneCall *call=linphone_core_get_current_call(lc);
- if (call!=NULL && call->videostream){
- video_stream_set_native_preview_window_id(call->videostream,id);
- }else if (lc->previewstream){
- video_preview_set_native_window_id(lc->previewstream,id);
+ {
+ LinphoneCall *call=linphone_core_get_current_call(lc);
+ if (call!=NULL && call->videostream){
+ video_stream_set_native_preview_window_id(call->videostream,id);
+ }else if (lc->previewstream){
+ video_preview_set_native_window_id(lc->previewstream,id);
+ }
}
#endif
}
@@ -4653,19 +5383,14 @@ void linphone_core_set_native_preview_window_id(LinphoneCore *lc, unsigned long
**/
void linphone_core_show_video(LinphoneCore *lc, bool_t show){
#ifdef VIDEO_ENABLED
- ms_error("linphone_core_show_video %d", show);
LinphoneCall *call=linphone_core_get_current_call(lc);
+ ms_error("linphone_core_show_video %d", show);
if (call!=NULL && call->videostream){
video_stream_show_video(call->videostream,show);
}
#endif
}
-/**
- * Tells the core to use a separate window for local camera preview video, instead of
- * inserting local view within the remote video window.
- *
-**/
void linphone_core_use_preview_window(LinphoneCore *lc, bool_t yesno){
lc->use_preview_window=yesno;
}
@@ -4687,36 +5412,57 @@ int linphone_core_get_device_rotation(LinphoneCore *lc ) {
*
**/
void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation) {
-ms_message("%s : rotation=%d\n", __FUNCTION__, rotation);
+ if (rotation!=lc->device_rotation) ms_message("%s : rotation=%d\n", __FUNCTION__, rotation);
lc->device_rotation = rotation;
#ifdef VIDEO_ENABLED
- LinphoneCall *call=linphone_core_get_current_call(lc);
- if (call!=NULL && call->videostream){
- video_stream_set_device_rotation(call->videostream,rotation);
+ {
+ LinphoneCall *call=linphone_core_get_current_call(lc);
+ if (call!=NULL && call->videostream){
+ video_stream_set_device_rotation(call->videostream,rotation);
+ }
}
#endif
}
-static MSVideoSizeDef supported_resolutions[]={
-#ifdef ENABLE_HD
- { {MS_VIDEO_SIZE_1080P_W,MS_VIDEO_SIZE_1080P_H} , "1080p" },
- { {MS_VIDEO_SIZE_720P_W,MS_VIDEO_SIZE_720P_H} , "1080p" },
+int linphone_core_get_camera_sensor_rotation(LinphoneCore *lc) {
+#ifdef VIDEO_ENABLED
+ LinphoneCall *call = linphone_core_get_current_call(lc);
+ if ((call != NULL) && (call->videostream != NULL)) {
+ return video_stream_get_camera_sensor_rotation(call->videostream);
+ }
#endif
- { {MS_VIDEO_SIZE_SVGA_W,MS_VIDEO_SIZE_SVGA_H} , "svga" },
- { {MS_VIDEO_SIZE_4CIF_W,MS_VIDEO_SIZE_4CIF_H} , "4cif" },
- { {MS_VIDEO_SIZE_VGA_W,MS_VIDEO_SIZE_VGA_H} , "vga" },
- { {MS_VIDEO_SIZE_IOS_MEDIUM_H,MS_VIDEO_SIZE_IOS_MEDIUM_W} , "ios-medium" },
- { {MS_VIDEO_SIZE_CIF_W,MS_VIDEO_SIZE_CIF_H} , "cif" },
- { {MS_VIDEO_SIZE_QVGA_W,MS_VIDEO_SIZE_QVGA_H} , "qvga" },
- { {MS_VIDEO_SIZE_QCIF_W,MS_VIDEO_SIZE_QCIF_H} , "qcif" },
- { {0,0} , NULL }
+ return -1;
+}
+
+static MSVideoSizeDef supported_resolutions[]={
+#if !ANDROID && !TARGET_OS_IPHONE
+ { { MS_VIDEO_SIZE_1080P_W, MS_VIDEO_SIZE_1080P_H } , "1080p" },
+#endif
+#if !ANDROID && !TARGET_OS_MAC /*limit to most common sizes because mac video API cannot list supported resolutions*/
+ { { MS_VIDEO_SIZE_UXGA_W, MS_VIDEO_SIZE_UXGA_H } , "uxga" },
+ { { MS_VIDEO_SIZE_SXGA_MINUS_W, MS_VIDEO_SIZE_SXGA_MINUS_H } , "sxga-" },
+#endif
+ { { MS_VIDEO_SIZE_720P_W, MS_VIDEO_SIZE_720P_H } , "720p" },
+#if !ANDROID && !TARGET_OS_MAC
+ { { MS_VIDEO_SIZE_XGA_W, MS_VIDEO_SIZE_XGA_H } , "xga" },
+#endif
+#if !ANDROID && !TARGET_OS_IPHONE
+ { { MS_VIDEO_SIZE_SVGA_W, MS_VIDEO_SIZE_SVGA_H } , "svga" },
+ { { MS_VIDEO_SIZE_4CIF_W, MS_VIDEO_SIZE_4CIF_H } , "4cif" },
+#endif
+
+ { { MS_VIDEO_SIZE_VGA_W, MS_VIDEO_SIZE_VGA_H } , "vga" },
+#if TARGET_OS_IPHONE
+ { { MS_VIDEO_SIZE_IOS_MEDIUM_H, MS_VIDEO_SIZE_IOS_MEDIUM_W } , "ios-medium" },
+#endif
+ { { MS_VIDEO_SIZE_CIF_W, MS_VIDEO_SIZE_CIF_H } , "cif" },
+#if !TARGET_OS_MAC || TARGET_OS_IPHONE /* OS_MAC is 1 for iPhone, but we need QVGA */
+ { { MS_VIDEO_SIZE_QVGA_W, MS_VIDEO_SIZE_QVGA_H } , "qvga" },
+#endif
+ { { MS_VIDEO_SIZE_QCIF_W, MS_VIDEO_SIZE_QCIF_H } , "qcif" },
+ { { 0,0 } , NULL }
};
-/**
- * Returns the zero terminated table of supported video resolutions.
- *
- * @ingroup media_parameters
-**/
const MSVideoSizeDef *linphone_core_get_supported_video_sizes(LinphoneCore *lc){
return supported_resolutions;
}
@@ -4724,22 +5470,33 @@ const MSVideoSizeDef *linphone_core_get_supported_video_sizes(LinphoneCore *lc){
static MSVideoSize video_size_get_by_name(const char *name){
MSVideoSizeDef *pdef=supported_resolutions;
MSVideoSize null_vsize={0,0};
+ MSVideoSize parsed;
+ if (!name) return null_vsize;
for(;pdef->name!=NULL;pdef++){
if (strcasecmp(name,pdef->name)==0){
return pdef->vsize;
}
}
+ if (sscanf(name,"%ix%i",&parsed.width,&parsed.height)==2){
+ return parsed;
+ }
ms_warning("Video resolution %s is not supported in linphone.",name);
return null_vsize;
}
+/* warning: function not reentrant*/
static const char *video_size_get_name(MSVideoSize vsize){
MSVideoSizeDef *pdef=supported_resolutions;
+ static char customsize[64]={0};
for(;pdef->name!=NULL;pdef++){
if (pdef->vsize.width==vsize.width && pdef->vsize.height==vsize.height){
return pdef->name;
}
}
+ if (vsize.width && vsize.height){
+ snprintf(customsize,sizeof(customsize)-1,"%ix%i",vsize.width,vsize.height);
+ return customsize;
+ }
return NULL;
}
@@ -4749,34 +5506,68 @@ static bool_t video_size_supported(MSVideoSize vsize){
return FALSE;
}
-/**
- * Sets the preferred video size.
- *
- * @ingroup media_parameters
- * This applies only to the stream that is captured and sent to the remote party,
- * since we accept all standard video size on the receive path.
-**/
+static void update_preview_size(LinphoneCore *lc, MSVideoSize oldvsize, MSVideoSize vsize){
+ if (!ms_video_size_equal(oldvsize,vsize) && lc->previewstream!=NULL){
+ toggle_video_preview(lc,FALSE);
+ toggle_video_preview(lc,TRUE);
+ }
+}
+
void linphone_core_set_preferred_video_size(LinphoneCore *lc, MSVideoSize vsize){
if (video_size_supported(vsize)){
- MSVideoSize oldvsize=lc->video_conf.vsize;
- lc->video_conf.vsize=vsize;
- if (!ms_video_size_equal(oldvsize,vsize) && lc->previewstream!=NULL){
- toggle_video_preview(lc,FALSE);
- toggle_video_preview(lc,TRUE);
+ MSVideoSize oldvsize=lc->video_conf.preview_vsize;
+
+ if (oldvsize.width==0){
+ oldvsize=lc->video_conf.vsize;
}
- if ( linphone_core_ready(lc))
+ lc->video_conf.vsize=vsize;
+ update_preview_size(lc,oldvsize,vsize);
+
+ if (linphone_core_ready(lc))
lp_config_set_string(lc->config,"video","size",video_size_get_name(vsize));
}
}
-/**
- * Sets the preferred video size by its name.
- *
- * @ingroup media_parameters
- * This is identical to linphone_core_set_preferred_video_size() except
- * that it takes the name of the video resolution as input.
- * Video resolution names are: qcif, svga, cif, vga, 4cif, svga ...
-**/
+void linphone_core_set_preview_video_size(LinphoneCore *lc, MSVideoSize vsize){
+ MSVideoSize oldvsize;
+ if (vsize.width==0 && vsize.height==0){
+ /*special case to reset the forced preview size mode*/
+ lc->video_conf.preview_vsize=vsize;
+ if (linphone_core_ready(lc))
+ lp_config_set_string(lc->config,"video","preview_size",NULL);
+ return;
+ }
+ oldvsize=lc->video_conf.preview_vsize;
+ lc->video_conf.preview_vsize=vsize;
+ if (!ms_video_size_equal(oldvsize,vsize) && lc->previewstream!=NULL){
+ toggle_video_preview(lc,FALSE);
+ toggle_video_preview(lc,TRUE);
+ }
+ if (linphone_core_ready(lc))
+ lp_config_set_string(lc->config,"video","preview_size",video_size_get_name(vsize));
+}
+
+MSVideoSize linphone_core_get_preview_video_size(const LinphoneCore *lc){
+ return lc->video_conf.preview_vsize;
+}
+
+MSVideoSize linphone_core_get_current_preview_video_size(const LinphoneCore *lc){
+ MSVideoSize ret={0};
+#ifndef VIDEO_ENABLED
+ ms_error("linphone_core_get_current_preview_video_size() fail. Support for video is disabled");
+#else
+ if (lc->previewstream){
+ ret=video_preview_get_current_size(lc->previewstream);
+ }
+#endif
+ return ret;
+}
+
+void linphone_core_set_preview_video_size_by_name(LinphoneCore *lc, const char *name){
+ MSVideoSize vsize=video_size_get_by_name(name);
+ linphone_core_set_preview_video_size(lc,vsize);
+}
+
void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char *name){
MSVideoSize vsize=video_size_get_by_name(name);
MSVideoSize default_vsize={MS_VIDEO_SIZE_CIF_W,MS_VIDEO_SIZE_CIF_H};
@@ -4784,28 +5575,33 @@ void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char
else linphone_core_set_preferred_video_size(lc,default_vsize);
}
-/**
- * Returns the current preferred video size for sending.
- *
- * @ingroup media_parameters
-**/
-MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc){
+MSVideoSize linphone_core_get_preferred_video_size(const LinphoneCore *lc){
return lc->video_conf.vsize;
}
-/**
- * Ask the core to stream audio from and to files, instead of using the soundcard.
-**/
-void linphone_core_use_files(LinphoneCore *lc, bool_t yesno){
+char * linphone_core_get_preferred_video_size_name(const LinphoneCore *lc) {
+ return ms_strdup(video_size_get_name(lc->video_conf.vsize));
+}
+
+void linphone_core_set_preferred_framerate(LinphoneCore *lc, float fps){
+ lc->video_conf.fps=fps;
+ if (linphone_core_ready(lc))
+ lp_config_set_float(lc->config,"video","framerate",fps);
+}
+
+float linphone_core_get_preferred_framerate(LinphoneCore *lc){
+ return lc->video_conf.fps;
+}
+
+
+void linphone_core_set_use_files(LinphoneCore *lc, bool_t yesno){
lc->use_files=yesno;
}
-/**
- * Sets a wav file to be played when putting somebody on hold,
- * or when files are used instead of soundcards (see linphone_core_use_files()).
- *
- * The file must be a 16 bit linear wav file.
-**/
+const char * linphone_core_get_play_file(const LinphoneCore *lc) {
+ return lc->play_file;
+}
+
void linphone_core_set_play_file(LinphoneCore *lc, const char *file){
LinphoneCall *call=linphone_core_get_current_call(lc);
if (lc->play_file!=NULL){
@@ -4814,19 +5610,15 @@ void linphone_core_set_play_file(LinphoneCore *lc, const char *file){
}
if (file!=NULL) {
lc->play_file=ms_strdup(file);
- if (call && call->audiostream && call->audiostream->ms.ticker)
+ if (call && call->audiostream && call->audiostream->ms.state==MSStreamStarted)
audio_stream_play(call->audiostream,file);
}
}
+const char * linphone_core_get_record_file(const LinphoneCore *lc) {
+ return lc->rec_file;
+}
-/**
- * Sets a wav file where incoming stream is to be recorded,
- * when files are used instead of soundcards (see linphone_core_use_files()).
- *
- * This feature is different from call recording (linphone_call_params_set_record_file())
- * The file will be a 16 bit linear wav file.
-**/
void linphone_core_set_record_file(LinphoneCore *lc, const char *file){
LinphoneCall *call=linphone_core_get_current_call(lc);
if (lc->rec_file!=NULL){
@@ -4840,17 +5632,24 @@ void linphone_core_set_record_file(LinphoneCore *lc, const char *file){
}
}
+typedef enum{
+ LinphoneToneGenerator,
+ LinphoneLocalPlayer
+}LinphoneAudioResourceType;
-static MSFilter *get_dtmf_gen(LinphoneCore *lc){
- LinphoneCall *call=linphone_core_get_current_call (lc);
+static MSFilter *get_audio_resource(LinphoneCore *lc, LinphoneAudioResourceType rtype){
+ LinphoneCall *call=linphone_core_get_current_call(lc);
AudioStream *stream=NULL;
+ RingStream *ringstream;
if (call){
stream=call->audiostream;
}else if (linphone_core_is_in_conference(lc)){
stream=lc->conf_ctx.local_participant;
}
if (stream){
- return stream->dtmfgen;
+ if (rtype==LinphoneToneGenerator) return stream->dtmfgen;
+ if (rtype==LinphoneLocalPlayer) return stream->local_player;
+ return NULL;
}
if (lc->ringstream==NULL){
float amp=lp_config_get_float(lc->config,"sound","dtmf_player_amp",0.1);
@@ -4858,14 +5657,21 @@ static MSFilter *get_dtmf_gen(LinphoneCore *lc){
if (ringcard == NULL)
return NULL;
- lc->ringstream=ring_start(NULL,0,ringcard);
+ ringstream=lc->ringstream=ring_start(NULL,0,ringcard);
ms_filter_call_method(lc->ringstream->gendtmf,MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE,&);
lc->dmfs_playing_start_time=time(NULL);
}else{
+ ringstream=lc->ringstream;
if (lc->dmfs_playing_start_time!=0)
lc->dmfs_playing_start_time=time(NULL);
}
- return lc->ringstream->gendtmf;
+ if (rtype==LinphoneToneGenerator) return ringstream->gendtmf;
+ if (rtype==LinphoneLocalPlayer) return ringstream->source;
+ return NULL;
+}
+
+static MSFilter *get_dtmf_gen(LinphoneCore *lc){
+ return get_audio_resource(lc,LinphoneToneGenerator);
}
/**
@@ -4882,28 +5688,87 @@ void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms){
return;
}
- if (duration_ms>0)
+ if (duration_ms > 0)
ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf);
else ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf);
}
/**
- * @ingroup media_parameters
- * Plays a repeated tone to the local user until next further call to #linphone_core_stop_dtmf()
- * @param lc #LinphoneCore
+ * Plays an audio file to the local user.
+ * This function works at any time, during calls, or when no calls are running.
+ * It doesn't request the underlying audio system to support multiple playback streams.
+ * @param lc the linphone core
+ * @param audiofile path to audio file in wav PCM 16 bit format.
+ * @ingroup misc
**/
-void linphone_core_play_tone(LinphoneCore *lc){
- MSFilter *f=get_dtmf_gen(lc);
- MSDtmfGenCustomTone def;
- if (f==NULL){
- ms_error("No dtmf generator at this time !");
- return;
+int linphone_core_play_local(LinphoneCore *lc, const char *audiofile){
+ MSFilter *f=get_audio_resource(lc,LinphoneLocalPlayer);
+ int loopms=-1;
+ if (!f) return -1;
+ ms_filter_call_method(f,MS_PLAYER_SET_LOOP,&loopms);
+ if (ms_filter_call_method(f,MS_PLAYER_OPEN,(void*)audiofile)!=0){
+ return -1;
+ }
+ ms_filter_call_method_noarg(f,MS_PLAYER_START);
+ return 0;
+}
+
+void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID toneid){
+ if (linphone_core_tone_indications_enabled(lc)){
+ const char *audiofile=linphone_core_get_tone_file(lc,toneid);
+ if (!audiofile){
+ MSFilter *f=get_dtmf_gen(lc);
+ MSDtmfGenCustomTone def;
+ if (f==NULL){
+ ms_error("No dtmf generator at this time !");
+ return;
+ }
+ memset(&def,0,sizeof(def));
+ def.amplitude=1;
+ /*these are french tones, excepted the failed one, which is USA congestion tone (does not exist in France)*/
+ switch(toneid){
+ case LinphoneToneCallOnHold:
+ case LinphoneToneCallWaiting:
+ def.duration=300;
+ def.frequencies[0]=440;
+ def.interval=2000;
+ break;
+ case LinphoneToneBusy:
+ def.duration=500;
+ def.frequencies[0]=440;
+ def.interval=500;
+ def.repeat_count=3;
+ break;
+ case LinphoneToneCallLost:
+ def.duration=250;
+ def.frequencies[0]=480;
+ def.frequencies[0]=620;
+ def.interval=250;
+ def.repeat_count=3;
+
+ break;
+ default:
+ ms_warning("Unhandled tone id.");
+ }
+ if (def.duration>0)
+ ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM,&def);
+ }else{
+ linphone_core_play_local(lc,audiofile);
+ }
+ }
+}
+
+void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason){
+ if (linphone_core_tone_indications_enabled(lc)){
+ LinphoneToneDescription *tone=linphone_core_get_call_error_tone(lc,reason);
+ if (tone){
+ if (tone->audiofile){
+ linphone_core_play_local(lc,tone->audiofile);
+ }else if (tone->toneid!=LinphoneToneUndefined){
+ linphone_core_play_named_tone(lc,tone->toneid);
+ }
+ }
}
- def.duration=300;
- def.frequency=500;
- def.amplitude=1;
- def.interval=2000;
- ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM,&def);
}
/**
@@ -4924,7 +5789,7 @@ void linphone_core_stop_dtmf(LinphoneCore *lc){
*
* @ingroup initializing
**/
-void *linphone_core_get_user_data(LinphoneCore *lc){
+void *linphone_core_get_user_data(const LinphoneCore *lc){
return lc->data;
}
@@ -4946,7 +5811,7 @@ int linphone_core_get_mtu(const LinphoneCore *lc){
* Sets the maximum transmission unit size in bytes.
* This information is useful for sending RTP packets.
* Default value is 1500.
- *
+ *
* @ingroup media_parameters
**/
void linphone_core_set_mtu(LinphoneCore *lc, int mtu){
@@ -4961,7 +5826,7 @@ void linphone_core_set_mtu(LinphoneCore *lc, int mtu){
}else ms_set_mtu(0);//use mediastreamer2 default value
}
-void linphone_core_set_waiting_callback(LinphoneCore *lc, LinphoneWaitingCallback cb, void *user_context){
+void linphone_core_set_waiting_callback(LinphoneCore *lc, LinphoneCoreWaitingCallback cb, void *user_context){
lc->wait_cb=cb;
lc->wait_ctx=user_context;
}
@@ -4976,11 +5841,7 @@ void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float
if (lc->wait_cb){
lc->wait_ctx=lc->wait_cb(lc,lc->wait_ctx,LinphoneWaitingProgress,purpose,progress);
}else{
-#ifdef WIN32
- Sleep(50000);
-#else
- usleep(50000);
-#endif
+ ms_usleep(50000);
}
}
@@ -4996,12 +5857,13 @@ void linphone_core_set_rtp_transport_factories(LinphoneCore* lc, LinphoneRtpTran
/**
* Retrieve RTP statistics regarding current call.
+ * @param lc the LinphoneCore
* @param local RTP statistics computed locally.
* @param remote RTP statistics computed by far end (obtained via RTCP feedback).
*
* @note Remote RTP statistics is not implemented yet.
*
- * @returns 0 or -1 if no call is running.
+ * @return 0 or -1 if no call is running.
**/
int linphone_core_get_current_call_stats(LinphoneCore *lc, rtp_stats_t *local, rtp_stats_t *remote){
@@ -5021,7 +5883,11 @@ void net_config_uninit(LinphoneCore *lc)
net_config_t *config=&lc->net_conf;
if (config->stun_server!=NULL){
- ms_free(lc->net_conf.stun_server);
+ ms_free(config->stun_server);
+ }
+ if (config->stun_addrinfo){
+ freeaddrinfo(config->stun_addrinfo);
+ config->stun_addrinfo=NULL;
}
if (config->nat_address!=NULL){
lp_config_set_string(lc->config,"net","nat_address",config->nat_address);
@@ -5039,44 +5905,65 @@ void sip_config_uninit(LinphoneCore *lc)
MSList *elem;
int i;
sip_config_t *config=&lc->sip_conf;
-
+ bool_t still_registered=TRUE;
+
lp_config_set_int(lc->config,"sip","guess_hostname",config->guess_hostname);
lp_config_set_string(lc->config,"sip","contact",config->contact);
lp_config_set_int(lc->config,"sip","inc_timeout",config->inc_timeout);
lp_config_set_int(lc->config,"sip","in_call_timeout",config->in_call_timeout);
lp_config_set_int(lc->config,"sip","delayed_timeout",config->delayed_timeout);
- lp_config_set_int(lc->config,"sip","use_info",config->use_info);
- lp_config_set_int(lc->config,"sip","use_rfc2833",config->use_rfc2833);
lp_config_set_int(lc->config,"sip","use_ipv6",config->ipv6_enabled);
lp_config_set_int(lc->config,"sip","register_only_when_network_is_up",config->register_only_when_network_is_up);
lp_config_set_int(lc->config,"sip","register_only_when_upnp_is_ok",config->register_only_when_upnp_is_ok);
-
+ if (lc->network_reachable) {
+ for(elem=config->proxies;elem!=NULL;elem=ms_list_next(elem)){
+ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
+ _linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */
+ }
- for(elem=config->proxies,i=0;elem!=NULL;elem=ms_list_next(elem),i++){
- LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
- linphone_proxy_config_edit(cfg); /* to unregister */
+ ms_message("Unregistration started.");
+
+ for (i=0;i<20&&still_registered;i++){
+ still_registered=FALSE;
+ sal_iterate(lc->sal);
+ for(elem=config->proxies;elem!=NULL;elem=ms_list_next(elem)){
+ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
+ LinphoneRegistrationState state = linphone_proxy_config_get_state(cfg);
+ still_registered|=(state==LinphoneRegistrationOk||state==LinphoneRegistrationProgress);
+ }
+ ms_usleep(100000);
+ }
+ if (i>=20) ms_warning("Cannot complete unregistration, giving up");
}
+ config->proxies=ms_list_free_with_data(config->proxies,(void (*)(void*)) _linphone_proxy_config_release);
- for (i=0;i<20;i++){
- sal_iterate(lc->sal);
-#ifndef WIN32
- usleep(100000);
-#else
- Sleep(100);
+ config->deleted_proxies=ms_list_free_with_data(config->deleted_proxies,(void (*)(void*)) _linphone_proxy_config_release);
+
+ /*no longuer need to write proxy config if not changedlinphone_proxy_config_write_to_config_file(lc->config,NULL,i);*/ /*mark the end */
+
+ lc->auth_info=ms_list_free_with_data(lc->auth_info,(void (*)(void*))linphone_auth_info_destroy);
+
+ /*now that we are unregisted, we no longer need the tunnel.*/
+#ifdef TUNNEL_ENABLED
+ if (lc->tunnel) {
+ linphone_tunnel_destroy(lc->tunnel);
+ lc->tunnel=NULL;
+ ms_message("Tunnel destroyed.");
+ }
#endif
+
+ sal_reset_transports(lc->sal);
+ sal_unlisten_ports(lc->sal); /*to make sure no new messages are received*/
+ if (lc->http_provider) {
+ belle_sip_object_unref(lc->http_provider);
+ lc->http_provider=NULL;
}
-
- ms_list_for_each(config->proxies,(void (*)(void*)) linphone_proxy_config_destroy);
- ms_list_free(config->proxies);
- config->proxies=NULL;
-
- linphone_proxy_config_write_to_config_file(lc->config,NULL,i); /*mark the end */
-
- ms_list_for_each(lc->auth_info,(void (*)(void*))linphone_auth_info_destroy);
- ms_list_free(lc->auth_info);
- lc->auth_info=NULL;
-
+ if (lc->http_verify_policy){
+ belle_sip_object_unref(lc->http_verify_policy);
+ lc->http_verify_policy=NULL;
+ }
+ sal_iterate(lc->sal); /*make sure event are purged*/
sal_uninit(lc->sal);
lc->sal=NULL;
@@ -5105,6 +5992,9 @@ void rtp_config_uninit(LinphoneCore *lc)
lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout);
lp_config_set_int(lc->config,"rtp","audio_adaptive_jitt_comp_enabled",config->audio_adaptive_jitt_comp_enabled);
lp_config_set_int(lc->config,"rtp","video_adaptive_jitt_comp_enabled",config->video_adaptive_jitt_comp_enabled);
+ ms_free(lc->rtp_conf.audio_multicast_addr);
+ ms_free(lc->rtp_conf.video_multicast_addr);
+ ms_free(config->srtp_suites);
}
static void sound_config_uninit(LinphoneCore *lc)
@@ -5118,7 +6008,7 @@ static void sound_config_uninit(LinphoneCore *lc)
if (config->local_ring) ms_free(config->local_ring);
if (config->remote_ring) ms_free(config->remote_ring);
- ms_snd_card_manager_destroy();
+ lc->tones=ms_list_free_with_data(lc->tones, (void (*)(void*))linphone_tone_description_destroy);
}
static void video_config_uninit(LinphoneCore *lc)
@@ -5168,17 +6058,23 @@ void _linphone_core_codec_config_write(LinphoneCore *lc){
static void codecs_config_uninit(LinphoneCore *lc)
{
_linphone_core_codec_config_write(lc);
- ms_list_free(lc->codecs_conf.audio_codecs);
- ms_list_free(lc->codecs_conf.video_codecs);
+ ms_list_free_with_data(lc->codecs_conf.audio_codecs, (void (*)(void*))payload_type_destroy);
+ ms_list_free_with_data(lc->codecs_conf.video_codecs, (void (*)(void*))payload_type_destroy);
}
void ui_config_uninit(LinphoneCore* lc)
{
+ ms_message("Destroying friends.");
if (lc->friends){
- ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_destroy);
+ ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_unref);
ms_list_free(lc->friends);
lc->friends=NULL;
}
+ if (lc->presence_model) {
+ linphone_presence_model_unref(lc->presence_model);
+ lc->presence_model = NULL;
+ }
+ ms_message("Destroying friends done.");
}
/**
@@ -5189,32 +6085,28 @@ void ui_config_uninit(LinphoneCore* lc)
* sections and pairs of key=value in the configuration file.
*
**/
-LpConfig *linphone_core_get_config(LinphoneCore *lc){
+LpConfig * linphone_core_get_config(LinphoneCore *lc){
return lc->config;
}
+LpConfig * linphone_core_create_lp_config(LinphoneCore *lc, const char *filename) {
+ return lp_config_new(filename);
+}
+
static void linphone_core_uninit(LinphoneCore *lc)
{
- linphone_core_free_hooks(lc);
+ linphone_task_list_free(&lc->hooks);
+ lc->video_conf.show_local = FALSE;
+
while(lc->calls)
{
LinphoneCall *the_call = lc->calls->data;
linphone_core_terminate_call(lc,the_call);
linphone_core_iterate(lc);
-#ifdef WIN32
- Sleep(50000);
-#else
- usleep(50000);
-#endif
+ ms_usleep(50000);
}
-#ifdef BUILD_UPNP
- if(lc->upnp != NULL) {
- linphone_upnp_context_destroy(lc->upnp);
- lc->upnp = NULL;
- }
-#endif //BUILD_UPNP
- if (lc->friends)
+ if (lc->friends) /* FIXME we should wait until subscription to complete*/
ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions);
linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down");
#ifdef VIDEO_ENABLED
@@ -5223,56 +6115,79 @@ static void linphone_core_uninit(LinphoneCore *lc)
lc->previewstream=NULL;
}
#endif
- ms_event_queue_destroy(lc->msevq);
+
lc->msevq=NULL;
/* save all config */
+ ui_config_uninit(lc);
+ sip_config_uninit(lc);
net_config_uninit(lc);
rtp_config_uninit(lc);
- if (lc->ringstream) ring_stop(lc->ringstream);
+ linphone_core_stop_ringing(lc);
sound_config_uninit(lc);
video_config_uninit(lc);
codecs_config_uninit(lc);
- ui_config_uninit(lc);
- sip_config_uninit(lc);
+
+ sip_setup_unregister_all();
+
+#ifdef BUILD_UPNP
+ if(lc->upnp != NULL) {
+ linphone_upnp_context_destroy(lc->upnp);
+ lc->upnp = NULL;
+ }
+#endif //BUILD_UPNP
+
+ ms_list_for_each(lc->chatrooms, (MSIterateFunc)linphone_chat_room_release);
+ lc->chatrooms = ms_list_free(lc->chatrooms);
+
if (lp_config_needs_commit(lc->config)) lp_config_sync(lc->config);
lp_config_destroy(lc->config);
lc->config = NULL; /* Mark the config as NULL to block further calls */
- sip_setup_unregister_all();
- ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy);
+ ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_unref);
lc->call_logs=ms_list_free(lc->call_logs);
-
+
ms_list_for_each(lc->last_recv_msg_ids,ms_free);
lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids);
-
- // Free struct variable
+
if(lc->zrtp_secrets_cache != NULL) {
ms_free(lc->zrtp_secrets_cache);
}
+ if(lc->user_certificates_path != NULL) {
+ ms_free(lc->user_certificates_path);
+ }
if(lc->play_file!=NULL){
ms_free(lc->play_file);
}
if(lc->rec_file!=NULL){
ms_free(lc->rec_file);
}
-
+ if (lc->chat_db_file){
+ ms_free(lc->chat_db_file);
+ }
+ if(lc->presence_model){
+ linphone_presence_model_unref(lc->presence_model);
+ }
linphone_core_free_payload_types(lc);
- ortp_exit();
+ if (lc->supported_formats) ms_free(lc->supported_formats);
+ linphone_core_message_storage_close(lc);
+ ms_exit();
linphone_core_set_state(lc,LinphoneGlobalOff,"Off");
-#ifdef TUNNEL_ENABLED
- if (lc->tunnel) linphone_tunnel_destroy(lc->tunnel);
-#endif
+ linphone_core_deactivate_log_serialization_if_needed();
+ ms_list_free_with_data(lc->vtable_refs,(void (*)(void *))v_table_reference_destroy);
}
static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){
// second get the list of available proxies
const MSList *elem=linphone_core_get_proxy_config_list(lc);
+ if (lc->network_reachable==isReachable) return; // no change, ignore.
+ lc->network_reachable_to_be_notified=TRUE;
ms_message("Network state is now [%s]",isReachable?"UP":"DOWN");
for(;elem!=NULL;elem=elem->next){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
if (linphone_proxy_config_register_enabled(cfg) ) {
if (!isReachable) {
+ linphone_proxy_config_stop_refreshing(cfg);
linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone,"Registration impossible (network down)");
}else{
cfg->commit=TRUE;
@@ -5281,9 +6196,25 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu
}
lc->netup_time=curtime;
lc->network_reachable=isReachable;
- if(!isReachable) {
+
+ if (!lc->network_reachable){
+ linphone_core_invalidate_friend_subscriptions(lc);
sal_reset_transports(lc->sal);
+ }else{
+ linphone_core_resolve_stun_server(lc);
}
+#ifdef BUILD_UPNP
+ if(lc->upnp == NULL) {
+ if(isReachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) {
+ lc->upnp = linphone_upnp_context_new(lc);
+ }
+ } else {
+ if(!isReachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) {
+ linphone_upnp_context_destroy(lc->upnp);
+ lc->upnp = NULL;
+ }
+ }
+#endif
}
void linphone_core_refresh_registers(LinphoneCore* lc) {
@@ -5358,11 +6289,40 @@ bool_t linphone_core_can_we_add_call(LinphoneCore *lc)
return FALSE;
}
+static void notify_soundcard_usage(LinphoneCore *lc, bool_t used){
+ MSSndCard *card=lc->sound_conf.capt_sndcard;
+ if (card && ms_snd_card_get_capabilities(card) & MS_SND_CARD_CAP_IS_SLOW){
+ ms_snd_card_set_usage_hint(card,used);
+ }
+}
+
+void linphone_core_soundcard_hint_check( LinphoneCore* lc){
+ MSList* the_calls = lc->calls;
+ LinphoneCall* call = NULL;
+ bool_t dont_need_sound = TRUE;
+ bool_t use_rtp_io = lp_config_get_int(lc->config, "sound", "rtp_io", FALSE);
+
+ /* check if the remaining calls are paused */
+ while( the_calls ){
+ call = the_calls->data;
+ if( call->state != LinphoneCallPausing && call->state != LinphoneCallPaused ){
+ dont_need_sound = FALSE;
+ break;
+ }
+ the_calls = the_calls->next;
+ }
+
+ /* if no more calls or all calls are paused, we can free the soundcard */
+ if ( (lc->calls==NULL || dont_need_sound) && !lc->use_files && !use_rtp_io){
+ ms_message("Notifying soundcard that we don't need it anymore for calls.");
+ notify_soundcard_usage(lc,FALSE);
+ }
+}
int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call)
{
- if(linphone_core_can_we_add_call(lc))
- {
+ if (linphone_core_can_we_add_call(lc)){
+ if (lc->calls==NULL) notify_soundcard_usage(lc,TRUE);
lc->calls = ms_list_append(lc->calls,call);
return 0;
}
@@ -5385,12 +6345,10 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call)
return -1;
}
lc->calls = the_calls;
+
return 0;
}
-/**
- * Specifiies a ring back tone to be played to far end during incoming calls.
-**/
void linphone_core_set_remote_ringback_tone(LinphoneCore *lc, const char *file){
if (lc->sound_conf.ringback_tone){
ms_free(lc->sound_conf.ringback_tone);
@@ -5400,29 +6358,20 @@ void linphone_core_set_remote_ringback_tone(LinphoneCore *lc, const char *file){
lc->sound_conf.ringback_tone=ms_strdup(file);
}
-/**
- * Returns the ring back tone played to far end during incoming calls.
-**/
const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc){
return lc->sound_conf.ringback_tone;
}
-static PayloadType* find_payload_type_from_list(const char* type, int rate, int channels, const MSList* from) {
- const MSList *elem;
- for(elem=from;elem!=NULL;elem=elem->next){
- PayloadType *pt=(PayloadType*)elem->data;
- if ((strcasecmp((char*)type, payload_type_get_mime(pt)) == 0)
- && (rate == LINPHONE_FIND_PAYLOAD_IGNORE_RATE || rate==pt->clock_rate)
- && (channels == LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS || channels==pt->channels)) {
- return pt;
- }
- }
- return NULL;
+void linphone_core_set_ring_during_incoming_early_media(LinphoneCore *lc, bool_t enable) {
+ lp_config_set_int(lc->config, "sound", "ring_during_incoming_early_media", (int)enable);
}
+bool_t linphone_core_get_ring_during_incoming_early_media(const LinphoneCore *lc) {
+ return (bool_t)lp_config_get_int(lc->config, "sound", "ring_during_incoming_early_media", 0);
+}
-PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) {
- PayloadType* result = find_payload_type_from_list(type, rate, channels, linphone_core_get_audio_codecs(lc));
+LinphonePayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) {
+ LinphonePayloadType* result = find_payload_type_from_list(type, rate, channels, linphone_core_get_audio_codecs(lc));
if (result) {
return result;
} else {
@@ -5435,6 +6384,21 @@ PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type,
return NULL;
}
+const char* linphone_configuring_state_to_string(LinphoneConfiguringState cs){
+ switch(cs){
+ case LinphoneConfiguringSuccessful:
+ return "LinphoneConfiguringSuccessful";
+ break;
+ case LinphoneConfiguringFailed:
+ return "LinphoneConfiguringFailed";
+ break;
+ case LinphoneConfiguringSkipped:
+ return "LinphoneConfiguringSkipped";
+ break;
+ }
+ return NULL;
+}
+
const char *linphone_global_state_to_string(LinphoneGlobalState gs){
switch(gs){
case LinphoneGlobalOff:
@@ -5448,6 +6412,8 @@ const char *linphone_global_state_to_string(LinphoneGlobalState gs){
break;
case LinphoneGlobalShutdown:
return "LinphoneGlobalShutdown";
+ case LinphoneGlobalConfiguring:
+ return "LinphoneGlobalConfiguring";
break;
}
return NULL;
@@ -5458,18 +6424,32 @@ LinphoneGlobalState linphone_core_get_global_state(const LinphoneCore *lc){
}
LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc){
- LinphoneCallParams *p=ms_new0(LinphoneCallParams,1);
+ LinphoneCallParams *p=linphone_call_params_new();
linphone_core_init_default_params(lc, p);
return p;
}
+/**
+ * Create a LinphoneCallParams suitable for linphone_core_invite_with_params(), linphone_core_accept_call_with_params(), linphone_core_accept_early_media_with_params(),
+ * linphone_core_accept_call_update().
+ * The parameters are initialized according to the current LinphoneCore configuration and the current state of the LinphoneCall.
+ * @param lc the LinphoneCore
+ * @param call the call for which the parameters are to be build, or NULL in the case where the parameters are to be used for a new outgoing call.
+ * @return a new LinphoneCallParams
+ * @ingroup call_control
+ */
+LinphoneCallParams *linphone_core_create_call_params(LinphoneCore *lc, LinphoneCall *call){
+ if (!call) return linphone_core_create_default_call_parameters(lc);
+ return linphone_call_params_copy(call->params);
+}
+
const char *linphone_reason_to_string(LinphoneReason err){
switch(err){
case LinphoneReasonNone:
return "No error";
case LinphoneReasonNoResponse:
return "No response";
- case LinphoneReasonBadCredentials:
+ case LinphoneReasonForbidden:
return "Bad credentials";
case LinphoneReasonDeclined:
return "Call declined";
@@ -5479,6 +6459,34 @@ const char *linphone_reason_to_string(LinphoneReason err){
return "Not answered";
case LinphoneReasonBusy:
return "Busy";
+ case LinphoneReasonMedia:
+ return "Incompatible media capabilities";
+ case LinphoneReasonIOError:
+ return "IO error";
+ case LinphoneReasonDoNotDisturb:
+ return "Do not distrub";
+ case LinphoneReasonUnauthorized:
+ return "Unauthorized";
+ case LinphoneReasonNotAcceptable:
+ return "Not acceptable here";
+ case LinphoneReasonNoMatch:
+ return "No match";
+ case LinphoneReasonMovedPermanently:
+ return "Moved permanently";
+ case LinphoneReasonGone:
+ return "Gone";
+ case LinphoneReasonTemporarilyUnavailable:
+ return "Temporarily unavailable";
+ case LinphoneReasonAddressIncomplete:
+ return "Address incomplete";
+ case LinphoneReasonNotImplemented:
+ return "Not implemented";
+ case LinphoneReasonBadGateway:
+ return "Bad gateway";
+ case LinphoneReasonServerTimeout:
+ return "Server timeout";
+ case LinphoneReasonUnknown:
+ return "Unknown error";
}
return "unknown error";
}
@@ -5514,10 +6522,31 @@ void linphone_core_start_dtmf_stream(LinphoneCore* lc) {
lc->ringstream_autorelease=FALSE; /*disable autorelease mode*/
}
-void linphone_core_stop_dtmf_stream(LinphoneCore* lc) {
- if (lc->ringstream && lc->dmfs_playing_start_time!=0) {
+/**
+ * Whenever the liblinphone is playing a ring to advertise an incoming call or ringback of an outgoing call, this function stops
+ * the ringing. Typical use is to stop ringing when the user requests to ignore the call.
+ *
+ * @param lc The LinphoneCore object
+ *
+ * @ingroup media_parameters
+**/
+void linphone_core_stop_ringing(LinphoneCore* lc) {
+ LinphoneCall *call=linphone_core_get_current_call(lc);
+ if (lc->ringstream) {
ring_stop(lc->ringstream);
lc->ringstream=NULL;
+ lc->dmfs_playing_start_time=0;
+ lc->ringstream_autorelease=TRUE;
+ }
+ if (call && call->ringing_beep){
+ linphone_core_stop_dtmf(lc);
+ call->ringing_beep=FALSE;
+ }
+}
+
+void linphone_core_stop_dtmf_stream(LinphoneCore* lc) {
+ if (lc->dmfs_playing_start_time!=0) {
+ linphone_core_stop_ringing(lc);
}
}
@@ -5528,47 +6557,18 @@ void linphone_core_set_max_calls(LinphoneCore *lc, int max) {
lc->max_calls=max;
}
-typedef struct Hook{
- LinphoneCoreIterateHook fun;
- void *data;
-}Hook;
-
-static Hook *hook_new(LinphoneCoreIterateHook hook, void *hook_data){
- Hook *h=ms_new(Hook,1);
- h->fun=hook;
- h->data=hook_data;
- return h;
-}
-
-static void hook_invoke(Hook *h){
- h->fun(h->data);
-}
void linphone_core_add_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data){
- lc->hooks=ms_list_append(lc->hooks,hook_new(hook,hook_data));
+ linphone_task_list_add(&lc->hooks, hook, hook_data);
}
static void linphone_core_run_hooks(LinphoneCore *lc){
- ms_list_for_each(lc->hooks,(void (*)(void*))hook_invoke);
-}
-
-static void linphone_core_free_hooks(LinphoneCore *lc){
- ms_list_for_each(lc->hooks,(void (*)(void*))ms_free);
- ms_list_free(lc->hooks);
- lc->hooks=NULL;
+ linphone_task_list_run(&lc->hooks);
}
void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data){
- MSList *elem;
- for(elem=lc->hooks;elem!=NULL;elem=elem->next){
- Hook *h=(Hook*)elem->data;
- if (h->fun==hook && h->data==hook_data){
- lc->hooks = ms_list_remove_link(lc->hooks,elem);
- ms_free(h);
- return;
- }
- }
- ms_error("linphone_core_remove_iterate_hook(): No such hook found.");
+ linphone_task_list_remove(&lc->hooks, hook, hook_data);
+
}
void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file){
@@ -5582,9 +6582,21 @@ const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc){
return lc->zrtp_secrets_cache;
}
-const LinphoneCall* linphone_core_find_call_from_uri(LinphoneCore *lc, const char *uri) {
+void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path){
+ char* new_value;
+ new_value = path?ms_strdup(path):NULL;
+ if (lc->user_certificates_path) ms_free(lc->user_certificates_path);
+ lp_config_set_string(lc->config,"misc","user_certificates_path",lc->user_certificates_path=new_value);
+ return ;
+}
+
+const char *linphone_core_get_user_certificates_path(LinphoneCore *lc){
+ return lc->user_certificates_path;
+}
+
+LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri) {
MSList *calls;
- const LinphoneCall *c;
+ LinphoneCall *c;
const LinphoneAddress *address;
char *current_uri;
@@ -5613,10 +6625,9 @@ const LinphoneCall* linphone_core_find_call_from_uri(LinphoneCore *lc, const cha
* @param lc The LinphoneCore
**/
bool_t linphone_core_sound_resources_locked(LinphoneCore *lc){
- MSList *calls=lc->calls;
- while(calls) {
- LinphoneCall *c=(LinphoneCall*)calls->data;
- calls=calls->next;
+ MSList *elem;
+ for(elem=lc->calls;elem!=NULL;elem=elem->next) {
+ LinphoneCall *c=(LinphoneCall*)elem->data;
switch (c->state) {
case LinphoneCallOutgoingInit:
case LinphoneCallOutgoingProgress:
@@ -5626,6 +6637,7 @@ bool_t linphone_core_sound_resources_locked(LinphoneCore *lc){
case LinphoneCallRefered:
case LinphoneCallIncomingEarlyMedia:
case LinphoneCallUpdating:
+ ms_message("Call %p is locking sound resources.",c);
return TRUE;
default:
break;
@@ -5638,36 +6650,75 @@ void linphone_core_set_srtp_enabled(LinphoneCore *lc, bool_t enabled) {
lp_config_set_int(lc->config,"sip","srtp",(int)enabled);
}
-/**
- * Returns whether a media encryption scheme is supported by the LinphoneCore engine
-**/
+const char *linphone_media_encryption_to_string(LinphoneMediaEncryption menc){
+ switch(menc){
+ case LinphoneMediaEncryptionSRTP:
+ return "LinphoneMediaEncryptionSRTP";
+ case LinphoneMediaEncryptionDTLS:
+ return "LinphoneMediaEncryptionDTLS";
+ case LinphoneMediaEncryptionZRTP:
+ return "LinphoneMediaEncryptionZRTP";
+ case LinphoneMediaEncryptionNone:
+ return "LinphoneMediaEncryptionNone";
+ }
+ ms_error("Invalid LinphoneMediaEncryption value %i",(int)menc);
+ return "INVALID";
+}
+
+
bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, LinphoneMediaEncryption menc){
switch(menc){
case LinphoneMediaEncryptionSRTP:
- return ortp_srtp_supported();
+ return ms_srtp_supported();
+ case LinphoneMediaEncryptionDTLS:
+ return ms_dtls_srtp_available();
case LinphoneMediaEncryptionZRTP:
- return ortp_zrtp_available();
+ return ms_zrtp_available();
case LinphoneMediaEncryptionNone:
return TRUE;
}
return FALSE;
}
-int linphone_core_set_media_encryption(LinphoneCore *lc, enum LinphoneMediaEncryption menc) {
+int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption menc) {
const char *type="none";
- int ret=0;
- if (menc == LinphoneMediaEncryptionSRTP){
- if (!ortp_srtp_supported()){
- ms_warning("SRTP not supported by library.");
- type="none";
- ret=-1;
- }else type="srtp";
- }else if (menc == LinphoneMediaEncryptionZRTP){
- if (!ortp_zrtp_available()){
- ms_warning("ZRTP not supported by library.");
- type="none";
- ret=-1;
- }else type="zrtp";
+ int ret=-1;
+
+ switch(menc){
+ case LinphoneMediaEncryptionSRTP:
+ if (!ms_srtp_supported()){
+ ms_warning("SRTP not supported by library.");
+ type="none";
+ ret=-1;
+ }else{
+ type="srtp";
+ ret = 0;
+ }
+ break;
+ case LinphoneMediaEncryptionZRTP:
+ if (!ms_zrtp_available()){
+ ms_warning("ZRTP not supported by library.");
+ type="none";
+ ret=-1;
+ }else {
+ type="zrtp";
+ ret = 0;
+ }
+ break;
+ case LinphoneMediaEncryptionDTLS:
+ if (!ms_dtls_srtp_available()){
+ ms_warning("DTLS not supported by library.");
+ type="none";
+ ret=-1;
+ }else {
+ type="dtls";
+ ret = 0;
+ }
+ break;
+ case LinphoneMediaEncryptionNone:
+ type = "none";
+ ret = 0;
+ break;
}
lp_config_set_string(lc->config,"sip","media_encryption",type);
return ret;
@@ -5675,11 +6726,13 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, enum LinphoneMediaEncry
LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc) {
const char* menc = lp_config_get_string(lc->config, "sip", "media_encryption", NULL);
-
+
if (menc == NULL)
return LinphoneMediaEncryptionNone;
else if (strcmp(menc, "srtp")==0)
return LinphoneMediaEncryptionSRTP;
+ else if (strcmp(menc, "dtls")==0)
+ return LinphoneMediaEncryptionDTLS;
else if (strcmp(menc, "zrtp")==0)
return LinphoneMediaEncryptionZRTP;
else
@@ -5698,6 +6751,13 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para
params->has_video=linphone_core_video_enabled(lc) && lc->video_policy.automatically_initiate;
params->media_encryption=linphone_core_get_media_encryption(lc);
params->in_conference=FALSE;
+ params->privacy=LinphonePrivacyDefault;
+ params->avpf_enabled=FALSE;
+ params->audio_dir=LinphoneMediaDirectionSendRecv;
+ params->video_dir=LinphoneMediaDirectionSendRecv;
+ params->real_early_media=lp_config_get_int(lc->config,"misc","real_early_media",FALSE);
+ params->audio_multicast_enabled=linphone_core_audio_multicast_enabled(lc);
+ params->video_multicast_enabled=linphone_core_video_multicast_enabled(lc);
}
void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) {
@@ -5710,25 +6770,25 @@ const char* linphone_core_get_device_identifier(const LinphoneCore *lc) {
/**
* Set the DSCP field for SIP signaling channel.
- *
+ *
* @ingroup network_parameters
* * The DSCP defines the quality of service in IP packets.
- *
+ *
**/
void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp){
sal_set_dscp(lc->sal,dscp);
if (linphone_core_ready(lc)){
lp_config_set_int_hex(lc->config,"sip","dscp",dscp);
- apply_transports(lc);
+ _linphone_core_apply_transports(lc);
}
}
/**
* Get the DSCP field for SIP signaling channel.
- *
+ *
* @ingroup network_parameters
* * The DSCP defines the quality of service in IP packets.
- *
+ *
**/
int linphone_core_get_sip_dscp(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"sip","dscp",0x1a);
@@ -5739,7 +6799,7 @@ int linphone_core_get_sip_dscp(const LinphoneCore *lc){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
- *
+ *
**/
void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp){
if (linphone_core_ready(lc))
@@ -5751,7 +6811,7 @@ void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
- *
+ *
**/
int linphone_core_get_audio_dscp(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"rtp","audio_dscp",0x2e);
@@ -5762,12 +6822,12 @@ int linphone_core_get_audio_dscp(const LinphoneCore *lc){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
- *
+ *
**/
void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){
if (linphone_core_ready(lc))
lp_config_set_int_hex(lc->config,"rtp","video_dscp",dscp);
-
+
}
/**
@@ -5775,8 +6835,253 @@ void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
- *
+ *
**/
int linphone_core_get_video_dscp(const LinphoneCore *lc){
- return lp_config_get_int(lc->config,"rtp","video_dscp",0x2e);
+ return lp_config_get_int(lc->config,"rtp","video_dscp",0);
+}
+
+
+/**
+ * Sets the database filename where chat messages will be stored.
+ * If the file does not exist, it will be created.
+ * @ingroup initializing
+ * @param lc the linphone core
+ * @param path filesystem path
+**/
+void linphone_core_set_chat_database_path(LinphoneCore *lc, const char *path){
+ if (lc->chat_db_file){
+ ms_free(lc->chat_db_file);
+ lc->chat_db_file=NULL;
+ }
+ if (path) {
+ lc->chat_db_file=ms_strdup(path);
+ linphone_core_message_storage_init(lc);
+ }
+}
+void linphone_core_enable_sdp_200_ack(LinphoneCore *lc, bool_t enable) {
+ lp_config_set_int(lc->config,"sip","sdp_200_ack",lc->sip_conf.sdp_200_ack=enable);
+}
+bool_t linphone_core_sdp_200_ack_enabled(const LinphoneCore *lc) {
+ return lc->sip_conf.sdp_200_ack!=0;
+}
+
+void linphone_core_set_file_transfer_server(LinphoneCore *core, const char * server_url) {
+ lp_config_set_string(core->config, "misc", "file_transfer_server_url", server_url);
+}
+
+const char * linphone_core_get_file_transfer_server(LinphoneCore *core) {
+ return lp_config_get_string(core->config, "misc", "file_transfer_server_url", NULL);
+}
+
+/**
+ * This function controls signaling features supported by the core.
+ * They are typically included in a SIP Supported header.
+ * @param lc the LinphoneCore
+ * @param tag the feature tag name
+ * @ingroup initializing
+**/
+void linphone_core_add_supported_tag(LinphoneCore *lc, const char *tag){
+ sal_add_supported_tag(lc->sal,tag);
+ lp_config_set_string(lc->config,"sip","supported",sal_get_supported_tags(lc->sal));
+}
+
+/**
+ * Remove a supported tag. @see linphone_core_add_supported_tag()
+ * @param lc the LinphoneCore
+ * @param tag the tag to remove
+ * @ingroup initializing
+**/
+void linphone_core_remove_supported_tag(LinphoneCore *lc, const char *tag){
+ sal_remove_supported_tag(lc->sal,tag);
+ lp_config_set_string(lc->config,"sip","supported",sal_get_supported_tags(lc->sal));
+}
+
+/**
+ * Enable RTCP feedback (also known as RTP/AVPF profile).
+ * Setting LinphoneAVPFDefault is equivalent to LinphoneAVPFDisabled.
+ * This setting can be overriden per LinphoneProxyConfig with linphone_proxy_config_set_avpf_mode().
+ * The value set here is used for calls placed or received out of any proxy configured, or if the proxy config is configured with LinphoneAVPFDefault.
+ * @param lc the LinphoneCore
+ * @param mode the mode.
+ * @ingroup media_parameters
+**/
+void linphone_core_set_avpf_mode(LinphoneCore *lc, LinphoneAVPFMode mode){
+ if (mode==LinphoneAVPFDefault) mode=LinphoneAVPFDisabled;
+ lc->rtp_conf.avpf_mode=mode;
+ if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"rtp","avpf",mode);
+}
+
+/**
+ * Return AVPF enablement. See linphone_core_set_avpf_mode() .
+ * @param lc the core
+ * @return the avpf enablement mode.
+ * @ingroup media_parameters
+**/
+LinphoneAVPFMode linphone_core_get_avpf_mode(const LinphoneCore *lc){
+ return lc->rtp_conf.avpf_mode;
+}
+
+/**
+ * Return the avpf report interval in seconds.
+ * @param lc the LinphoneCore
+ * @return the avpf report interval in seconds.
+ * @ingroup media_parameters
+**/
+int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc){
+ return lp_config_get_int(lc->config,"rtp","avpf_rr_interval",5);
+}
+
+/**
+ * Set the avpf report interval in seconds.
+ * This value can be overriden by the proxy config using linphone_proxy_config_set_avpf_rr_interval().
+ * @param lc the core
+ * @param interval interval in seconds.
+ * @ingroup media_parameters
+**/
+void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int interval){
+ lp_config_set_int(lc->config,"rtp","avpf_rr_interval",interval);
+}
+
+int linphone_payload_type_get_type(const LinphonePayloadType *pt) {
+ return pt->type;
+}
+
+int linphone_payload_type_get_normal_bitrate(const LinphonePayloadType *pt) {
+ return pt->normal_bitrate;
+}
+
+const char * linphone_payload_type_get_mime_type(const LinphonePayloadType *pt) {
+ return pt->mime_type;
+}
+
+int linphone_payload_type_get_channels(const LinphonePayloadType *pt) {
+ return pt->channels;
+}
+
+int linphone_core_set_audio_multicast_addr(LinphoneCore *lc, const char* ip) {
+ char* new_value;
+ if (ip && !ms_is_multicast(ip)) {
+ ms_error("Cannot set multicast audio addr to core [%p] because [%s] is not multicast",lc,ip);
+ return -1;
+ }
+ new_value = ip?ms_strdup(ip):NULL;
+ if (lc->rtp_conf.audio_multicast_addr) ms_free(lc->rtp_conf.audio_multicast_addr);
+ lp_config_set_string(lc->config,"rtp","audio_multicast_addr",lc->rtp_conf.audio_multicast_addr=new_value);
+ return 0;
+}
+
+int linphone_core_set_video_multicast_addr(LinphoneCore *lc, const char* ip) {
+ char* new_value;
+ if (ip && !ms_is_multicast(ip)) {
+ ms_error("Cannot set multicast video addr to core [%p] because [%s] is not multicast",lc,ip);
+ return -1;
+ }
+ new_value = ip?ms_strdup(ip):NULL;
+ if (lc->rtp_conf.video_multicast_addr) ms_free(lc->rtp_conf.video_multicast_addr);
+ lp_config_set_string(lc->config,"rtp","video_multicast_addr",lc->rtp_conf.video_multicast_addr=new_value);
+ return 0;
+}
+
+const char* linphone_core_get_audio_multicast_addr(const LinphoneCore *lc) {
+ return lc->rtp_conf.audio_multicast_addr;
+}
+
+const char* linphone_core_get_video_multicast_addr(const LinphoneCore *lc){
+ return lc->rtp_conf.video_multicast_addr;
+}
+
+int linphone_core_set_audio_multicast_ttl(LinphoneCore *lc, int ttl) {
+ if (ttl>255) {
+ ms_error("Cannot set multicast audio ttl to core [%p] to [%i] value must be <256",lc,ttl);
+ return -1;
+ }
+
+ lp_config_set_int(lc->config,"rtp","audio_multicast_ttl",lc->rtp_conf.audio_multicast_ttl=ttl);
+ return 0;
+}
+
+int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int ttl) {
+ if (ttl>255) {
+ ms_error("Cannot set multicast video ttl to core [%p] to [%i] value must be <256",lc,ttl);
+ return -1;
+ }
+
+ lp_config_set_int(lc->config,"rtp","video_multicast_ttl",lc->rtp_conf.video_multicast_ttl=ttl);
+ return 0;
+}
+
+int linphone_core_get_audio_multicast_ttl(const LinphoneCore *lc) {
+ return lc->rtp_conf.audio_multicast_ttl;
+}
+
+
+int linphone_core_get_video_multicast_ttl(const LinphoneCore *lc){
+ return lc->rtp_conf.video_multicast_ttl;
+}
+
+void linphone_core_enable_audio_multicast(LinphoneCore *lc, bool_t yesno) {
+ lp_config_set_int(lc->config,"rtp","audio_multicast_enabled",lc->rtp_conf.audio_multicast_enabled=yesno);
+}
+
+ bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *lc) {
+ return lc->rtp_conf.audio_multicast_enabled;
+}
+
+void linphone_core_enable_video_multicast(LinphoneCore *lc, bool_t yesno) {
+ lp_config_set_int(lc->config,"rtp","video_multicast_enabled",lc->rtp_conf.video_multicast_enabled=yesno);
+}
+
+bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) {
+ return lc->rtp_conf.video_multicast_enabled;
+}
+
+void linphone_core_set_video_preset(LinphoneCore *lc, const char *preset) {
+ lp_config_set_string(lc->config, "video", "preset", preset);
+}
+
+const char * linphone_core_get_video_preset(const LinphoneCore *lc) {
+ return lp_config_get_string(lc->config, "video", "preset", NULL);
+}
+
+#ifdef ANDROID
+static int linphone_core_call_void_method(jobject obj, jmethodID id) {
+ JNIEnv *env=ms_get_jni_env();
+ if (env && obj) {
+ (*env)->CallVoidMethod(env,obj,id);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionClear(env);
+ return -1;
+ } else
+ return 0;
+ } else
+ return -1;
+}
+
+void linphone_core_wifi_lock_acquire(LinphoneCore *lc) {
+ if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_acquire_id))
+ ms_warning("No wifi lock configured or not usable for core [%p]",lc);
+}
+void linphone_core_wifi_lock_release(LinphoneCore *lc) {
+ if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_release_id))
+ ms_warning("No wifi lock configured or not usable for core [%p]",lc);
+}
+void linphone_core_multicast_lock_acquire(LinphoneCore *lc) {
+ if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_acquire_id))
+ ms_warning("No multicast lock configured or not usable for core [%p]",lc);
+}
+void linphone_core_multicast_lock_release(LinphoneCore *lc) {
+ if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_release_id))
+ ms_warning("No wifi lock configured or not usable for core [%p]",lc);
+}
+#endif
+
+
+LINPHONE_PUBLIC const char *linphone_core_log_collection_upload_state_to_string(const LinphoneCoreLogCollectionUploadState lcus) {
+ switch (lcus) {
+ case LinphoneCoreLogCollectionUploadStateInProgress : return "LinphoneCoreLogCollectionUploadStateInProgress";
+ case LinphoneCoreLogCollectionUploadStateDelivered : return "LinphoneCoreLogCollectionUploadStateDelivered";
+ case LinphoneCoreLogCollectionUploadStateNotDelivered : return "LinphoneCoreLogCollectionUploadStateNotDelivered";
+ }
+ return "UNKNOWN";
}
diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h
index 2012492c4..cdc275bb1 100644
--- a/coreapi/linphonecore.h
+++ b/coreapi/linphonecore.h
@@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ortp/payloadtype.h"
#include "mediastreamer2/mscommon.h"
#include "mediastreamer2/msvideo.h"
+#include "mediastreamer2/mediastream.h"
+#include "mediastreamer2/bitratecontrol.h"
#ifdef IN_LINPHONE
#include "sipsetup.h"
@@ -30,13 +32,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "linphone/sipsetup.h"
#endif
+#include "lpconfig.h"
+
#define LINPHONE_IPADDR_SIZE 64
#define LINPHONE_HOSTNAME_SIZE 128
-#ifdef LIBLINPHONE_EXPORTS
-#define LINPHONE_PUBLIC __declspec(dllexport)
-#else
-#define LINPHONE_PUBLIC __declspec(dllimport)
+#ifndef LINPHONE_PUBLIC
+ #define LINPHONE_PUBLIC MS2_PUBLIC
#endif
#ifdef __cplusplus
@@ -50,17 +52,62 @@ struct _LinphoneCore;
*/
typedef struct _LinphoneCore LinphoneCore;
-struct _LpConfig;
+/**
+ * Disable a sip transport
+ * Use with #LCSipTransports
+ * @ingroup initializing
+ */
+#define LC_SIP_TRANSPORT_DISABLED 0
+/**
+ * Randomly chose a sip port for this transport
+ * Use with #LCSipTransports
+ * @ingroup initializing
+ */
+#define LC_SIP_TRANSPORT_RANDOM -1
-struct _LCSipTransports{
+/**
+ * Linphone core SIP transport ports.
+ * Use with #linphone_core_set_sip_transports
+ * @ingroup initializing
+ */
+typedef struct _LCSipTransports{
+ /**
+ * udp port to listening on, negative value if not set
+ * */
int udp_port;
+ /**
+ * tcp port to listening on, negative value if not set
+ * */
int tcp_port;
+ /**
+ * dtls port to listening on, negative value if not set
+ * */
int dtls_port;
+ /**
+ * tls port to listening on, negative value if not set
+ * */
int tls_port;
-};
+} LCSipTransports;
-typedef struct _LCSipTransports LCSipTransports;
+
+/**
+ * Enum describing transport type for LinphoneAddress.
+ * @ingroup linphone_address
+**/
+enum _LinphoneTransportType{
+ LinphoneTransportUdp,
+ LinphoneTransportTcp,
+ LinphoneTransportTls,
+ LinphoneTransportDtls
+};
+/*this enum MUST be kept in sync with the SalTransport from sal.h*/
+
+/**
+ * Typedef for transport type enum.
+ * @ingroup linphone_address
+**/
+typedef enum _LinphoneTransportType LinphoneTransportType;
/**
* Object that represents a SIP address.
@@ -74,175 +121,10 @@ typedef struct _LCSipTransports LCSipTransports;
* return NULL.
*
* @ingroup linphone_address
- * @var LinphoneAddress
*/
typedef struct SalAddress LinphoneAddress;
-#ifdef IN_LINPHONE
-#include "linphonefriend.h"
-#else
-#include "linphone/linphonefriend.h"
-#endif
-LINPHONE_PUBLIC LinphoneAddress * linphone_address_new(const char *uri);
-LinphoneAddress * linphone_address_clone(const LinphoneAddress *uri);
-const char *linphone_address_get_scheme(const LinphoneAddress *u);
-LINPHONE_PUBLIC const char *linphone_address_get_display_name(const LinphoneAddress* u);
-LINPHONE_PUBLIC const char *linphone_address_get_username(const LinphoneAddress *u);
-LINPHONE_PUBLIC const char *linphone_address_get_domain(const LinphoneAddress *u);
-/**
- * Get port number as an integer value.
- *
- */
-int linphone_address_get_port_int(const LinphoneAddress *u);
-/**
- * Get port number, null if not present.
- */
-const char* linphone_address_get_port(const LinphoneAddress *u);
-LINPHONE_PUBLIC void linphone_address_set_display_name(LinphoneAddress *u, const char *display_name);
-LINPHONE_PUBLIC void linphone_address_set_username(LinphoneAddress *uri, const char *username);
-LINPHONE_PUBLIC void linphone_address_set_domain(LinphoneAddress *uri, const char *host);
-void linphone_address_set_port(LinphoneAddress *uri, const char *port);
-void linphone_address_set_port_int(LinphoneAddress *uri, int port);
-/*remove tags, params etc... so that it is displayable to the user*/
-LINPHONE_PUBLIC void linphone_address_clean(LinphoneAddress *uri);
-LINPHONE_PUBLIC char *linphone_address_as_string(const LinphoneAddress *u);
-char *linphone_address_as_string_uri_only(const LinphoneAddress *u);
-LINPHONE_PUBLIC bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2);
-LINPHONE_PUBLIC void linphone_address_destroy(LinphoneAddress *u);
-
-struct _SipSetupContext;
-
-
-/**
- * Enum representing the direction of a call.
- * @ingroup call_logs
-**/
-enum _LinphoneCallDir {
- LinphoneCallOutgoing, /**< outgoing calls*/
- LinphoneCallIncoming /**< incoming calls*/
-};
-
-/**
- * Typedef for enum
- * @ingroup call_logs
-**/
-typedef enum _LinphoneCallDir LinphoneCallDir;
-
-/**
- * Enum representing the status of a call
- * @ingroup call_logs
-**/
-typedef enum _LinphoneCallStatus {
- LinphoneCallSuccess, /**< The call was sucessful*/
- LinphoneCallAborted, /**< The call was aborted */
- LinphoneCallMissed, /**< The call was missed (unanswered)*/
- LinphoneCallDeclined /**< The call was declined, either locally or by remote end*/
-} LinphoneCallStatus;
-
-/**
- * Structure representing a call log.
- *
- * @ingroup call_logs
- *
-**/
-typedef struct _LinphoneCallLog LinphoneCallLog;
-
-/**
- * Enum describing type of media encryption types.
-**/
-enum LinphoneMediaEncryption {
- LinphoneMediaEncryptionNone,
- LinphoneMediaEncryptionSRTP,
- LinphoneMediaEncryptionZRTP
-};
-
-/**
- * Enum describing type of media encryption types.
-**/
-typedef enum LinphoneMediaEncryption LinphoneMediaEncryption;
-
-/*public: */
-LinphoneAddress *linphone_call_log_get_from(LinphoneCallLog *cl);
-LinphoneAddress *linphone_call_log_get_to(LinphoneCallLog *cl);
-LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl);
-LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl);
-LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl);
-time_t linphone_call_log_get_start_date(LinphoneCallLog *cl);
-int linphone_call_log_get_duration(LinphoneCallLog *cl);
-float linphone_call_log_get_quality(LinphoneCallLog *cl);
-void linphone_call_log_set_user_pointer(LinphoneCallLog *cl, void *up);
-void *linphone_call_log_get_user_pointer(const LinphoneCallLog *cl);
-void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey);
-const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl);
-const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl);
-const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl);
-const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl);
-char * linphone_call_log_to_str(LinphoneCallLog *cl);
-
-struct _LinphoneCallParams;
-
-/**
- * The LinphoneCallParams is an object containing various call related parameters.
- * It can be used to retrieve parameters from a currently running call or modify the call's characteristics
- * dynamically.
-**/
-typedef struct _LinphoneCallParams LinphoneCallParams;
-
-const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp);
-const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp);
-LINPHONE_PUBLIC LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp);
-LINPHONE_PUBLIC void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled);
-LINPHONE_PUBLIC bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp);
-LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp);
-void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, LinphoneMediaEncryption e);
-void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled);
-bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp);
-bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp);
-void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bw);
-void linphone_call_params_destroy(LinphoneCallParams *cp);
-bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp);
-void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled);
-void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path);
-const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp);
-void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char *header_value);
-const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name);
-/**
- * Enum describing failure reasons.
- * @ingroup initializing
-**/
-enum _LinphoneReason{
- LinphoneReasonNone,
- LinphoneReasonNoResponse, /**
+ */
+ LinphonePrivacyUser=0x1,
+ /**
+ * Request that privacy services modify headers that cannot
+ * be set arbitrarily by the user (Contact/Via).
+ */
+ LinphonePrivacyHeader=0x2,
+ /**
+ * Request that privacy services provide privacy for session
+ * media
+ */
+ LinphonePrivacySession=0x4,
+ /**
+ * rfc3325
+ * The presence of this privacy type in
+ * a Privacy header field indicates that the user would like the Network
+ * Asserted Identity to be kept private with respect to SIP entities
+ * outside the Trust Domain with which the user authenticated. Note
+ * that a user requesting multiple types of privacy MUST include all of
+ * the requested privacy types in its Privacy header field value
+ *
+ */
+ LinphonePrivacyId=0x8,
+ /**
+ * Privacy service must perform the specified services or
+ * fail the request
+ *
+ **/
+ LinphonePrivacyCritical=0x10,
+
+ /**
+ * Special keyword to use privacy as defined either globally or by proxy using linphone_proxy_config_set_privacy()
+ */
+ LinphonePrivacyDefault=0x8000,
+} LinphonePrivacy;
+/*
+ * a mask of #LinphonePrivacy values
+ * */
+typedef unsigned int LinphonePrivacyMask;
+
+
+LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy);
+
+
+#ifdef IN_LINPHONE
+#include "buffer.h"
+#include "call_log.h"
+#include "call_params.h"
+#include "content.h"
+#include "event.h"
+#include "linphonefriend.h"
+#include "xmlrpc.h"
+#else
+#include "linphone/buffer.h"
+#include "linphone/call_log.h"
+#include "linphone/call_params.h"
+#include "linphone/content.h"
+#include "linphone/event.h"
+#include "linphone/linphonefriend.h"
+#include "linphone/xmlrpc.h"
+#endif
+
+LINPHONE_PUBLIC LinphoneAddress * linphone_address_new(const char *addr);
+LINPHONE_PUBLIC LinphoneAddress * linphone_address_clone(const LinphoneAddress *addr);
+LINPHONE_PUBLIC LinphoneAddress * linphone_address_ref(LinphoneAddress *addr);
+LINPHONE_PUBLIC void linphone_address_unref(LinphoneAddress *addr);
+LINPHONE_PUBLIC const char *linphone_address_get_scheme(const LinphoneAddress *u);
+LINPHONE_PUBLIC const char *linphone_address_get_display_name(const LinphoneAddress* u);
+LINPHONE_PUBLIC const char *linphone_address_get_username(const LinphoneAddress *u);
+LINPHONE_PUBLIC const char *linphone_address_get_domain(const LinphoneAddress *u);
+LINPHONE_PUBLIC int linphone_address_get_port(const LinphoneAddress *u);
+LINPHONE_PUBLIC void linphone_address_set_display_name(LinphoneAddress *u, const char *display_name);
+LINPHONE_PUBLIC void linphone_address_set_username(LinphoneAddress *uri, const char *username);
+LINPHONE_PUBLIC void linphone_address_set_domain(LinphoneAddress *uri, const char *host);
+LINPHONE_PUBLIC void linphone_address_set_port(LinphoneAddress *uri, int port);
+/*remove tags, params etc... so that it is displayable to the user*/
+LINPHONE_PUBLIC void linphone_address_clean(LinphoneAddress *uri);
+LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *addr);
+LINPHONE_PUBLIC bool_t linphone_address_get_secure(const LinphoneAddress *addr);
+LINPHONE_PUBLIC void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled);
+LINPHONE_PUBLIC bool_t linphone_address_is_sip(const LinphoneAddress *uri);
+LINPHONE_PUBLIC LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri);
+LINPHONE_PUBLIC void linphone_address_set_transport(LinphoneAddress *uri,LinphoneTransportType type);
+LINPHONE_PUBLIC char *linphone_address_as_string(const LinphoneAddress *u);
+LINPHONE_PUBLIC char *linphone_address_as_string_uri_only(const LinphoneAddress *u);
+LINPHONE_PUBLIC bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2);
+LINPHONE_PUBLIC bool_t linphone_address_equal(const LinphoneAddress *a1, const LinphoneAddress *a2);
+LINPHONE_PUBLIC void linphone_address_set_password(LinphoneAddress *addr, const char *passwd);
+LINPHONE_PUBLIC const char *linphone_address_get_password(const LinphoneAddress *addr);
+LINPHONE_PUBLIC void linphone_address_set_header(LinphoneAddress *addr, const char *header_name, const char *header_value);
+LINPHONE_PUBLIC void linphone_address_destroy(LinphoneAddress *u);
+
+/**
+ * Create a #LinphoneAddress object by parsing the user supplied address, given as a string.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] address String containing the user supplied address
+ * @return The create #LinphoneAddress object
+ * @ingroup linphone_address
+ */
+LINPHONE_PUBLIC LinphoneAddress * linphone_core_create_address(LinphoneCore *lc, const char *address);
+
+struct _SipSetupContext;
+
+
+struct _LinphoneInfoMessage;
+/**
+ * The LinphoneInfoMessage is an object representing an informational message sent or received by the core.
+**/
+typedef struct _LinphoneInfoMessage LinphoneInfoMessage;
+
+LINPHONE_PUBLIC LinphoneInfoMessage *linphone_core_create_info_message(LinphoneCore*lc);
+LINPHONE_PUBLIC int linphone_call_send_info_message(struct _LinphoneCall *call, const LinphoneInfoMessage *info);
+LINPHONE_PUBLIC void linphone_info_message_add_header(LinphoneInfoMessage *im, const char *name, const char *value);
+LINPHONE_PUBLIC const char *linphone_info_message_get_header(const LinphoneInfoMessage *im, const char *name);
+LINPHONE_PUBLIC void linphone_info_message_set_content(LinphoneInfoMessage *im, const LinphoneContent *content);
+LINPHONE_PUBLIC const LinphoneContent * linphone_info_message_get_content(const LinphoneInfoMessage *im);
+LINPHONE_PUBLIC void linphone_info_message_destroy(LinphoneInfoMessage *im);
+LINPHONE_PUBLIC LinphoneInfoMessage *linphone_info_message_copy(const LinphoneInfoMessage *orig);
+
+
+
+/**
+ * Structure describing policy regarding video streams establishments.
+ * @ingroup media_parameters
+**/
+struct _LinphoneVideoPolicy{
+ bool_t automatically_initiate; /** In case this #LinphoneProxyConfig has been added to #LinphoneCore, follows the linphone_proxy_config_edit() rule.
- * @param obj object pointer
- * @param val if true, registration will be engaged
- */
-LINPHONE_PUBLIC void linphone_proxy_config_enable_register(LinphoneProxyConfig *obj, bool_t val);
-#define linphone_proxy_config_enableregister linphone_proxy_config_enable_register
-LINPHONE_PUBLIC void linphone_proxy_config_edit(LinphoneProxyConfig *obj);
-LINPHONE_PUBLIC int linphone_proxy_config_done(LinphoneProxyConfig *obj);
-/**
- * Indicates either or not, PUBLISH must be issued for this #LinphoneProxyConfig .
- * In case this #LinphoneProxyConfig has been added to #LinphoneCore, follows the linphone_proxy_config_edit() rule.
- * @param obj object pointer
- * @param val if true, publish will be engaged
- *
- */
-LINPHONE_PUBLIC void linphone_proxy_config_enable_publish(LinphoneProxyConfig *obj, bool_t val);
-void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val);
-void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix);
-
-LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyConfig *obj);
-LINPHONE_PUBLIC bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *obj);
-const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg);
-
-const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *obj);
-LINPHONE_PUBLIC const char *linphone_proxy_config_get_identity(const LinphoneProxyConfig *obj);
-bool_t linphone_proxy_config_publish_enabled(const LinphoneProxyConfig *obj);
-LINPHONE_PUBLIC const char *linphone_proxy_config_get_addr(const LinphoneProxyConfig *obj);
-int linphone_proxy_config_get_expires(const LinphoneProxyConfig *obj);
-bool_t linphone_proxy_config_register_enabled(const LinphoneProxyConfig *obj);
-void linphone_proxy_config_refresh_register(LinphoneProxyConfig *obj);
-const char *linphone_proxy_config_get_contact_parameters(const LinphoneProxyConfig *obj);
-void linphone_proxy_config_set_contact_parameters(LinphoneProxyConfig *obj, const char *contact_params);
-struct _LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig *obj);
-
-bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg);
-const char * linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg);
-
-LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg);
-
-/* destruction is called automatically when removing the proxy config */
-void linphone_proxy_config_destroy(LinphoneProxyConfig *cfg);
-void linphone_proxy_config_set_sip_setup(LinphoneProxyConfig *cfg, const char *type);
-SipSetupContext *linphone_proxy_config_get_sip_setup_context(LinphoneProxyConfig *cfg);
-SipSetup *linphone_proxy_config_get_sip_setup(LinphoneProxyConfig *cfg);
-/**
- * normalize a human readable phone number into a basic string. 888-444-222 becomes 888444222
- */
-int linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len);
-/*
- * attached a user data to a proxy config
- */
-void linphone_proxy_config_set_user_data(LinphoneProxyConfig *cr, void * ud);
-/*
- * get user data to a proxy config. return null if any
- */
-void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr);
-
/**
* @}
-**/
+ */
-typedef struct _LinphoneAccountCreator{
- struct _LinphoneCore *lc;
- struct _SipSetupContext *ssctx;
- char *username;
- char *password;
- char *domain;
- char *route;
- char *email;
- int suscribe;
- bool_t succeeded;
-}LinphoneAccountCreator;
-
-LinphoneAccountCreator *linphone_account_creator_new(struct _LinphoneCore *core, const char *type);
-void linphone_account_creator_set_username(LinphoneAccountCreator *obj, const char *username);
-void linphone_account_creator_set_password(LinphoneAccountCreator *obj, const char *password);
-void linphone_account_creator_set_domain(LinphoneAccountCreator *obj, const char *domain);
-void linphone_account_creator_set_route(LinphoneAccountCreator *obj, const char *route);
-void linphone_account_creator_set_email(LinphoneAccountCreator *obj, const char *email);
-void linphone_account_creator_set_suscribe(LinphoneAccountCreator *obj, int suscribre);
-const char * linphone_account_creator_get_username(LinphoneAccountCreator *obj);
-const char * linphone_account_creator_get_domain(LinphoneAccountCreator *obj);
-int linphone_account_creator_test_existence(LinphoneAccountCreator *obj);
-int linphone_account_creator_test_validation(LinphoneAccountCreator *obj);
-LinphoneProxyConfig * linphone_account_creator_validate(LinphoneAccountCreator *obj);
-void linphone_account_creator_destroy(LinphoneAccountCreator *obj);
+#include "linphone_proxy_config.h"
struct _LinphoneAuthInfo;
/**
- * @ingroup authentication
+ * @addtogroup authentication
+ * @{
* Object holding authentication information.
*
* @note The object's fields should not be accessed directly. Prefer using
@@ -617,19 +1020,137 @@ struct _LinphoneAuthInfo;
**/
typedef struct _LinphoneAuthInfo LinphoneAuthInfo;
+/**
+ * Creates a #LinphoneAuthInfo object with supplied information.
+ * The object can be created empty, that is with all arguments set to NULL.
+ * Username, userid, password, realm and domain can be set later using specific methods.
+ * At the end, username and passwd (or ha1) are required.
+ * @param username The username that needs to be authenticated
+ * @param userid The userid used for authenticating (use NULL if you don't know what it is)
+ * @param passwd The password in clear text
+ * @param ha1 The ha1-encrypted password if password is not given in clear text.
+ * @param realm The authentication domain (which can be larger than the sip domain. Unfortunately many SIP servers don't use this parameter.
+ * @param domain The SIP domain for which this authentication information is valid, if it has to be restricted for a single SIP domain.
+ * @return A #LinphoneAuthInfo object. linphone_auth_info_destroy() must be used to destroy it when no longer needed. The LinphoneCore makes a copy of LinphoneAuthInfo
+ * passed through linphone_core_add_auth_info().
+**/
LINPHONE_PUBLIC LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *userid,
- const char *passwd, const char *ha1,const char *realm);
-void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd);
-void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username);
-void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid);
+ const char *passwd, const char *ha1,const char *realm, const char *domain);
-const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i);
-const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i);
-const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i);
+/**
+ * @addtogroup authentication
+ * Instantiates a new auth info with values from source.
+ * @param[in] source The #LinphoneAuthInfo object to be cloned
+ * @return The newly created #LinphoneAuthInfo object.
+ */
+LINPHONE_PUBLIC LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo* source);
+
+/**
+ * Sets the password.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] passwd The password.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd);
+
+/**
+ * Sets the username.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] username The username.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username);
+
+/**
+ * Sets the userid.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] userid The userid.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid);
+
+/**
+ * Sets the realm.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] realm The realm.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm);
+
+/**
+ * Sets the domain for which this authentication is valid.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] domain The domain.
+ * This should not be necessary because realm is supposed to be unique and sufficient.
+ * However, many SIP servers don't set realm correctly, then domain has to be used to distinguish between several SIP account bearing the same username.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_domain(LinphoneAuthInfo *info, const char *domain);
+
+/**
+ * Sets the ha1.
+ * @param[in] info The #LinphoneAuthInfo object
+ * @param[in] ha1 The ha1.
+**/
+LINPHONE_PUBLIC void linphone_auth_info_set_ha1(LinphoneAuthInfo *info, const char *ha1);
+
+/**
+ * Gets the username.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The username.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_username(const LinphoneAuthInfo *info);
+
+/**
+ * Gets the password.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The password.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *info);
+
+/**
+ * Gets the userid.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The userid.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *info);
+
+/**
+ * Gets the realm.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The realm.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *info);
+
+/**
+ * Gets the domain.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The domain.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *info);
+
+/**
+ * Gets the ha1.
+ *
+ * @param[in] info The #LinphoneAuthInfo object
+ * @return The ha1.
+ */
+LINPHONE_PUBLIC const char *linphone_auth_info_get_ha1(const LinphoneAuthInfo *info);
/* you don't need those function*/
-void linphone_auth_info_destroy(LinphoneAuthInfo *info);
-LinphoneAuthInfo * linphone_auth_info_new_from_config_file(struct _LpConfig *config, int pos);
+LINPHONE_PUBLIC void linphone_auth_info_destroy(LinphoneAuthInfo *info);
+LINPHONE_PUBLIC LinphoneAuthInfo * linphone_auth_info_new_from_config_file(LpConfig *config, int pos);
+
+/**
+ * @}
+ */
+
+
+#ifdef IN_LINPHONE
+#include "account_creator.h"
+#else
+#include "linphone/account_creator.h"
+#endif
struct _LinphoneChatRoom;
@@ -639,11 +1160,16 @@ struct _LinphoneChatRoom;
*/
/**
- * A chat room message to old content to be sent.
+ * An object to handle the callbacks for the handling a LinphoneChatMessage objects.
+ */
+typedef struct _LinphoneChatMessageCbs LinphoneChatMessageCbs;
+
+/**
+ * A chat room message to hold content to be sent.
* Can be created by linphone_chat_room_create_message().
*/
typedef struct _LinphoneChatMessage LinphoneChatMessage;
-
+
/**
* A chat room is the place where text messages are exchanged.
* Can be created by linphone_core_create_chat_room().
@@ -651,47 +1177,547 @@ typedef struct _LinphoneChatMessage LinphoneChatMessage;
typedef struct _LinphoneChatRoom LinphoneChatRoom;
/**
- *LinphoneChatMessageState is used to notify if messages have been succesfully delivered or not.
+ * LinphoneChatMessageState is used to notify if messages have been succesfully delivered or not.
*/
-typedef enum _LinphoneChatMessageStates {
- LinphoneChatMessageStateIdle, /** Status on this subscription request is notified by \link linphone_friend_set_inc_subscribe_policy() changing policy \endlink for this friend
- * @param lc #LinphoneCore object
- * @param lf #LinphoneFriend corresponding to the subscriber
- * @param url of the subscriber
+ * Status on this subscription request is notified by \link linphone_friend_set_inc_subscribe_policy() changing policy \endlink for this friend
+ * @param lc #LinphoneCore object
+ * @param lf #LinphoneFriend corresponding to the subscriber
+ * @param url of the subscriber
* Callback prototype
- * */
-typedef void (*NewSubscribtionRequestCb)(struct _LinphoneCore *lc, LinphoneFriend *lf, const char *url);
-/** Callback prototype */
-typedef void (*AuthInfoRequested)(struct _LinphoneCore *lc, const char *realm, const char *username);
-/** Callback prototype */
-typedef void (*CallLogUpdated)(struct _LinphoneCore *lc, struct _LinphoneCallLog *newcl);
+ */
+typedef void (*LinphoneCoreNewSubscriptionRequestedCb)(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
+/**
+ * Callback for requesting authentication information to application or user.
+ * @param lc the LinphoneCore
+ * @param realm the realm (domain) on which authentication is required.
+ * @param username the username that needs to be authenticated.
+ * Application shall reply to this callback using linphone_core_add_auth_info().
+ */
+typedef void (*LinphoneCoreAuthInfoRequestedCb)(LinphoneCore *lc, const char *realm, const char *username, const char *domain);
+
+/**
+ * Callback to notify a new call-log entry has been added.
+ * This is done typically when a call terminates.
+ * @param lc the LinphoneCore
+ * @param newcl the new call log entry added.
+ */
+typedef void (*LinphoneCoreCallLogUpdatedCb)(LinphoneCore *lc, LinphoneCallLog *newcl);
+
/**
* Callback prototype
- * @deprecated use #MessageReceived instead.
+ * @deprecated use #LinphoneCoreMessageReceivedCb instead.
*
* @param lc #LinphoneCore object
* @param room #LinphoneChatRoom involved in this conversation. Can be be created by the framework in case \link #LinphoneAddress the from \endlink is not present in any chat room.
* @param from #LinphoneAddress from
* @param message incoming message
- * */
-typedef void (*TextMessageReceived)(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message);
+ */
+typedef void (*LinphoneCoreTextMessageReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message);
+
/**
* Chat message callback prototype
*
* @param lc #LinphoneCore object
* @param room #LinphoneChatRoom involved in this conversation. Can be be created by the framework in case \link #LinphoneAddress the from \endlink is not present in any chat room.
* @param LinphoneChatMessage incoming message
- * */
-typedef void (*MessageReceived)(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message);
-
+ */
+typedef void (*LinphoneCoreMessageReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message);
+
+/**
+ * File transfer receive callback prototype. This function is called by the core upon an incoming File transfer is started. This function may be call several time for the same file in case of large file.
+ *
+ *
+ * @param lc #LinphoneCore object
+ * @param message #LinphoneChatMessage message from which the body is received.
+ * @param content #LinphoneContent incoming content information
+ * @param buff pointer to the received data
+ * @param size number of bytes to be read from buff. 0 means end of file.
+ *
+ */
+typedef void (*LinphoneCoreFileTransferRecvCb)(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size);
+
+/**
+ * File transfer send callback prototype. This function is called by the core upon an outgoing File transfer is started. This function is called until size is set to 0.
+ * a #LinphoneContent with a size equal zero
+ *
+ * @param lc #LinphoneCore object
+ * @param message #LinphoneChatMessage message from which the body is received.
+ * @param content #LinphoneContent outgoing content
+ * @param buff pointer to the buffer where data chunk shall be written by the app
+ * @param size as input value, it represents the number of bytes expected by the framework. As output value, it means the number of bytes wrote by the application in the buffer. 0 means end of file.
+ *
+ */
+typedef void (*LinphoneCoreFileTransferSendCb)(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size);
+
+/**
+ * File transfer progress indication callback prototype.
+ *
+ * @param lc #LinphoneCore object
+ * @param message #LinphoneChatMessage message from which the body is received.
+ * @param content #LinphoneContent incoming content information
+ * @param offset The number of bytes sent/received since the beginning of the transfer.
+ * @param total The total number of bytes to be sent/received.
+ */
+typedef void (*LinphoneCoreFileTransferProgressIndicationCb)(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total);
+
+/**
+ * Is composing notification callback prototype.
+ *
+ * @param[in] lc #LinphoneCore object
+ * @param[in] room #LinphoneChatRoom involved in the conversation.
+ */
+typedef void (*LinphoneCoreIsComposingReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room);
+
+/**
+ * Callback for being notified of DTMFs received.
+ * @param lc the linphone core
+ * @param call the call that received the dtmf
+ * @param dtmf the ascii code of the dtmf
+ */
+typedef void (*LinphoneCoreDtmfReceivedCb)(LinphoneCore* lc, LinphoneCall *call, int dtmf);
+
/** Callback prototype */
-typedef void (*DtmfReceived)(struct _LinphoneCore* lc, LinphoneCall *call, int dtmf);
+typedef void (*LinphoneCoreReferReceivedCb)(LinphoneCore *lc, const char *refer_to);
/** Callback prototype */
-typedef void (*ReferReceived)(struct _LinphoneCore *lc, const char *refer_to);
-/** Callback prototype */
-typedef void (*BuddyInfoUpdated)(struct _LinphoneCore *lc, LinphoneFriend *lf);
-/** Callback prototype for in progress transfers. The new_call_state is the state of the call resulting of the transfer, at the other party. */
-typedef void (*LinphoneTransferStateChanged)(struct _LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
-/** Callback prototype for receiving quality statistics for calls*/
-typedef void (*CallStatsUpdated)(struct _LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats);
+typedef void (*LinphoneCoreBuddyInfoUpdatedCb)(LinphoneCore *lc, LinphoneFriend *lf);
+/**
+ * Callback for notifying progresses of transfers.
+ * @param lc the LinphoneCore
+ * @param transfered the call that was transfered
+ * @param new_call_state the state of the call to transfer target at the far end.
+ */
+typedef void (*LinphoneCoreTransferStateChangedCb)(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
+
+/**
+ * Callback for receiving quality statistics for calls.
+ * @param lc the LinphoneCore
+ * @param call the call
+ * @param stats the call statistics.
+ */
+typedef void (*LinphoneCoreCallStatsUpdatedCb)(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats);
+
+/**
+ * Callback prototype for receiving info messages.
+ * @param lc the LinphoneCore
+ * @param call the call whose info message belongs to.
+ * @param msg the info message.
+ */
+typedef void (*LinphoneCoreInfoReceivedCb)(LinphoneCore *lc, LinphoneCall *call, const LinphoneInfoMessage *msg);
+
+/**
+ * LinphoneGlobalState describes the global state of the LinphoneCore object.
+ * It is notified via the LinphoneCoreVTable::global_state_changed
+**/
+typedef enum _LinphoneConfiguringState {
+ LinphoneConfiguringSuccessful,
+ LinphoneConfiguringFailed,
+ LinphoneConfiguringSkipped
+} LinphoneConfiguringState;
+
+/**
+ * Converts a _LinphoneConfiguringState enum to a string.
+ * @ingroup misc
+**/
+LINPHONE_PUBLIC const char *linphone_configuring_state_to_string(LinphoneConfiguringState cs);
+
+/**
+ * Callback prototype for configuring status changes notification
+ * @param lc the LinphoneCore
+ * @param message informational message.
+ */
+typedef void (*LinphoneCoreConfiguringStatusCb)(LinphoneCore *lc, LinphoneConfiguringState status, const char *message);
+
+/**
+ * Callback prototype for reporting network change either automatically detected or notified by #linphone_core_set_network_reachable.
+ * @param lc the LinphoneCore
+ * @param reachable true if network is reachable.
+ */
+typedef void (*LinphoneCoreNetworkReachableCb)(LinphoneCore *lc, bool_t reachable);
+
+/**
+ * Callback prototype for reporting log collection upload state change.
+ * @param[in] lc LinphoneCore object
+ * @param[in] state The state of the log collection upload
+ * @param[in] info Additional information: error message in case of error state, URL of uploaded file in case of success.
+ */
+typedef void (*LinphoneCoreLogCollectionUploadStateChangedCb)(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info);
+
+/**
+ * Callback prototype for reporting log collection upload progress indication.
+ * @param[in] lc LinphoneCore object
+ * @param[in] progress Percentage of the file size of the log collection already uploaded.
+ */
+typedef void (*LinphoneCoreLogCollectionUploadProgressIndicationCb)(LinphoneCore *lc, size_t offset, size_t total);
/**
* This structure holds all callbacks that the application should implement.
* None is mandatory.
**/
-typedef struct _LinphoneVTable{
- LinphoneGlobalStateCb global_state_changed; /** A text message has been received */
- MessageReceived message_received; /** a message is received, can be text or external body*/
- DtmfReceived dtmf_received; /**< A dtmf has been received received */
- ReferReceived refer_received; /**< An out of call refer was received */
- CallEncryptionChangedCb call_encryption_changed; /** A text message has been received */
+ LinphoneCoreFileTransferRecvCb file_transfer_recv; /**< @deprecated Callback to store file received attached to a #LinphoneChatMessage */
+ LinphoneCoreFileTransferSendCb file_transfer_send; /**< @deprecated Callback to collect file chunk to be sent for a #LinphoneChatMessage */
+ LinphoneCoreFileTransferProgressIndicationCb file_transfer_progress_indication; /**< @deprecated Callback to indicate file transfer progress */
+ LinphoneCoreNetworkReachableCb network_reachable; /**< Callback to report IP network status (I.E up/down )*/
+ LinphoneCoreLogCollectionUploadStateChangedCb log_collection_upload_state_changed; /**< Callback to upload collected logs */
+ LinphoneCoreLogCollectionUploadProgressIndicationCb log_collection_upload_progress_indication; /**< Callback to indicate log collection upload progress */
+ void *user_data; /**data field of the MSList points a PayloadType
+ * structure holding the codec information.
+ * It is possible to make copy of the list with ms_list_copy() in order to modify it
+ * (such as the order of codecs).
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const MSList *linphone_core_get_audio_codecs(const LinphoneCore *lc);
+
+LINPHONE_PUBLIC int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs);
+
+/**
+ * Returns the list of available video codecs.
+ * @param[in] lc The LinphoneCore object
+ * @return \mslist{PayloadType}
+ *
+ * This list is unmodifiable. The ->data field of the MSList points a PayloadType
+ * structure holding the codec information.
+ * It is possible to make copy of the list with ms_list_copy() in order to modify it
+ * (such as the order of codecs).
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc);
+
+LINPHONE_PUBLIC int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs);
+
+LINPHONE_PUBLIC void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled);
+
+LINPHONE_PUBLIC bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc);
+
+/**
+ * Tells whether the specified payload type is enabled.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] pt The #LinphonePayloadType we want to know is enabled or not.
+ * @return TRUE if the payload type is enabled, FALSE if disabled.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt);
+
+/**
+ * Tells whether the specified payload type represents a variable bitrate codec.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] pt The #LinphonePayloadType we want to know
+ * @return TRUE if the payload type represents a VBR codec, FALSE if disabled.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt);
+
+/**
+ * Set an explicit bitrate (IP bitrate, not codec bitrate) for a given codec, in kbit/s.
+ * @param[in] lc the #LinphoneCore object
+ * @param[in] pt the #LinphonePayloadType to modify.
+ * @param[in] bitrate the IP bitrate in kbit/s.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, LinphonePayloadType *pt, int bitrate);
+
+/**
+ * Get the bitrate explicitely set with linphone_core_set_payload_type_bitrate().
+ * @param[in] lc the #LinphoneCore object
+ * @param[in] pt the #LinphonePayloadType to modify.
+ * @return bitrate the IP bitrate in kbit/s, or -1 if an error occured.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC int linphone_core_get_payload_type_bitrate(LinphoneCore *lc, const LinphonePayloadType *pt);
+
+/**
+ * Enable or disable the use of the specified payload type.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] pt The #LinphonePayloadType to enable or disable. It can be retrieved using #linphone_core_find_payload_type
+ * @param[in] enable TRUE to enable the payload type, FALSE to disable it.
+ * @return 0 if successful, any other value otherwise.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enable);
+
+/**
+ * Wildcard value used by #linphone_core_find_payload_type to ignore rate in search algorithm
* @ingroup media_parameters
*/
#define LINPHONE_FIND_PAYLOAD_IGNORE_RATE -1
/**
- * Wildcard value used by #linphone_core_find_payload_type to ignore channel in search algirithm
+ * Wildcard value used by #linphone_core_find_payload_type to ignore channel in search algorithm
* @ingroup media_parameters
*/
#define LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS -1
@@ -995,100 +2714,216 @@ LINPHONE_PUBLIC int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadT
* @param rate can be #LINPHONE_FIND_PAYLOAD_IGNORE_RATE
* @param channels number of channels, can be #LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS
* @return Returns NULL if not found.
- */
-LINPHONE_PUBLIC PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) ;
-
-int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt);
-
-const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt);
-
-bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt);
+ */
+LINPHONE_PUBLIC LinphonePayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) ;
/**
- * @ingroup proxy
- *Create a proxy config with default value from Linphone core.
- *@param lc #LinphoneCore object
- *@return #LinphoneProxyConfig with defualt value set
+ * @ingroup media_parameters
+ * Returns the payload type number assigned for this codec.
+**/
+LINPHONE_PUBLIC int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt);
+
+/**
+ * @ingroup media_parameters
+ * Force a number for a payload type. The LinphoneCore does payload type number assignment automatically. THis function is to be used mainly for tests, in order
+ * to override the automatic assignment mechanism.
+**/
+LINPHONE_PUBLIC void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number);
+
+LINPHONE_PUBLIC const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt);
+
+LINPHONE_PUBLIC bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt);
+
+/**
+ * @addtogroup proxies
+ * @{
*/
-LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc);
-
-LINPHONE_PUBLIC int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config);
-void linphone_core_clear_proxy_config(LinphoneCore *lc);
+/**
+ * Create a proxy config with default values from Linphone core.
+ * @param[in] lc #LinphoneCore object
+ * @return #LinphoneProxyConfig with default values set
+ */
+LINPHONE_PUBLIC LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc);
-void linphone_core_remove_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config);
+/**
+ * Add a proxy configuration.
+ * This will start registration on the proxy, if registration is enabled.
+**/
+LINPHONE_PUBLIC int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config);
-LINPHONE_PUBLIC const MSList *linphone_core_get_proxy_config_list(const LinphoneCore *lc);
+/**
+ * Erase all proxies from config.
+**/
+LINPHONE_PUBLIC void linphone_core_clear_proxy_config(LinphoneCore *lc);
-LINPHONE_PUBLIC void linphone_core_set_default_proxy(LinphoneCore *lc, LinphoneProxyConfig *config);
+/**
+ * Removes a proxy configuration.
+ *
+ * LinphoneCore will then automatically unregister and place the proxy configuration
+ * on a deleted list. For that reason, a removed proxy does NOT need to be freed.
+**/
+LINPHONE_PUBLIC void linphone_core_remove_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config);
-void linphone_core_set_default_proxy_index(LinphoneCore *lc, int index);
+/**
+ * Returns an unmodifiable list of entered proxy configurations.
+ * @param[in] lc The LinphoneCore object
+ * @return \mslist{LinphoneProxyConfig}
+**/
+LINPHONE_PUBLIC const MSList *linphone_core_get_proxy_config_list(const LinphoneCore *lc);
-LINPHONE_PUBLIC int linphone_core_get_default_proxy(LinphoneCore *lc, LinphoneProxyConfig **config);
+/** @deprecated Use linphone_core_set_default_proxy_config() instead. */
+#define linphone_core_set_default_proxy(lc, config) linphone_core_set_default_proxy_config(lc, config)
+
+LINPHONE_PUBLIC void linphone_core_set_default_proxy_index(LinphoneCore *lc, int index);
+
+/**
+ * @return the default proxy configuration, that is the one used to determine the current identity.
+ * @deprecated Use linphone_core_get_default_proxy_config() instead.
+**/
+LINPHONE_PUBLIC int linphone_core_get_default_proxy(LinphoneCore *lc, LinphoneProxyConfig **config);
+
+/**
+ * @return the default proxy configuration, that is the one used to determine the current identity.
+ * @param[in] lc LinphoneCore object
+ * @return The default proxy configuration.
+**/
+LINPHONE_PUBLIC LinphoneProxyConfig * linphone_core_get_default_proxy_config(LinphoneCore *lc);
+
+/**
+ * Sets the default proxy.
+ *
+ * This default proxy must be part of the list of already entered LinphoneProxyConfig.
+ * Toggling it as default will make LinphoneCore use the identity associated with
+ * the proxy configuration in all incoming and outgoing calls.
+ * @param[in] lc LinphoneCore object
+ * @param[in] config The proxy configuration to use as the default one.
+**/
+LINPHONE_PUBLIC void linphone_core_set_default_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config);
+
+/**
+ * @}
+ */
+
+/**
+ * Create an authentication information with default values from Linphone core.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] username String containing the username part of the authentication credentials
+ * @param[in] userid String containing the username to use to calculate the authentication digest (optional)
+ * @param[in] passwd String containing the password of the authentication credentials (optional, either passwd or ha1 must be set)
+ * @param[in] ha1 String containing a ha1 hash of the password (optional, either passwd or ha1 must be set)
+ * @param[in] realm String used to discriminate different SIP authentication domains (optional)
+ * @param[in] domain String containing the SIP domain for which this authentication information is valid, if it has to be restricted for a single SIP domain.
+ * @return #LinphoneAuthInfo with default values set
+ * @ingroup authentication
+ */
+LINPHONE_PUBLIC LinphoneAuthInfo * linphone_core_create_auth_info(LinphoneCore *lc, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain);
LINPHONE_PUBLIC void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info);
-void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info);
+LINPHONE_PUBLIC void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info);
-const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc);
+LINPHONE_PUBLIC const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc);
-const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username);
+LINPHONE_PUBLIC const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *sip_domain);
-void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *info);
+LINPHONE_PUBLIC void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *info);
-void linphone_core_clear_all_auth_info(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_clear_all_auth_info(LinphoneCore *lc);
-void linphone_core_enable_audio_adaptive_jittcomp(LinphoneCore *lc, bool_t enable);
+/**
+ * Enable or disable the audio adaptive jitter compensation.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] enable TRUE to enable the audio adaptive jitter compensation, FALSE to disable it.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_enable_audio_adaptive_jittcomp(LinphoneCore *lc, bool_t enable);
-bool_t linphone_core_audio_adaptive_jittcomp_enabled(LinphoneCore *lc);
+/**
+ * Tells whether the audio adaptive jitter compensation is enabled.
+ * @param[in] lc #LinphoneCore object
+ * @return TRUE if the audio adaptive jitter compensation is enabled, FALSE otherwise.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC bool_t linphone_core_audio_adaptive_jittcomp_enabled(LinphoneCore *lc);
-int linphone_core_get_audio_jittcomp(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_audio_jittcomp(LinphoneCore *lc);
-void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int value);
+/**
+ * Sets the nominal audio jitter buffer size in milliseconds.
+ * The value takes effect immediately for all running and pending calls, if any.
+ * A value of 0 disables the jitter buffer.
+ *
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int milliseconds);
-void linphone_core_enable_video_adaptive_jittcomp(LinphoneCore *lc, bool_t enable);
+/**
+ * Enable or disable the video adaptive jitter compensation.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] enable TRUE to enable the video adaptive jitter compensation, FALSE to disable it.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_enable_video_adaptive_jittcomp(LinphoneCore *lc, bool_t enable);
-bool_t linphone_core_video_adaptive_jittcomp_enabled(LinphoneCore *lc);
+/**
+ * Tells whether the video adaptive jitter compensation is enabled.
+ * @param[in] lc #LinphoneCore object
+ * @return TRUE if the video adaptive jitter compensation is enabled, FALSE otherwise.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC bool_t linphone_core_video_adaptive_jittcomp_enabled(LinphoneCore *lc);
-int linphone_core_get_video_jittcomp(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_video_jittcomp(LinphoneCore *lc);
-void linphone_core_set_video_jittcomp(LinphoneCore *lc, int value);
+/**
+ * Sets the nominal video jitter buffer size in milliseconds.
+ * The value takes effect immediately for all running and pending calls, if any.
+ * A value of 0 disables the jitter buffer.
+ *
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_set_video_jittcomp(LinphoneCore *lc, int milliseconds);
-int linphone_core_get_audio_port(const LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_audio_port(const LinphoneCore *lc);
-void linphone_core_get_audio_port_range(const LinphoneCore *lc, int *min_port, int *max_port);
+LINPHONE_PUBLIC void linphone_core_get_audio_port_range(const LinphoneCore *lc, int *min_port, int *max_port);
-int linphone_core_get_video_port(const LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_video_port(const LinphoneCore *lc);
-void linphone_core_get_video_port_range(const LinphoneCore *lc, int *min_port, int *max_port);
+LINPHONE_PUBLIC void linphone_core_get_video_port_range(const LinphoneCore *lc, int *min_port, int *max_port);
-int linphone_core_get_nortp_timeout(const LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_nortp_timeout(const LinphoneCore *lc);
-void linphone_core_set_audio_port(LinphoneCore *lc, int port);
+LINPHONE_PUBLIC void linphone_core_set_audio_port(LinphoneCore *lc, int port);
-void linphone_core_set_audio_port_range(LinphoneCore *lc, int min_port, int max_port);
+LINPHONE_PUBLIC void linphone_core_set_audio_port_range(LinphoneCore *lc, int min_port, int max_port);
-void linphone_core_set_video_port(LinphoneCore *lc, int port);
+LINPHONE_PUBLIC void linphone_core_set_video_port(LinphoneCore *lc, int port);
-void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_port);
+LINPHONE_PUBLIC void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_port);
-void linphone_core_set_nortp_timeout(LinphoneCore *lc, int port);
+LINPHONE_PUBLIC void linphone_core_set_nortp_timeout(LinphoneCore *lc, int port);
-void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc, bool_t use_info);
+LINPHONE_PUBLIC void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc, bool_t use_info);
-bool_t linphone_core_get_use_info_for_dtmf(LinphoneCore *lc);
+LINPHONE_PUBLIC bool_t linphone_core_get_use_info_for_dtmf(LinphoneCore *lc);
-void linphone_core_set_use_rfc2833_for_dtmf(LinphoneCore *lc,bool_t use_rfc2833);
+LINPHONE_PUBLIC void linphone_core_set_use_rfc2833_for_dtmf(LinphoneCore *lc,bool_t use_rfc2833);
-bool_t linphone_core_get_use_rfc2833_for_dtmf(LinphoneCore *lc);
+LINPHONE_PUBLIC bool_t linphone_core_get_use_rfc2833_for_dtmf(LinphoneCore *lc);
-void linphone_core_set_sip_port(LinphoneCore *lc, int port);
+LINPHONE_PUBLIC void linphone_core_set_sip_port(LinphoneCore *lc, int port);
-int linphone_core_get_sip_port(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_sip_port(LinphoneCore *lc);
LINPHONE_PUBLIC int linphone_core_set_sip_transports(LinphoneCore *lc, const LCSipTransports *transports);
LINPHONE_PUBLIC int linphone_core_get_sip_transports(LinphoneCore *lc, LCSipTransports *transports);
+
+LINPHONE_PUBLIC void linphone_core_get_sip_transports_used(LinphoneCore *lc, LCSipTransports *tr);
+
+LINPHONE_PUBLIC bool_t linphone_core_sip_transport_supported(const LinphoneCore *lc, LinphoneTransportType tp);
/**
*
* Give access to the UDP sip socket. Can be useful to configure this socket as persistent I.E kCFStreamNetworkServiceType set to kCFStreamNetworkServiceTypeVoIP)
@@ -1097,198 +2932,621 @@ LINPHONE_PUBLIC int linphone_core_get_sip_transports(LinphoneCore *lc, LCSipTran
*/
ortp_socket_t linphone_core_get_sip_socket(LinphoneCore *lc);
-void linphone_core_set_inc_timeout(LinphoneCore *lc, int seconds);
+LINPHONE_PUBLIC void linphone_core_set_inc_timeout(LinphoneCore *lc, int seconds);
-int linphone_core_get_inc_timeout(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_inc_timeout(LinphoneCore *lc);
-void linphone_core_set_in_call_timeout(LinphoneCore *lc, int seconds);
+LINPHONE_PUBLIC void linphone_core_set_in_call_timeout(LinphoneCore *lc, int seconds);
-int linphone_core_get_in_call_timeout(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_in_call_timeout(LinphoneCore *lc);
-void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds);
+LINPHONE_PUBLIC void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds);
-int linphone_core_get_delayed_timeout(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_delayed_timeout(LinphoneCore *lc);
-void linphone_core_set_stun_server(LinphoneCore *lc, const char *server);
+/**
+ * Set the STUN server address to use when the firewall policy is set to STUN.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] server The STUN server address to use.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_set_stun_server(LinphoneCore *lc, const char *server);
-const char * linphone_core_get_stun_server(const LinphoneCore *lc);
+/**
+ * Get the STUN server address being used.
+ * @param[in] lc #LinphoneCore object
+ * @return The STUN server address being used.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC const char * linphone_core_get_stun_server(const LinphoneCore *lc);
/**
* @ingroup network_parameters
* Return the availability of uPnP.
*
- * @param lc #LinphoneCore
- * @return true if uPnP is available otherwise return false.
+ * @return true if uPnP is available otherwise return false.
*/
-bool_t linphone_core_upnp_available(const LinphoneCore *lc);
+LINPHONE_PUBLIC bool_t linphone_core_upnp_available(void);
/**
* @ingroup network_parameters
- * Return the internal state of uPnP.
+ * Return the internal state of uPnP.
*
* @param lc #LinphoneCore
- * @return an LinphoneUpnpState.
+ * @return an LinphoneUpnpState.
*/
-LinphoneUpnpState linphone_core_get_upnp_state(const LinphoneCore *lc);
+LINPHONE_PUBLIC LinphoneUpnpState linphone_core_get_upnp_state(const LinphoneCore *lc);
/**
* @ingroup network_parameters
- * Return the external ip address of router.
+ * Return the external ip address of router.
* In some cases the uPnP can have an external ip address but not a usable uPnP
- * (state different of Ok).
+ * (state different of Ok).
*
* @param lc #LinphoneCore
* @return a null terminated string containing the external ip address. If the
- * the external ip address is not available return null.
+ * the external ip address is not available return null.
*/
-const char * linphone_core_get_upnp_external_ipaddress(const LinphoneCore *lc);
+LINPHONE_PUBLIC const char * linphone_core_get_upnp_external_ipaddress(const LinphoneCore *lc);
-void linphone_core_set_nat_address(LinphoneCore *lc, const char *addr);
+/**
+ * Set the public IP address of NAT when using the firewall policy is set to use NAT.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] addr The public IP address of NAT to use.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_set_nat_address(LinphoneCore *lc, const char *addr);
-const char *linphone_core_get_nat_address(const LinphoneCore *lc);
+/**
+ * Get the public IP address of NAT being used.
+ * @param[in] lc #LinphoneCore object.
+ * @return The public IP address of NAT being used.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC const char *linphone_core_get_nat_address(const LinphoneCore *lc);
-void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol);
+/**
+ * Set the policy to use to pass through firewalls.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] pol The #LinphoneFirewallPolicy to use.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol);
-LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc);
-
-const char * linphone_core_get_relay_addr(const LinphoneCore *lc);
-
-int linphone_core_set_relay_addr(LinphoneCore *lc, const char *addr);
+/**
+ * Get the policy that is used to pass through firewalls.
+ * @param[in] lc #LinphoneCore object.
+ * @return The #LinphoneFirewallPolicy that is being used.
+ * @ingroup network_parameters
+ */
+LINPHONE_PUBLIC LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc);
/* sound functions */
/* returns a null terminated static array of string describing the sound devices */
-const char** linphone_core_get_sound_devices(LinphoneCore *lc);
-void linphone_core_reload_sound_devices(LinphoneCore *lc);
-bool_t linphone_core_sound_device_can_capture(LinphoneCore *lc, const char *device);
-bool_t linphone_core_sound_device_can_playback(LinphoneCore *lc, const char *device);
-int linphone_core_get_ring_level(LinphoneCore *lc);
-int linphone_core_get_play_level(LinphoneCore *lc);
-int linphone_core_get_rec_level(LinphoneCore *lc);
-void linphone_core_set_ring_level(LinphoneCore *lc, int level);
-void linphone_core_set_play_level(LinphoneCore *lc, int level);
+LINPHONE_PUBLIC const char** linphone_core_get_sound_devices(LinphoneCore *lc);
-void linphone_core_set_mic_gain_db(LinphoneCore *lc, float level);
-float linphone_core_get_mic_gain_db(LinphoneCore *lc);
-void linphone_core_set_playback_gain_db(LinphoneCore *lc, float level);
-float linphone_core_get_playback_gain_db(LinphoneCore *lc);
+/**
+ * Update detection of sound devices.
+ *
+ * Use this function when the application is notified of USB plug events, so that
+ * list of available hardwares for sound playback and capture is updated.
+ * @param[in] lc #LinphoneCore object.
+ * @ingroup media_parameters
+ **/
+LINPHONE_PUBLIC void linphone_core_reload_sound_devices(LinphoneCore *lc);
-void linphone_core_set_rec_level(LinphoneCore *lc, int level);
-const char * linphone_core_get_ringer_device(LinphoneCore *lc);
-const char * linphone_core_get_playback_device(LinphoneCore *lc);
-const char * linphone_core_get_capture_device(LinphoneCore *lc);
-int linphone_core_set_ringer_device(LinphoneCore *lc, const char * devid);
-int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid);
-int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid);
+LINPHONE_PUBLIC bool_t linphone_core_sound_device_can_capture(LinphoneCore *lc, const char *device);
+LINPHONE_PUBLIC bool_t linphone_core_sound_device_can_playback(LinphoneCore *lc, const char *device);
+LINPHONE_PUBLIC int linphone_core_get_ring_level(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_play_level(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_rec_level(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_ring_level(LinphoneCore *lc, int level);
+LINPHONE_PUBLIC void linphone_core_set_play_level(LinphoneCore *lc, int level);
+
+LINPHONE_PUBLIC void linphone_core_set_mic_gain_db(LinphoneCore *lc, float level);
+LINPHONE_PUBLIC float linphone_core_get_mic_gain_db(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_playback_gain_db(LinphoneCore *lc, float level);
+LINPHONE_PUBLIC float linphone_core_get_playback_gain_db(LinphoneCore *lc);
+
+LINPHONE_PUBLIC void linphone_core_set_rec_level(LinphoneCore *lc, int level);
+LINPHONE_PUBLIC const char * linphone_core_get_ringer_device(LinphoneCore *lc);
+LINPHONE_PUBLIC const char * linphone_core_get_playback_device(LinphoneCore *lc);
+LINPHONE_PUBLIC const char * linphone_core_get_capture_device(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_set_ringer_device(LinphoneCore *lc, const char * devid);
+LINPHONE_PUBLIC int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid);
+LINPHONE_PUBLIC int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid);
char linphone_core_get_sound_source(LinphoneCore *lc);
void linphone_core_set_sound_source(LinphoneCore *lc, char source);
+LINPHONE_PUBLIC void linphone_core_stop_ringing(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_set_ring(LinphoneCore *lc, const char *path);
-const char *linphone_core_get_ring(const LinphoneCore *lc);
-void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno);
-void linphone_core_verify_server_cn(LinphoneCore *lc, bool_t yesno);
-void linphone_core_set_root_ca(LinphoneCore *lc, const char *path);
-const char *linphone_core_get_root_ca(LinphoneCore *lc);
+LINPHONE_PUBLIC const char *linphone_core_get_ring(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno);
+LINPHONE_PUBLIC void linphone_core_verify_server_cn(LinphoneCore *lc, bool_t yesno);
+LINPHONE_PUBLIC void linphone_core_set_root_ca(LinphoneCore *lc, const char *path);
+LINPHONE_PUBLIC const char *linphone_core_get_root_ca(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_set_ringback(LinphoneCore *lc, const char *path);
-const char * linphone_core_get_ringback(const LinphoneCore *lc);
+LINPHONE_PUBLIC const char * linphone_core_get_ringback(const LinphoneCore *lc);
-void linphone_core_set_remote_ringback_tone(LinphoneCore *lc,const char *);
-const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc);
+/**
+ * Specify a ring back tone to be played to far end during incoming calls.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] ring The path to the ring back tone to be played.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_set_remote_ringback_tone(LinphoneCore *lc, const char *ring);
-int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCbFunc func,void * userdata);
-void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val);
-bool_t linphone_core_echo_cancellation_enabled(LinphoneCore *lc);
+/**
+ * Get the ring back tone played to far end during incoming calls.
+ * @param[in] lc #LinphoneCore object
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc);
-void linphone_core_enable_echo_limiter(LinphoneCore *lc, bool_t val);
-bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc);
+/**
+ * Enable or disable the ring play during an incoming early media call.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] enable A boolean value telling whether to enable ringing during an incoming early media call.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_set_ring_during_incoming_early_media(LinphoneCore *lc, bool_t enable);
+
+/**
+ * Tells whether the ring play is enabled during an incoming early media call.
+ * @param[in] lc #LinphoneCore object
+ * @ingroup media_paramaters
+ */
+LINPHONE_PUBLIC bool_t linphone_core_get_ring_during_incoming_early_media(const LinphoneCore *lc);
+
+LINPHONE_PUBLIC int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCbFunc func,void * userdata);
+LINPHONE_PUBLIC int linphone_core_play_local(LinphoneCore *lc, const char *audiofile);
+LINPHONE_PUBLIC void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val);
+LINPHONE_PUBLIC bool_t linphone_core_echo_cancellation_enabled(LinphoneCore *lc);
+
+/**
+ * Enables or disable echo limiter.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] val TRUE to enable echo limiter, FALSE to disable it.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_enable_echo_limiter(LinphoneCore *lc, bool_t val);
+
+/**
+ * Tells whether echo limiter is enabled.
+ * @param[in] lc #LinphoneCore object.
+ * @return TRUE if the echo limiter is enabled, FALSE otherwise.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc);
void linphone_core_enable_agc(LinphoneCore *lc, bool_t val);
bool_t linphone_core_agc_enabled(const LinphoneCore *lc);
-void linphone_core_mute_mic(LinphoneCore *lc, bool_t muted);
/**
- * return mic state.
- *
+ * @deprecated Use #linphone_core_enable_mic instead.
+**/
+LINPHONE_PUBLIC void linphone_core_mute_mic(LinphoneCore *lc, bool_t muted);
+
+/**
+ * Get mic state.
+ * @deprecated Use #linphone_core_mic_enabled instead
+**/
+LINPHONE_PUBLIC bool_t linphone_core_is_mic_muted(LinphoneCore *lc);
+
+/**
+ * Enable or disable the microphone.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] enable TRUE to enable the microphone, FALSE to disable it.
* @ingroup media_parameters
**/
-bool_t linphone_core_is_mic_muted(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_enable_mic(LinphoneCore *lc, bool_t enable);
-bool_t linphone_core_is_rtp_muted(LinphoneCore *lc);
+/**
+ * Tells whether the microphone is enabled.
+ * @param[in] lc #LinphoneCore object
+ * @return TRUE if the microphone is enabled, FALSE if disabled.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_core_mic_enabled(LinphoneCore *lc);
-bool_t linphone_core_get_rtp_no_xmit_on_audio_mute(const LinphoneCore *lc);
-void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, bool_t val);
+LINPHONE_PUBLIC bool_t linphone_core_is_rtp_muted(LinphoneCore *lc);
+
+LINPHONE_PUBLIC bool_t linphone_core_get_rtp_no_xmit_on_audio_mute(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, bool_t val);
-/* returns a list of LinphoneCallLog */
-const MSList * linphone_core_get_call_logs(LinphoneCore *lc);
-void linphone_core_clear_call_logs(LinphoneCore *lc);
-int linphone_core_get_missed_calls_count(LinphoneCore *lc);
-void linphone_core_reset_missed_calls_count(LinphoneCore *lc);
-void linphone_core_remove_call_log(LinphoneCore *lc, LinphoneCallLog *call_log);
+/*******************************************************************************
+ * Call log related functions *
+ ******************************************************************************/
+
+/**
+ * @addtogroup call_logs
+ * @{
+**/
+
+/**
+ * Get the list of call logs (past calls).
+ * @param[in] lc LinphoneCore object
+ * @return \mslist{LinphoneCallLog}
+**/
+LINPHONE_PUBLIC const MSList * linphone_core_get_call_logs(LinphoneCore *lc);
+
+/**
+ * Erase the call log.
+ * @param[in] lc LinphoneCore object
+**/
+LINPHONE_PUBLIC void linphone_core_clear_call_logs(LinphoneCore *lc);
+
+/**
+ * Get the number of missed calls.
+ * Once checked, this counter can be reset with linphone_core_reset_missed_calls_count().
+ * @param[in] lc #LinphoneCore object.
+ * @return The number of missed calls.
+**/
+LINPHONE_PUBLIC int linphone_core_get_missed_calls_count(LinphoneCore *lc);
+
+/**
+ * Reset the counter of missed calls.
+ * @param[in] lc #LinphoneCore object.
+**/
+LINPHONE_PUBLIC void linphone_core_reset_missed_calls_count(LinphoneCore *lc);
+
+/**
+ * Remove a specific call log from call history list.
+ * This function destroys the call log object. It must not be accessed anymore by the application after calling this function.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] call_log #LinphoneCallLog object to remove.
+**/
+LINPHONE_PUBLIC void linphone_core_remove_call_log(LinphoneCore *lc, LinphoneCallLog *call_log);
+
+/**
+ * @}
+**/
+
/* video support */
-bool_t linphone_core_video_supported(LinphoneCore *lc);
+LINPHONE_PUBLIC bool_t linphone_core_video_supported(LinphoneCore *lc);
+
+/**
+ * Enables video globally.
+ *
+ * This function does not have any effect during calls. It just indicates LinphoneCore to
+ * initiate future calls with video or not. The two boolean parameters indicate in which
+ * direction video is enabled. Setting both to false disables video entirely.
+ *
+ * @param lc The LinphoneCore object
+ * @param vcap_enabled indicates whether video capture is enabled
+ * @param display_enabled indicates whether video display should be shown
+ * @ingroup media_parameters
+ * @deprecated Use #linphone_core_enable_video_capture and #linphone_core_enable_video_display instead.
+**/
LINPHONE_PUBLIC void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t display_enabled);
-bool_t linphone_core_video_enabled(LinphoneCore *lc);
+
+/**
+ * Returns TRUE if video is enabled, FALSE otherwise.
+ * @ingroup media_parameters
+ * @deprecated Use #linphone_core_video_capture_enabled and #linphone_core_video_display_enabled instead.
+**/
+LINPHONE_PUBLIC bool_t linphone_core_video_enabled(LinphoneCore *lc);
+
+/**
+ * Enable or disable video capture.
+ *
+ * This function does not have any effect during calls. It just indicates the #LinphoneCore to
+ * initiate future calls with video capture or not.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] enable TRUE to enable video capture, FALSE to disable it.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_enable_video_capture(LinphoneCore *lc, bool_t enable);
+
+/**
+ * Enable or disable video display.
+ *
+ * This function does not have any effect during calls. It just indicates the #LinphoneCore to
+ * initiate future calls with video display or not.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] enable TRUE to enable video display, FALSE to disable it.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_enable_video_display(LinphoneCore *lc, bool_t enable);
+
+
+/**
+ * Enable or disable video source reuse when switching from preview to actual video call.
+ *
+ * This source reuse is useful when you always display the preview, even before calls are initiated.
+ * By keeping the video source for the transition to a real video call, you will smooth out the
+ * source close/reopen cycle.
+ *
+ * This function does not have any effect durfing calls. It just indicates the #LinphoneCore to
+ * initiate future calls with video source reuse or not.
+ * Also, at the end of a video call, the source will be closed whatsoever for now.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] enable TRUE to enable video source reuse. FALSE to disable it for subsequent calls.
+ * @ingroup media_parameters
+ *
+ */
+LINPHONE_PUBLIC void linphone_core_enable_video_source_reuse(LinphoneCore* lc, bool_t enable);
+
+/**
+ * Tells whether video capture is enabled.
+ * @param[in] lc #LinphoneCore object.
+ * @return TRUE if video capture is enabled, FALSE if disabled.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_core_video_capture_enabled(LinphoneCore *lc);
+
+/**
+ * Tells whether video display is enabled.
+ * @param[in] lc #LinphoneCore object.
+ * @return TRUE if video display is enabled, FALSE if disabled.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_core_video_display_enabled(LinphoneCore *lc);
+
LINPHONE_PUBLIC void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy);
-const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc);
+LINPHONE_PUBLIC const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc);
typedef struct MSVideoSizeDef{
MSVideoSize vsize;
const char *name;
}MSVideoSizeDef;
-/* returns a zero terminated table of MSVideoSizeDef*/
-const MSVideoSizeDef *linphone_core_get_supported_video_sizes(LinphoneCore *lc);
-void linphone_core_set_preferred_video_size(LinphoneCore *lc, MSVideoSize vsize);
-MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc);
-void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char *name);
+/**
+ * Returns the zero terminated table of supported video resolutions.
+ *
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const MSVideoSizeDef *linphone_core_get_supported_video_sizes(LinphoneCore *lc);
-void linphone_core_enable_video_preview(LinphoneCore *lc, bool_t val);
-bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc);
+/**
+ * Sets the preferred video size.
+ *
+ * @ingroup media_parameters
+ * This applies only to the stream that is captured and sent to the remote party,
+ * since we accept all standard video size on the receive path.
+**/LINPHONE_PUBLIC void linphone_core_set_preferred_video_size(LinphoneCore *lc, MSVideoSize vsize);
+/**
+ * Sets the video size for the captured (preview) video.
+ * This method is for advanced usage where a video capture must be set independently of the size of the stream actually sent through the call.
+ * This allows for example to have the preview window with HD resolution even if due to bandwidth constraint the sent video size is small.
+ * Using this feature increases the CPU consumption, since a rescaling will be done internally.
+ * @ingroup media_parameters
+ * @param lc the linphone core
+ * @param vsize the video resolution choosed for capuring and previewing. It can be (0,0) to not request any specific preview size and let the core optimize the processing.
+**/
+LINPHONE_PUBLIC void linphone_core_set_preview_video_size(LinphoneCore *lc, MSVideoSize vsize);
+/**
+ * Sets the preview video size by its name. See linphone_core_set_preview_video_size() for more information about this feature.
+ *
+ * @ingroup media_parameters
+ * Video resolution names are: qcif, svga, cif, vga, 4cif, svga ...
+**/
+LINPHONE_PUBLIC void linphone_core_set_preview_video_size_by_name(LinphoneCore *lc, const char *name);
+/**
+ * Returns video size for the captured video if it was previously set by linphone_core_set_preview_video_size(), otherwise returns a 0,0 size.
+ * @see linphone_core_set_preview_video_size()
+ * @ingroup media_parameters
+ * @param lc the core
+ * @return a MSVideoSize
+**/
+LINPHONE_PUBLIC MSVideoSize linphone_core_get_preview_video_size(const LinphoneCore *lc);
+/**
+ * Returns the effective video size for the captured video as provided by the camera.
+ * When preview is disabled or not yet started, this function returns a zeroed video size.
+ * @see linphone_core_set_preview_video_size()
+ * @ingroup media_parameters
+ * @param lc the core
+ * @return a MSVideoSize
+**/
+LINPHONE_PUBLIC MSVideoSize linphone_core_get_current_preview_video_size(const LinphoneCore *lc);
+/**
+ * Returns the current preferred video size for sending.
+ *
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC MSVideoSize linphone_core_get_preferred_video_size(const LinphoneCore *lc);
-void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val);
-bool_t linphone_core_self_view_enabled(const LinphoneCore *lc);
+/**
+ * Get the name of the current preferred video size for sending.
+ * @param[in] lc #LinphoneCore object.
+ * @return A string containing the name of the current preferred video size (to be freed with ms_free()).
+ */
+LINPHONE_PUBLIC char * linphone_core_get_preferred_video_size_name(const LinphoneCore *lc);
+/**
+ * Sets the preferred video size by its name.
+ *
+ * @ingroup media_parameters
+ * This is identical to linphone_core_set_preferred_video_size() except
+ * that it takes the name of the video resolution as input.
+ * Video resolution names are: qcif, svga, cif, vga, 4cif, svga ...
+**/
+LINPHONE_PUBLIC void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char *name);
+/**
+ * Set the preferred frame rate for video.
+ * Based on the available bandwidth constraints and network conditions, the video encoder
+ * remains free to lower the framerate. There is no warranty that the preferred frame rate be the actual framerate.
+ * used during a call. Default value is 0, which means "use encoder's default fps value".
+ * @ingroup media_parameters
+ * @param lc the LinphoneCore
+ * @param fps the target frame rate in number of frames per seconds.
+**/
+LINPHONE_PUBLIC void linphone_core_set_preferred_framerate(LinphoneCore *lc, float fps);
+/**
+ * Returns the preferred video framerate, previously set by linphone_core_set_preferred_framerate().
+ * @ingroup media_parameters
+ * @param lc the linphone core
+ * @return frame rate in number of frames per seconds.
+**/
+LINPHONE_PUBLIC float linphone_core_get_preferred_framerate(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_enable_video_preview(LinphoneCore *lc, bool_t val);
+LINPHONE_PUBLIC bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val);
+LINPHONE_PUBLIC bool_t linphone_core_self_view_enabled(const LinphoneCore *lc);
+
+
+/**
+ * Update detection of camera devices.
+ *
+ * Use this function when the application is notified of USB plug events, so that
+ * list of available hardwares for video capture is updated.
+ * @param[in] lc #LinphoneCore object.
+ * @ingroup media_parameters
+ **/
+LINPHONE_PUBLIC void linphone_core_reload_video_devices(LinphoneCore *lc);
/* returns a null terminated static array of string describing the webcams */
-void linphone_core_reload_video_devices(LinphoneCore *lc);
-const char** linphone_core_get_video_devices(const LinphoneCore *lc);
-int linphone_core_set_video_device(LinphoneCore *lc, const char *id);
-const char *linphone_core_get_video_device(const LinphoneCore *lc);
+LINPHONE_PUBLIC const char** linphone_core_get_video_devices(const LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_set_video_device(LinphoneCore *lc, const char *id);
+LINPHONE_PUBLIC const char *linphone_core_get_video_device(const LinphoneCore *lc);
/* Set and get static picture to be used when "Static picture" is the video device */
-int linphone_core_set_static_picture(LinphoneCore *lc, const char *path);
-const char *linphone_core_get_static_picture(LinphoneCore *lc);
+/**
+ * Set the path to the image file to stream when "Static picture" is set as the video device.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] path The path to the image file to use.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC int linphone_core_set_static_picture(LinphoneCore *lc, const char *path);
-/* Set and get frame rate for static picture */
-int linphone_core_set_static_picture_fps(LinphoneCore *lc, float fps);
-float linphone_core_get_static_picture_fps(LinphoneCore *lc);
+/**
+ * Get the path to the image file streamed when "Static picture" is set as the video device.
+ * @param[in] lc #LinphoneCore object.
+ * @return The path to the image file streamed when "Static picture" is set as the video device.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC const char *linphone_core_get_static_picture(LinphoneCore *lc);
+
+/**
+ * Set the frame rate for static picture.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] fps The new frame rate to use for static picture.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC int linphone_core_set_static_picture_fps(LinphoneCore *lc, float fps);
+
+/**
+ * Get the frame rate for static picture
+ * @param[in] lc #LinphoneCore object.
+ * @return The frame rate used for static picture.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC float linphone_core_get_static_picture_fps(LinphoneCore *lc);
/*function to be used for eventually setting window decorations (icons, title...)*/
-unsigned long linphone_core_get_native_video_window_id(const LinphoneCore *lc);
-void linphone_core_set_native_video_window_id(LinphoneCore *lc, unsigned long id);
+LINPHONE_PUBLIC void * linphone_core_get_native_video_window_id(const LinphoneCore *lc);
-unsigned long linphone_core_get_native_preview_window_id(const LinphoneCore *lc);
-void linphone_core_set_native_preview_window_id(LinphoneCore *lc, unsigned long id);
+/**
+ * @ingroup media_parameters
+ * For MacOS, Linux, Windows: core will create its own window
+ * */
+#define LINPHONE_VIDEO_DISPLAY_AUTO (void*)((unsigned long) 0)
+/**
+ * @ingroup media_parameters
+ * For MacOS, Linux, Windows: do nothing
+ * */
-void linphone_core_use_preview_window(LinphoneCore *lc, bool_t yesno);
-int linphone_core_get_device_rotation(LinphoneCore *lc );
-void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation);
+#define LINPHONE_VIDEO_DISPLAY_NONE (void*)((unsigned long) -1)
+/**
+ * @ingroup media_parameters
+ * Set the native video window id where the video is to be displayed.
+ * For MacOS, Linux, Windows: if not set or LINPHONE_VIDEO_DISPLAY_AUTO the core will create its own window, unless the special id LINPHONE_VIDEO_DISPLAY_NONE is given.
+**/
+LINPHONE_PUBLIC void linphone_core_set_native_video_window_id(LinphoneCore *lc, void *id);
+
+LINPHONE_PUBLIC void * linphone_core_get_native_preview_window_id(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_native_preview_window_id(LinphoneCore *lc, void *id);
+
+/**
+ * Tells the core to use a separate window for local camera preview video, instead of
+ * inserting local view within the remote video window.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] yesno TRUE to use a separate window, FALSE to insert the preview in the remote video window.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_use_preview_window(LinphoneCore *lc, bool_t yesno);
+
+LINPHONE_PUBLIC int linphone_core_get_device_rotation(LinphoneCore *lc );
+LINPHONE_PUBLIC void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation);
+
+/**
+ * Get the camera sensor rotation.
+ *
+ * This is needed on some mobile platforms to get the number of degrees the camera sensor
+ * is rotated relative to the screen.
+ *
+ * @param lc The linphone core related to the operation
+ * @return The camera sensor rotation in degrees (0 to 360) or -1 if it could not be retrieved
+ */
+LINPHONE_PUBLIC int linphone_core_get_camera_sensor_rotation(LinphoneCore *lc);
/* start or stop streaming video in case of embedded window */
void linphone_core_show_video(LinphoneCore *lc, bool_t show);
-/*play/record support: use files instead of soundcard*/
-void linphone_core_use_files(LinphoneCore *lc, bool_t yesno);
-void linphone_core_set_play_file(LinphoneCore *lc, const char *file);
-void linphone_core_set_record_file(LinphoneCore *lc, const char *file);
+/** @deprecated Use linphone_core_set_use_files() instead. */
+#define linphone_core_use_files(lc, yesno) linphone_core_set_use_files(lc, yesno)
+/**
+ * Ask the core to stream audio from and to files, instead of using the soundcard.
+ * @ingroup media_parameters
+ * @param[in] lc LinphoneCore object
+ * @param[in] yesno A boolean value asking to stream audio from and to files or not.
+**/
+LINPHONE_PUBLIC void linphone_core_set_use_files(LinphoneCore *lc, bool_t yesno);
-void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms);
-void linphone_core_stop_dtmf(LinphoneCore *lc);
+/**
+ * Get the wav file that is played when putting somebody on hold,
+ * or when files are used instead of soundcards (see linphone_core_set_use_files()).
+ *
+ * The file is a 16 bit linear wav file.
+ * @ingroup media_parameters
+ * @param[in] lc LinphoneCore object
+ * @return The path to the file that is played when putting somebody on hold.
+ */
+LINPHONE_PUBLIC const char * linphone_core_get_play_file(const LinphoneCore *lc);
-int linphone_core_get_current_call_duration(const LinphoneCore *lc);
+/**
+ * Sets a wav file to be played when putting somebody on hold,
+ * or when files are used instead of soundcards (see linphone_core_set_use_files()).
+ *
+ * The file must be a 16 bit linear wav file.
+ * @ingroup media_parameters
+ * @param[in] lc LinphoneCore object
+ * @param[in] file The path to the file to be played when putting somebody on hold.
+**/
+LINPHONE_PUBLIC void linphone_core_set_play_file(LinphoneCore *lc, const char *file);
+
+/**
+ * Get the wav file where incoming stream is recorded,
+ * when files are used instead of soundcards (see linphone_core_set_use_files()).
+ *
+ * This feature is different from call recording (linphone_call_params_set_record_file())
+ * The file is a 16 bit linear wav file.
+ * @ingroup media_parameters
+ * @param[in] lc LinphoneCore object
+ * @return The path to the file where incoming stream is recorded.
+**/
+LINPHONE_PUBLIC const char * linphone_core_get_record_file(const LinphoneCore *lc);
+
+/**
+ * Sets a wav file where incoming stream is to be recorded,
+ * when files are used instead of soundcards (see linphone_core_set_use_files()).
+ *
+ * This feature is different from call recording (linphone_call_params_set_record_file())
+ * The file will be a 16 bit linear wav file.
+ * @ingroup media_parameters
+ * @param[in] lc LinphoneCore object
+ * @param[in] file The path to the file where incoming stream is to be recorded.
+**/
+LINPHONE_PUBLIC void linphone_core_set_record_file(LinphoneCore *lc, const char *file);
+
+LINPHONE_PUBLIC void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms);
+LINPHONE_PUBLIC void linphone_core_stop_dtmf(LinphoneCore *lc);
+
+LINPHONE_PUBLIC int linphone_core_get_current_call_duration(const LinphoneCore *lc);
-int linphone_core_get_mtu(const LinphoneCore *lc);
-void linphone_core_set_mtu(LinphoneCore *lc, int mtu);
+LINPHONE_PUBLIC int linphone_core_get_mtu(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_mtu(LinphoneCore *lc, int mtu);
/**
* @ingroup network_parameters
@@ -1301,44 +3559,52 @@ LINPHONE_PUBLIC void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t
* @ingroup network_parameters
* return network state either as positioned by the application or by linphone itself.
*/
-bool_t linphone_core_is_network_reachable(LinphoneCore* lc);
+LINPHONE_PUBLIC bool_t linphone_core_is_network_reachable(LinphoneCore* lc);
/**
* @ingroup network_parameters
* enable signaling keep alive. small udp packet sent periodically to keep udp NAT association
*/
-void linphone_core_enable_keep_alive(LinphoneCore* lc,bool_t enable);
+LINPHONE_PUBLIC void linphone_core_enable_keep_alive(LinphoneCore* lc,bool_t enable);
/**
* @ingroup network_parameters
* Is signaling keep alive
*/
-bool_t linphone_core_keep_alive_enabled(LinphoneCore* lc);
+LINPHONE_PUBLIC bool_t linphone_core_keep_alive_enabled(LinphoneCore* lc);
-LINPHONE_PUBLIC void *linphone_core_get_user_data(LinphoneCore *lc);
+LINPHONE_PUBLIC void *linphone_core_get_user_data(const LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_set_user_data(LinphoneCore *lc, void *userdata);
/* returns LpConfig object to read/write to the config file: usefull if you wish to extend
the config file with your own sections */
-struct _LpConfig *linphone_core_get_config(LinphoneCore *lc);
+LINPHONE_PUBLIC LpConfig * linphone_core_get_config(LinphoneCore *lc);
+
+/**
+ * Create a LpConfig object from a user config file.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] filename The filename of the config file to read to fill the instantiated LpConfig
+ * @ingroup misc
+ */
+LINPHONE_PUBLIC LpConfig * linphone_core_create_lp_config(LinphoneCore *lc, const char *filename);
/*set a callback for some blocking operations, it takes you informed of the progress of the operation*/
-void linphone_core_set_waiting_callback(LinphoneCore *lc, LinphoneWaitingCallback cb, void *user_context);
+LINPHONE_PUBLIC void linphone_core_set_waiting_callback(LinphoneCore *lc, LinphoneCoreWaitingCallback cb, void *user_context);
/*returns the list of registered SipSetup (linphonecore plugins) */
-const MSList * linphone_core_get_sip_setups(LinphoneCore *lc);
+LINPHONE_PUBLIC const MSList * linphone_core_get_sip_setups(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_destroy(LinphoneCore *lc);
/*for advanced users:*/
-typedef RtpTransport * (*LinphoneRtpTransportFactoryFunc)(void *data, int port);
+typedef RtpTransport * (*LinphoneCoreRtpTransportFactoryFunc)(void *data, int port);
struct _LinphoneRtpTransportFactories{
- LinphoneRtpTransportFactoryFunc audio_rtp_func;
+ LinphoneCoreRtpTransportFactoryFunc audio_rtp_func;
void *audio_rtp_func_data;
- LinphoneRtpTransportFactoryFunc audio_rtcp_func;
+ LinphoneCoreRtpTransportFactoryFunc audio_rtcp_func;
void *audio_rtcp_func_data;
- LinphoneRtpTransportFactoryFunc video_rtp_func;
+ LinphoneCoreRtpTransportFactoryFunc video_rtp_func;
void *video_rtp_func_data;
- LinphoneRtpTransportFactoryFunc video_rtcp_func;
+ LinphoneCoreRtpTransportFactoryFunc video_rtcp_func;
void *video_rtcp_func_data;
};
typedef struct _LinphoneRtpTransportFactories LinphoneRtpTransportFactories;
@@ -1347,80 +3613,422 @@ void linphone_core_set_rtp_transport_factories(LinphoneCore* lc, LinphoneRtpTran
int linphone_core_get_current_call_stats(LinphoneCore *lc, rtp_stats_t *local, rtp_stats_t *remote);
-int linphone_core_get_calls_nb(const LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_get_calls_nb(const LinphoneCore *lc);
LINPHONE_PUBLIC const MSList *linphone_core_get_calls(LinphoneCore *lc);
-LinphoneGlobalState linphone_core_get_global_state(const LinphoneCore *lc);
+LINPHONE_PUBLIC LinphoneGlobalState linphone_core_get_global_state(const LinphoneCore *lc);
/**
* force registration refresh to be initiated upon next iterate
* @ingroup proxies
*/
-void linphone_core_refresh_registers(LinphoneCore* lc);
+LINPHONE_PUBLIC void linphone_core_refresh_registers(LinphoneCore* lc);
-/* Path to the file storing secrets cache */
-void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file);
-const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc);
+/**
+ * Set the path to the file storing the zrtp secrets cache.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] file The path to the file to use to store the zrtp secrets cache.
+ * @ingroup initializing
+ */
+LINPHONE_PUBLIC void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file);
-const LinphoneCall* linphone_core_find_call_from_uri(LinphoneCore *lc, const char *uri);
+/**
+ * Get the path to the file storing the zrtp secrets cache.
+ * @param[in] lc #LinphoneCore object.
+ * @return The path to the file storing the zrtp secrets cache.
+ * @ingroup initializing
+ */
+LINPHONE_PUBLIC const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc);
+
+/**
+ * Set the path to the directory storing the user's x509 certificates (used by dtls)
+ * @param[in] lc #LinphoneCore object
+ * @param[in] path The path to the directory to use to store the user's certificates.
+ * @ingroup initializing
+ */
+LINPHONE_PUBLIC void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path);
+
+/**
+ * Get the path to the directory storing the user's certificates.
+ * @param[in] lc #LinphoneCore object.
+ * @returns The path to the directory storing the user's certificates.
+ * @ingroup initializing
+ */
+LINPHONE_PUBLIC const char *linphone_core_get_user_certificates_path(LinphoneCore *lc);
+
+/**
+ * Search from the list of current calls if a remote address match uri
+ * @ingroup call_control
+ * @param lc
+ * @param uri which should match call remote uri
+ * @return LinphoneCall or NULL is no match is found
+ */
+LINPHONE_PUBLIC LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri);
LINPHONE_PUBLIC int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call);
-int linphone_core_add_all_to_conference(LinphoneCore *lc);
-int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call);
+LINPHONE_PUBLIC int linphone_core_add_all_to_conference(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call);
+/**
+ * Indicates whether the local participant is part of a conference.
+ * @param lc the linphone core
+ * @return TRUE if the local participant is in a conference, FALSE otherwise.
+**/
LINPHONE_PUBLIC bool_t linphone_core_is_in_conference(const LinphoneCore *lc);
-int linphone_core_enter_conference(LinphoneCore *lc);
-int linphone_core_leave_conference(LinphoneCore *lc);
-float linphone_core_get_conference_local_input_volume(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_enter_conference(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_leave_conference(LinphoneCore *lc);
+LINPHONE_PUBLIC float linphone_core_get_conference_local_input_volume(LinphoneCore *lc);
LINPHONE_PUBLIC int linphone_core_terminate_conference(LinphoneCore *lc);
LINPHONE_PUBLIC int linphone_core_get_conference_size(LinphoneCore *lc);
-int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path);
-int linphone_core_stop_conference_recording(LinphoneCore *lc);
+LINPHONE_PUBLIC int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path);
+LINPHONE_PUBLIC int linphone_core_stop_conference_recording(LinphoneCore *lc);
+/**
+ * Get the maximum number of simultaneous calls Linphone core can manage at a time. All new call above this limit are declined with a busy answer
+ * @ingroup initializing
+ * @param lc core
+ * @return max number of simultaneous calls
+ */
+LINPHONE_PUBLIC int linphone_core_get_max_calls(LinphoneCore *lc);
+/**
+ * Set the maximum number of simultaneous calls Linphone core can manage at a time. All new call above this limit are declined with a busy answer
+ * @ingroup initializing
+ * @param lc core
+ * @param max number of simultaneous calls
+ */
+LINPHONE_PUBLIC void linphone_core_set_max_calls(LinphoneCore *lc, int max);
-int linphone_core_get_max_calls(LinphoneCore *lc);
-void linphone_core_set_max_calls(LinphoneCore *lc, int max);
+LINPHONE_PUBLIC bool_t linphone_core_sound_resources_locked(LinphoneCore *lc);
+/**
+ * @ingroup initializing
+ * Check if a media encryption type is supported
+ * @param lc core
+ * @param menc LinphoneMediaEncryption
+ * @return whether a media encryption scheme is supported by the LinphoneCore engine
+**/
-bool_t linphone_core_sound_resources_locked(LinphoneCore *lc);
-
-bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, LinphoneMediaEncryption menc);
+LINPHONE_PUBLIC bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, LinphoneMediaEncryption menc);
/**
- * Choose media encryption policy to be used for RTP packets
+ * Choose the media encryption policy to be used for RTP packets.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] menc The media encryption policy to be used.
+ * @return 0 if successful, any other value otherwise.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption menc);
+
+/**
+ * Get the media encryption policy being used for RTP packets.
+ * @param[in] lc #LinphoneCore object.
+ * @return The media encryption policy being used.
+ * @ingroup media_parameters
*/
-LINPHONE_PUBLIC int linphone_core_set_media_encryption(LinphoneCore *lc, enum LinphoneMediaEncryption menc);
LINPHONE_PUBLIC LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc);
-bool_t linphone_core_is_media_encryption_mandatory(LinphoneCore *lc);
/**
- * Defines Linphone behaviour when encryption parameters negociation fails on outoing call.
- * If set to TRUE call will fail; if set to FALSE will resend an INVITE with encryption disabled
+ * Get behaviour when encryption parameters negociation fails on outgoing call.
+ * @param[in] lc #LinphoneCore object.
+ * @return TRUE means the call will fail; FALSE means an INVITE will be resent with encryption disabled.
+ * @ingroup media_parameters
*/
-void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m);
+LINPHONE_PUBLIC bool_t linphone_core_is_media_encryption_mandatory(LinphoneCore *lc);
+
+/**
+ * Define behaviour when encryption parameters negociation fails on outgoing call.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] m If set to TRUE call will fail; if set to FALSE will resend an INVITE with encryption disabled.
+ * @ingroup media_parameters
+ */
+LINPHONE_PUBLIC void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m);
/**
* Init call params using LinphoneCore's current configuration
*/
-void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *params);
+LINPHONE_PUBLIC void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *params);
/**
* True if tunnel support was compiled.
+ * @ingroup tunnel
*/
-bool_t linphone_core_tunnel_available(void);
+LINPHONE_PUBLIC bool_t linphone_core_tunnel_available(void);
+/**
+ * Linphone tunnel object.
+ * @ingroup tunnel
+ */
typedef struct _LinphoneTunnel LinphoneTunnel;
+
/**
* get tunnel instance if available
+* @ingroup tunnel
+* @param lc core object
+* @returns LinphoneTunnel or NULL if not available
*/
-LinphoneTunnel *linphone_core_get_tunnel(LinphoneCore *lc);
+LINPHONE_PUBLIC LinphoneTunnel *linphone_core_get_tunnel(const LinphoneCore *lc);
-void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp);
-int linphone_core_get_sip_dscp(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp);
+LINPHONE_PUBLIC int linphone_core_get_sip_dscp(const LinphoneCore *lc);
-void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp);
-int linphone_core_get_audio_dscp(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp);
+LINPHONE_PUBLIC int linphone_core_get_audio_dscp(const LinphoneCore *lc);
-void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp);
-int linphone_core_get_video_dscp(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp);
+LINPHONE_PUBLIC int linphone_core_get_video_dscp(const LinphoneCore *lc);
+
+LINPHONE_PUBLIC const char *linphone_core_get_video_display_filter(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_video_display_filter(LinphoneCore *lc, const char *filtername);
+
+/** Contact Providers
+ */
+
+typedef unsigned int ContactSearchID;
+
+typedef struct _LinphoneContactSearch LinphoneContactSearch;
+typedef struct _LinphoneContactProvider LinphoneContactProvider;
+
+typedef void (*ContactSearchCallback)( LinphoneContactSearch* id, MSList* friends, void* data );
+
+/*
+ * Remote provisioning
+ */
+
+/**
+ * Set URI where to download xml configuration file at startup.
+ * This can also be set from configuration file or factory config file, from [misc] section, item "config-uri".
+ * Calling this function does not load the configuration. It will write the value into configuration so that configuration
+ * from remote URI will take place at next LinphoneCore start.
+ * @param lc the linphone core
+ * @param uri the http or https uri to use in order to download the configuration.
+ * @ingroup initializing
+**/
+LINPHONE_PUBLIC void linphone_core_set_provisioning_uri(LinphoneCore *lc, const char*uri);
+
+/**
+ * Get provisioning URI.
+ * @param lc the linphone core
+ * @return the provisioning URI.
+ * @ingroup initializing
+**/
+LINPHONE_PUBLIC const char* linphone_core_get_provisioning_uri(const LinphoneCore *lc);
+
+/**
+ * Gets if the provisioning URI should be removed after it's been applied successfully
+ * @param lc the linphone core
+ * @return TRUE if the provisioning URI should be removed, FALSE otherwise
+ */
+LINPHONE_PUBLIC bool_t linphone_core_is_provisioning_transient(LinphoneCore *lc);
+
+LINPHONE_PUBLIC int linphone_core_migrate_to_multi_transport(LinphoneCore *lc);
+
+
+/**
+ * Control when media offer is sent in SIP INVITE.
+ * @param lc the linphone core
+ * @param enable true if INVITE has to be sent whitout SDP.
+ * @ingroup network_parameters
+**/
+LINPHONE_PUBLIC void linphone_core_enable_sdp_200_ack(LinphoneCore *lc, bool_t enable);
+/**
+ * Media offer control param for SIP INVITE.
+ * @return true if INVITE has to be sent whitout SDP.
+ * @ingroup network_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_core_sdp_200_ack_enabled(const LinphoneCore *lc);
+
+
+/**
+ * Enum listing frequent telephony tones.
+**/
+enum _LinphoneToneID{
+ LinphoneToneUndefined, /** For incoming calls behavior is unchanged.
+ * @param core #LinphoneCore
+ * @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_core_enable_audio_multicast(LinphoneCore *core, bool_t yesno);
+
+/**
+ * Use to get multicast state of audio stream.
+ * @param core #LinphoneCore
+ * @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_core_audio_multicast_enabled(const LinphoneCore *core);
+
+/**
+ * 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 #LinphoneCore
+ * @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_core_enable_video_multicast(LinphoneCore *core, bool_t yesno);
+/**
+ * Use to get multicast state of video stream.
+ * @param core #LinphoneCore
+ * @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_core_video_multicast_enabled(const LinphoneCore *core);
+
+/**
+ * Set the network simulator parameters.
+ * Liblinphone has the capabability of simulating the effects of a network (latency, lost packets, jitter, max bandwidth).
+ * Please refer to the oRTP documentation for the meaning of the parameters of the OrtpNetworkSimulatorParams structure.
+ * This function has effect for future calls, but not for currently running calls, though this behavior may be changed in future versions.
+ * @warning Due to design of network simulation in oRTP, simulation is applied independently for audio and video stream. This means for example that a bandwidth
+ * limit of 250kbit/s will have no effect on an audio stream running at 40kbit/s while a videostream targetting 400kbit/s will be highly affected.
+ * @param lc the LinphoneCore
+ * @param params the parameters used for the network simulation.
+ * @return 0 if successful, -1 otherwise.
+**/
+LINPHONE_PUBLIC int linphone_core_set_network_simulator_params(LinphoneCore *lc, const OrtpNetworkSimulatorParams *params);
+
+
+/**
+ * Get the previously set network simulation parameters.
+ * @see linphone_core_set_network_simulator_params
+ * @return a OrtpNetworkSimulatorParams structure.
+**/
+LINPHONE_PUBLIC const OrtpNetworkSimulatorParams *linphone_core_get_network_simulator_params(const LinphoneCore *lc);
+
+/**
+ * Set the video preset to be used for video calls.
+ * @param[in] lc LinphoneCore object
+ * @param[in] preset The name of the video preset to be used (can be NULL to use the default video preset).
+ */
+LINPHONE_PUBLIC void linphone_core_set_video_preset(LinphoneCore *lc, const char *preset);
+
+/**
+ * Get the video preset used for video calls.
+ * @param[in] lc LinphoneCore object
+ * @return The name of the video preset used for video calls (can be NULL if the default video preset is used).
+ */
+LINPHONE_PUBLIC const char * linphone_core_get_video_preset(const LinphoneCore *lc);
#ifdef __cplusplus
}
diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc
index 57ab7c4ca..ab34a3604 100644
--- a/coreapi/linphonecore_jni.cc
+++ b/coreapi/linphonecore_jni.cc
@@ -21,11 +21,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "linphonecore_jni.h"
#endif
#include "linphonecore_utils.h"
-#include
+#include
extern "C" {
#include "mediastreamer2/mediastream.h"
+#include "mediastreamer2/mscommon.h"
+#include "mediastreamer2/msmediaplayer.h"
+#include "mediastreamer2/msutils.h"
+#include "devices.h"
}
#include "mediastreamer2/msjava.h"
#include "private.h"
@@ -39,6 +43,9 @@ extern "C" void libmsilbc_init();
#ifdef HAVE_X264
extern "C" void libmsx264_init();
#endif
+#ifdef HAVE_OPENH264
+extern "C" void libmsopenh264_init();
+#endif
#ifdef HAVE_AMR
extern "C" void libmsamr_init();
#endif
@@ -48,19 +55,47 @@ extern "C" void libmssilk_init();
#ifdef HAVE_G729
extern "C" void libmsbcg729_init();
#endif
+#ifdef HAVE_WEBRTC
+extern "C" void libmswebrtc_init();
+#endif
+#ifdef HAVE_CODEC2
+extern "C" void libmscodec2_init();
+#endif
+#include
#endif /*ANDROID*/
+#define RETURN_USER_DATA_OBJECT(javaclass, funcprefix, cobj) \
+ { \
+ jclass jUserDataObjectClass; \
+ jmethodID jUserDataObjectCtor; \
+ jobject jUserDataObj; \
+ jUserDataObj = (jobject)funcprefix ## _get_user_data(cobj); \
+ if (jUserDataObj == NULL) { \
+ jUserDataObjectClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/" javaclass)); \
+ jUserDataObjectCtor = env->GetMethodID(jUserDataObjectClass,"", "(J)V"); \
+ jUserDataObj = env->NewObject(jUserDataObjectClass, jUserDataObjectCtor, (jlong)funcprefix ## _ref(cobj)); \
+ jUserDataObj = env->NewGlobalRef(jUserDataObj); \
+ funcprefix ## _set_user_data(cobj, jUserDataObj); \
+ env->DeleteGlobalRef(jUserDataObjectClass); \
+ } \
+ return jUserDataObj; \
+ }
+
static JavaVM *jvm=0;
static const char* LogDomain = "Linphone";
+static jclass handler_class;
+static jmethodID loghandler_id;
+static jobject handler_obj=NULL;
+
+static jobject create_java_linphone_content(JNIEnv *env, const LinphoneContent *content);
+static jobject create_java_linphone_buffer(JNIEnv *env, const LinphoneBuffer *buffer);
+static LinphoneBuffer* create_c_linphone_buffer_from_java_linphone_buffer(JNIEnv *env, jobject jbuffer);
#ifdef ANDROID
-void linphone_android_log_handler(int prio, const char *fmt, va_list args) {
- char str[4096];
+void linphone_android_log_handler(int prio, char *str) {
char *current;
char *next;
- vsnprintf(str, sizeof(str) - 1, fmt, args);
- str[sizeof(str) - 1] = '\0';
if (strlen(str) < 512) {
__android_log_write(prio, LogDomain, str);
} else {
@@ -75,27 +110,48 @@ void linphone_android_log_handler(int prio, const char *fmt, va_list args) {
}
static void linphone_android_ortp_log_handler(OrtpLogLevel lev, const char *fmt, va_list args) {
+ char str[4096];
+ const char *levname="undef";
+ vsnprintf(str, sizeof(str) - 1, fmt, args);
+ str[sizeof(str) - 1] = '\0';
+
int prio;
switch(lev){
- case ORTP_DEBUG: prio = ANDROID_LOG_DEBUG; break;
- case ORTP_MESSAGE: prio = ANDROID_LOG_INFO; break;
- case ORTP_WARNING: prio = ANDROID_LOG_WARN; break;
- case ORTP_ERROR: prio = ANDROID_LOG_ERROR; break;
- case ORTP_FATAL: prio = ANDROID_LOG_FATAL; break;
- default: prio = ANDROID_LOG_DEFAULT; break;
+ case ORTP_DEBUG: prio = ANDROID_LOG_DEBUG; levname="debug"; break;
+ case ORTP_MESSAGE: prio = ANDROID_LOG_INFO; levname="message"; break;
+ case ORTP_WARNING: prio = ANDROID_LOG_WARN; levname="warning"; break;
+ case ORTP_ERROR: prio = ANDROID_LOG_ERROR; levname="error"; break;
+ case ORTP_FATAL: prio = ANDROID_LOG_FATAL; levname="fatal"; break;
+ default: prio = ANDROID_LOG_DEFAULT; break;
}
- linphone_android_log_handler(prio, fmt, args);
+ if (handler_obj){
+ JNIEnv *env=ms_get_jni_env();
+ jstring jdomain=env->NewStringUTF(LogDomain);
+ jstring jlevname=env->NewStringUTF(levname);
+ jstring jstr=env->NewStringUTF(str);
+ env->CallVoidMethod(handler_obj,loghandler_id,jdomain,(jint)lev,jlevname,jstr,NULL);
+ if (jdomain) env->DeleteLocalRef(jdomain);
+ if (jlevname) env->DeleteLocalRef(jlevname);
+ if (jstr) env->DeleteLocalRef(jstr);
+ }else
+ linphone_android_log_handler(prio, str);
}
int dumbMethodForAllowingUsageOfCpuFeaturesFromStaticLibMediastream() {
return (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
}
+
+int dumbMethodForAllowingUsageOfMsAudioDiffFromStaticLibMediastream() {
+ return ms_audio_diff(NULL, NULL, NULL, 0, NULL, NULL);
+}
#endif /*ANDROID*/
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved)
{
#ifdef ANDROID
ms_set_jvm(ajvm);
+
#endif /*ANDROID*/
jvm=ajvm;
return JNI_VERSION_1_2;
@@ -114,99 +170,325 @@ extern "C" void Java_org_linphone_core_LinphoneCoreFactoryImpl_setDebugMode(JNIE
linphone_core_disable_logs();
}
}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreFactoryImpl_enableLogCollection(JNIEnv* env
+ ,jobject thiz
+ ,jboolean enable) {
+ linphone_core_enable_log_collection(enable ? LinphoneLogCollectionEnabledWithoutPreviousLogHandler : LinphoneLogCollectionDisabled);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreFactoryImpl_setLogCollectionPath(JNIEnv* env
+ ,jobject thiz
+ ,jstring jpath) {
+
+ const char* path = env->GetStringUTFChars(jpath, NULL);
+ linphone_core_set_log_collection_path(path);
+ env->ReleaseStringUTFChars(jpath, path);
+}
// LinphoneCore
+/*
+ * returns the java LinphoneProxyConfig associated with a C LinphoneProxyConfig.
+**/
+jobject getProxy(JNIEnv *env, LinphoneProxyConfig *proxy, jobject core){
+ jobject jobj=0;
+
+ if (proxy!=NULL){
+ jclass proxyClass = (jclass)env->FindClass("org/linphone/core/LinphoneProxyConfigImpl");
+ jmethodID proxyCtrId = env->GetMethodID(proxyClass,"", "(Lorg/linphone/core/LinphoneCoreImpl;J)V");
+
+ void *up=linphone_proxy_config_get_user_data(proxy);
+
+ if (up==NULL){
+ jobj=env->NewObject(proxyClass,proxyCtrId,core,(jlong)proxy);
+ linphone_proxy_config_set_user_data(proxy,(void*)env->NewWeakGlobalRef(jobj));
+ linphone_proxy_config_ref(proxy);
+ }else{
+ //promote the weak ref to local ref
+ jobj=env->NewLocalRef((jobject)up);
+ if (jobj == NULL){
+ //the weak ref was dead
+ jobj=env->NewObject(proxyClass,proxyCtrId,core,(jlong)proxy);
+ linphone_proxy_config_set_user_data(proxy,(void*)env->NewWeakGlobalRef(jobj));
+ }
+ }
+ env->DeleteLocalRef(proxyClass);
+ }
+ return jobj;
+}
+
+jobject getCall(JNIEnv *env, LinphoneCall *call){
+ jobject jobj=0;
+
+ if (call!=NULL){
+ jclass callClass = (jclass)env->FindClass("org/linphone/core/LinphoneCallImpl");
+ jmethodID callCtrId = env->GetMethodID(callClass,"", "(J)V");
+
+ void *up=linphone_call_get_user_pointer(call);
+
+ if (up==NULL){
+ jobj=env->NewObject(callClass,callCtrId,(jlong)call);
+ jobj=env->NewGlobalRef(jobj);
+ linphone_call_set_user_pointer(call,(void*)jobj);
+ linphone_call_ref(call);
+ }else{
+ jobj=(jobject)up;
+ }
+ env->DeleteLocalRef(callClass);
+ }
+ return jobj;
+}
+
+jobject getChatMessage(JNIEnv *env, LinphoneChatMessage *msg){
+ jobject jobj = 0;
+
+ if (msg != NULL){
+ jclass chatMessageClass = (jclass)env->FindClass("org/linphone/core/LinphoneChatMessageImpl");
+ jmethodID chatMessageCtrId = env->GetMethodID(chatMessageClass,"", "(J)V");
+
+ void *up = linphone_chat_message_get_user_data(msg);
+
+ if (up == NULL) {
+ jobj = env->NewObject(chatMessageClass,chatMessageCtrId,(jlong)linphone_chat_message_ref(msg));
+ jobj = env->NewGlobalRef(jobj);
+ linphone_chat_message_set_user_data(msg,(void*)jobj);
+ } else {
+ jobj = (jobject)up;
+ }
+ env->DeleteLocalRef(chatMessageClass);
+ }
+ return jobj;
+}
+
+jobject getFriend(JNIEnv *env, LinphoneFriend *lfriend){
+ jobject jobj=0;
+
+ if (lfriend != NULL){
+ jclass friendClass = (jclass)env->FindClass("org/linphone/core/LinphoneFriendImpl");
+ jmethodID friendCtrId = env->GetMethodID(friendClass,"", "(J)V");
+
+ void *up=linphone_friend_get_user_data(lfriend);
+
+ if (up == NULL){
+ jobj=env->NewObject(friendClass,friendCtrId,(jlong)lfriend);
+ linphone_friend_set_user_data(lfriend,(void*)env->NewWeakGlobalRef(jobj));
+ linphone_friend_ref(lfriend);
+ }else{
+
+ jobj=env->NewLocalRef((jobject)up);
+ if (jobj == NULL){
+ jobj=env->NewObject(friendClass,friendCtrId,(jlong)lfriend);
+ linphone_friend_set_user_data(lfriend,(void*)env->NewWeakGlobalRef(jobj));
+ }
+ }
+ env->DeleteLocalRef(friendClass);
+ }
+ return jobj;
+}
+
+jobject getEvent(JNIEnv *env, LinphoneEvent *lev){
+ if (lev==NULL) return NULL;
+ jobject jev=(jobject)linphone_event_get_user_data(lev);
+ if (jev==NULL){
+ jclass linphoneEventClass = (jclass)env->FindClass("org/linphone/core/LinphoneEventImpl");
+ jmethodID linphoneEventCtrId = env->GetMethodID(linphoneEventClass,"", "(J)V");
+
+ jev=env->NewObject(linphoneEventClass,linphoneEventCtrId,(jlong)linphone_event_ref(lev));
+ jev=env->NewGlobalRef(jev);
+ linphone_event_set_user_data(lev,jev);
+
+ env->DeleteLocalRef(linphoneEventClass);
+ }
+ return jev;
+}
+
class LinphoneCoreData {
public:
- LinphoneCoreData(JNIEnv* env, jobject lc,jobject alistener, jobject auserdata) {
-
+ LinphoneCoreData(JNIEnv *env, jobject lc, LinphoneCoreVTable *vTable, jobject alistener) {
core = env->NewGlobalRef(lc);
listener = env->NewGlobalRef(alistener);
- userdata = auserdata?env->NewGlobalRef(auserdata):0;
- memset(&vTable,0,sizeof(vTable));
- vTable.show = showInterfaceCb;
- vTable.auth_info_requested = authInfoRequested;
- vTable.display_status = displayStatusCb;
- vTable.display_message = displayMessageCb;
- vTable.display_warning = displayMessageCb;
- vTable.global_state_changed = globalStateChange;
- vTable.registration_state_changed = registrationStateChange;
- vTable.call_state_changed = callStateChange;
- vTable.call_encryption_changed = callEncryptionChange;
- vTable.text_received = text_received;
- vTable.message_received = message_received;
- vTable.dtmf_received = dtmf_received;
- vTable.new_subscription_request = new_subscription_request;
- vTable.notify_presence_recv = notify_presence_recv;
- vTable.call_stats_updated = callStatsUpdated;
- listenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass( alistener));
+ memset(vTable, 0, sizeof(LinphoneCoreVTable));
+
+ listenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass(alistener));
/*displayStatus(LinphoneCore lc,String message);*/
displayStatusId = env->GetMethodID(listenerClass,"displayStatus","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;)V");
+ if (displayStatusId) {
+ vTable->display_status = displayStatusCb;
+ }
/*void generalState(LinphoneCore lc,int state); */
- globalStateId = env->GetMethodID(listenerClass,"globalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GlobalState;Ljava/lang/String;)V");
globalStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$GlobalState"));
globalStateFromIntId = env->GetStaticMethodID(globalStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$GlobalState;");
+ globalStateId = env->GetMethodID(listenerClass,"globalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GlobalState;Ljava/lang/String;)V");
+ if (globalStateId) {
+ vTable->global_state_changed = globalStateChange;
+ }
/*registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String smessage);*/
- registrationStateId = env->GetMethodID(listenerClass,"registrationState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneProxyConfig;Lorg/linphone/core/LinphoneCore$RegistrationState;Ljava/lang/String;)V");
registrationStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RegistrationState"));
registrationStateFromIntId = env->GetStaticMethodID(registrationStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RegistrationState;");
+ registrationStateId = env->GetMethodID(listenerClass,"registrationState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneProxyConfig;Lorg/linphone/core/LinphoneCore$RegistrationState;Ljava/lang/String;)V");
+ if (registrationStateId) {
+ vTable->registration_state_changed = registrationStateChange;
+ }
/*callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message);*/
- callStateId = env->GetMethodID(listenerClass,"callState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;Ljava/lang/String;)V");
callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State"));
callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;");
+ callStateId = env->GetMethodID(listenerClass,"callState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;Ljava/lang/String;)V");
+ if (callStateId) {
+ vTable->call_state_changed = callStateChange;
+ }
+
+ transferStateId = env->GetMethodID(listenerClass,"transferState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;)V");
+ if (transferStateId) {
+ vTable->transfer_state_changed = transferStateChanged;
+ }
/*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/
callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V");
+ if (callStatsUpdatedId) {
+ vTable->call_stats_updated = callStatsUpdated;
+ }
+
+ /*callEncryption(LinphoneCore lc, LinphoneCall call, boolean encrypted,String auth_token);*/
+ callEncryptionChangedId = env->GetMethodID(listenerClass,"callEncryptionChanged","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;ZLjava/lang/String;)V");
+ if (callEncryptionChangedId) {
+ vTable->call_encryption_changed = callEncryptionChange;
+ }
+
+ /*void ecCalibrationStatus(LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data);*/
+ ecCalibratorStatusClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$EcCalibratorStatus"));
+ ecCalibratorStatusFromIntId = env->GetStaticMethodID(ecCalibratorStatusClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;");
+ ecCalibrationStatusId = env->GetMethodID(listenerClass,"ecCalibrationStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;ILjava/lang/Object;)V");
+
+ /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/
+ newSubscriptionRequestId = env->GetMethodID(listenerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V");
+ if (newSubscriptionRequestId) {
+ vTable->new_subscription_requested = new_subscription_requested;
+ }
+
+ authInfoRequestedId = env->GetMethodID(listenerClass,"authInfoRequested","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ if (authInfoRequestedId) {
+ vTable->auth_info_requested = authInfoRequested;
+ }
+
+ /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/
+ notifyPresenceReceivedId = env->GetMethodID(listenerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V");
+ if (notifyPresenceReceivedId) {
+ vTable->notify_presence_received = notify_presence_received;
+ }
+
+ messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V");
+ if (messageReceivedId) {
+ vTable->message_received = message_received;
+ }
+
+ isComposingReceivedId = env->GetMethodID(listenerClass,"isComposingReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;)V");
+ if (isComposingReceivedId) {
+ vTable->is_composing_received = is_composing_received;
+ }
+
+ dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V");
+ if (dtmfReceivedId) {
+ vTable->dtmf_received = dtmf_received;
+ }
+
+ infoReceivedId = env->GetMethodID(listenerClass,"infoReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneInfoMessage;)V");
+ if (infoReceivedId) {
+ vTable->info_received = infoReceived;
+ }
+
+ subscriptionStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/SubscriptionState"));
+ subscriptionStateFromIntId = env->GetStaticMethodID(subscriptionStateClass,"fromInt","(I)Lorg/linphone/core/SubscriptionState;");
+ subscriptionStateId = env->GetMethodID(listenerClass,"subscriptionStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/SubscriptionState;)V");
+ if (subscriptionStateId) {
+ vTable->subscription_state_changed = subscriptionStateChanged;
+ }
+
+ publishStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/PublishState"));
+ publishStateFromIntId = env->GetStaticMethodID(publishStateClass,"fromInt","(I)Lorg/linphone/core/PublishState;");
+ publishStateId = env->GetMethodID(listenerClass,"publishStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/PublishState;)V");
+ if (publishStateId) {
+ vTable->publish_state_changed = publishStateChanged;
+ }
+
+ notifyRecvId = env->GetMethodID(listenerClass,"notifyReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Ljava/lang/String;Lorg/linphone/core/LinphoneContent;)V");
+ if (notifyRecvId) {
+ vTable->notify_received = notifyReceived;
+ }
+
+ configuringStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RemoteProvisioningState"));
+ configuringStateFromIntId = env->GetStaticMethodID(configuringStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;");
+ configuringStateId = env->GetMethodID(listenerClass,"configuringStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;Ljava/lang/String;)V");
+ if (configuringStateId) {
+ vTable->configuring_status = configuringStatus;
+ }
+
+ fileTransferProgressIndicationId = env->GetMethodID(listenerClass, "fileTransferProgressIndication", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;I)V");
+ if (fileTransferProgressIndicationId) {
+ vTable->file_transfer_progress_indication = fileTransferProgressIndication;
+ }
+
+ fileTransferSendId = env->GetMethodID(listenerClass, "fileTransferSend", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Ljava/nio/ByteBuffer;I)I");
+ if (fileTransferSendId) {
+ vTable->file_transfer_send = fileTransferSend;
+ }
+
+ fileTransferRecvId = env->GetMethodID(listenerClass, "fileTransferRecv", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;[BI)V");
+ if (fileTransferRecvId) {
+ vTable->file_transfer_recv = fileTransferRecv;
+ }
+
+ logCollectionUploadStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$LogCollectionUploadState"));
+ logCollectionUploadStateFromIntId = env->GetStaticMethodID(logCollectionUploadStateClass, "fromInt", "(I)Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;");
+ logCollectionUploadProgressId = env->GetMethodID(listenerClass, "uploadProgressIndication", "(Lorg/linphone/core/LinphoneCore;II)V");
+ if (logCollectionUploadProgressId) {
+ vTable->log_collection_upload_progress_indication = logCollectionUploadProgressIndication;
+ }
+ logCollectionUploadStateId = env->GetMethodID(listenerClass, "uploadStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;Ljava/lang/String;)V");
+ if (logCollectionUploadStateId) {
+ vTable->log_collection_upload_state_changed = logCollectionUploadStateChange;
+ }
chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State"));
chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;");
- /*callEncryption(LinphoneCore lc, LinphoneCall call, boolean encrypted,String auth_token);*/
- callEncryptionChangedId=env->GetMethodID(listenerClass,"callEncryptionChanged","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;ZLjava/lang/String;)V");
-
- /*void ecCalibrationStatus(LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data);*/
- ecCalibrationStatusId = env->GetMethodID(listenerClass,"ecCalibrationStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;ILjava/lang/Object;)V");
- ecCalibratorStatusClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$EcCalibratorStatus"));
- ecCalibratorStatusFromIntId = env->GetStaticMethodID(ecCalibratorStatusClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;");
-
- /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/
- newSubscriptionRequestId = env->GetMethodID(listenerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V");
-
- /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/
- notifyPresenceReceivedId = env->GetMethodID(listenerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V");
-
- /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/
- textReceivedId = env->GetMethodID(listenerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V");
- messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V");
- dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V");
-
proxyClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl"));
- proxyCtrId = env->GetMethodID(proxyClass,"", "(J)V");
+ proxyCtrId = env->GetMethodID(proxyClass,"", "(Lorg/linphone/core/LinphoneCoreImpl;J)V");
callClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallImpl"));
callCtrId = env->GetMethodID(callClass,"", "(J)V");
+ callSetAudioStatsId = env->GetMethodID(callClass, "setAudioStats", "(Lorg/linphone/core/LinphoneCallStats;)V");
+ callSetVideoStatsId = env->GetMethodID(callClass, "setVideoStats", "(Lorg/linphone/core/LinphoneCallStats;)V");
chatMessageClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessageImpl"));
- if (chatMessageClass) chatMessageCtrId = env->GetMethodID(chatMessageClass,"", "(J)V");
+ if (chatMessageClass) {
+ chatMessageCtrId = env->GetMethodID(chatMessageClass,"", "(J)V");
+ }
chatRoomClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatRoomImpl"));
chatRoomCtrId = env->GetMethodID(chatRoomClass,"", "(J)V");
friendClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneFriendImpl"));;
- friendCtrId =env->GetMethodID(friendClass,"", "(J)V");
+ friendCtrId = env->GetMethodID(friendClass,"", "(J)V");
addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl"));
- addressCtrId =env->GetMethodID(addressClass,"", "(J)V");
+ addressCtrId = env->GetMethodID(addressClass,"", "(J)V");
callStatsClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallStatsImpl"));
callStatsId = env->GetMethodID(callStatsClass, "", "(JJ)V");
- callSetAudioStatsId = env->GetMethodID(callClass, "setAudioStats", "(Lorg/linphone/core/LinphoneCallStats;)V");
- callSetVideoStatsId = env->GetMethodID(callClass, "setVideoStats", "(Lorg/linphone/core/LinphoneCallStats;)V");
+
+ infoMessageClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneInfoMessageImpl"));
+ infoMessageCtor = env->GetMethodID(infoMessageClass,"", "(J)V");
+
+ linphoneEventClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneEventImpl"));
+ linphoneEventCtrId = env->GetMethodID(linphoneEventClass,"", "(J)V");
+
+ subscriptionDirClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/SubscriptionDir"));
+ subscriptionDirFromIntId = env->GetStaticMethodID(subscriptionDirClass,"fromInt","(I)Lorg/linphone/core/SubscriptionDir;");
}
~LinphoneCoreData() {
@@ -214,32 +496,44 @@ public:
jvm->AttachCurrentThread(&env,NULL);
env->DeleteGlobalRef(core);
env->DeleteGlobalRef(listener);
- if (userdata) env->DeleteGlobalRef(userdata);
env->DeleteGlobalRef(listenerClass);
env->DeleteGlobalRef(globalStateClass);
+ env->DeleteGlobalRef(configuringStateClass);
env->DeleteGlobalRef(registrationStateClass);
env->DeleteGlobalRef(callStateClass);
- env->DeleteGlobalRef(callStatsClass);
env->DeleteGlobalRef(chatMessageStateClass);
env->DeleteGlobalRef(proxyClass);
env->DeleteGlobalRef(callClass);
env->DeleteGlobalRef(chatMessageClass);
env->DeleteGlobalRef(chatRoomClass);
env->DeleteGlobalRef(friendClass);
-
+ env->DeleteGlobalRef(infoMessageClass);
+ env->DeleteGlobalRef(linphoneEventClass);
+ env->DeleteGlobalRef(subscriptionStateClass);
+ env->DeleteGlobalRef(subscriptionDirClass);
+ env->DeleteGlobalRef(logCollectionUploadStateClass);
}
jobject core;
jobject listener;
- jobject userdata;
jclass listenerClass;
jmethodID displayStatusId;
jmethodID newSubscriptionRequestId;
jmethodID notifyPresenceReceivedId;
- jmethodID textReceivedId;
jmethodID messageReceivedId;
+ jmethodID isComposingReceivedId;
jmethodID dtmfReceivedId;
jmethodID callStatsUpdatedId;
+ jmethodID transferStateId;
+ jmethodID infoReceivedId;
+ jmethodID subscriptionStateId;
+ jmethodID authInfoRequestedId;
+ jmethodID publishStateId;
+ jmethodID notifyRecvId;
+
+ jclass configuringStateClass;
+ jmethodID configuringStateId;
+ jmethodID configuringStateFromIntId;
jclass globalStateClass;
jmethodID globalStateId;
@@ -285,75 +579,118 @@ public:
jclass addressClass;
jmethodID addressCtrId;
+ jclass infoMessageClass;
+ jmethodID infoMessageCtor;
+
+ jclass linphoneEventClass;
+ jmethodID linphoneEventCtrId;
+
+ jclass subscriptionStateClass;
+ jmethodID subscriptionStateFromIntId;
+
+ jclass publishStateClass;
+ jmethodID publishStateFromIntId;
+
+ jclass subscriptionDirClass;
+ jmethodID subscriptionDirFromIntId;
+
+ jmethodID fileTransferProgressIndicationId;
+ jmethodID fileTransferSendId;
+ jmethodID fileTransferRecvId;
+
+ jclass logCollectionUploadStateClass;
+ jmethodID logCollectionUploadStateId;
+ jmethodID logCollectionUploadStateFromIntId;
+ jmethodID logCollectionUploadProgressId;
+
LinphoneCoreVTable vTable;
- static void showInterfaceCb(LinphoneCore *lc) {
-
- }
- static void byeReceivedCb(LinphoneCore *lc, const char *from) {
-
- }
static void displayStatusCb(LinphoneCore *lc, const char *message) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
- env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,message ? env->NewStringUTF(message) : NULL);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring msg = message ? env->NewStringUTF(message) : NULL;
+ env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,msg);
+ handle_possible_java_exception(env, lcData->listener);
+ if (msg) {
+ env->DeleteLocalRef(msg);
+ }
}
- static void displayMessageCb(LinphoneCore *lc, const char *message) {
-
- }
- static void authInfoRequested(LinphoneCore *lc, const char *realm, const char *username) {
-
+ static void authInfoRequested(LinphoneCore *lc, const char *realm, const char *username, const char *domain) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring r = realm ? env->NewStringUTF(realm) : NULL;
+ jstring u = username ? env->NewStringUTF(username) : NULL;
+ jstring d = domain ? env->NewStringUTF(domain) : NULL;
+ env->CallVoidMethod(lcData->listener,
+ lcData->authInfoRequestedId,
+ lcData->core,
+ r,
+ u,
+ d);
+ handle_possible_java_exception(env, lcData->listener);
+ if (r) {
+ env->DeleteLocalRef(r);
+ }
+ if (u) {
+ env->DeleteLocalRef(u);
+ }
+ if (d) {
+ env->DeleteLocalRef(d);
+ }
}
static void globalStateChange(LinphoneCore *lc, LinphoneGlobalState gstate,const char* message) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring msg = message ? env->NewStringUTF(message) : NULL;
env->CallVoidMethod(lcData->listener
,lcData->globalStateId
,lcData->core
,env->CallStaticObjectMethod(lcData->globalStateClass,lcData->globalStateFromIntId,(jint)gstate),
- message ? env->NewStringUTF(message) : NULL);
+ msg);
+ handle_possible_java_exception(env, lcData->listener);
+ if (msg) {
+ env->DeleteLocalRef(msg);
+ }
}
static void registrationStateChange(LinphoneCore *lc, LinphoneProxyConfig* proxy,LinphoneRegistrationState state,const char* message) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
+ jobject jproxy;
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring msg = message ? env->NewStringUTF(message) : NULL;
env->CallVoidMethod(lcData->listener
,lcData->registrationStateId
,lcData->core
- ,env->NewObject(lcData->proxyClass,lcData->proxyCtrId,(jlong)proxy)
+ ,(jproxy=getProxy(env,proxy,lcData->core))
,env->CallStaticObjectMethod(lcData->registrationStateClass,lcData->registrationStateFromIntId,(jint)state),
- message ? env->NewStringUTF(message) : NULL);
- }
- jobject getCall(JNIEnv *env , LinphoneCall *call){
- jobject jobj=0;
-
- if (call!=NULL){
- void *up=linphone_call_get_user_pointer(call);
-
- if (up==NULL){
- jobj=env->NewObject(callClass,callCtrId,(jlong)call);
- jobj=env->NewGlobalRef(jobj);
- linphone_call_set_user_pointer(call,(void*)jobj);
- linphone_call_ref(call);
- }else{
- jobj=(jobject)up;
- }
+ msg);
+ handle_possible_java_exception(env, lcData->listener);
+ if (msg) {
+ env->DeleteLocalRef(msg);
}
- return jobj;
}
static void callStateChange(LinphoneCore *lc, LinphoneCall* call,LinphoneCallState state,const char* message) {
@@ -361,123 +698,148 @@ public:
jint result = jvm->AttachCurrentThread(&env,NULL);
jobject jcall;
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring msg = message ? env->NewStringUTF(message) : NULL;
env->CallVoidMethod(lcData->listener
,lcData->callStateId
,lcData->core
- ,(jcall=lcData->getCall(env,call))
+ ,(jcall=getCall(env,call))
,env->CallStaticObjectMethod(lcData->callStateClass,lcData->callStateFromIntId,(jint)state),
- message ? env->NewStringUTF(message) : NULL);
- if (state==LinphoneCallReleased){
+ msg);
+ handle_possible_java_exception(env, lcData->listener);
+ if (state==LinphoneCallReleased) {
linphone_call_set_user_pointer(call,NULL);
env->DeleteGlobalRef(jcall);
}
+ if (msg) {
+ env->DeleteLocalRef(msg);
+ }
}
static void callEncryptionChange(LinphoneCore *lc, LinphoneCall* call, bool_t encrypted,const char* authentication_token) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
env->CallVoidMethod(lcData->listener
,lcData->callEncryptionChangedId
,lcData->core
- ,lcData->getCall(env,call)
+ ,getCall(env,call)
,encrypted
,authentication_token ? env->NewStringUTF(authentication_token) : NULL);
+ handle_possible_java_exception(env, lcData->listener);
}
- static void notify_presence_recv (LinphoneCore *lc, LinphoneFriend *my_friend) {
+ static void notify_presence_received(LinphoneCore *lc, LinphoneFriend *my_friend) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
env->CallVoidMethod(lcData->listener
,lcData->notifyPresenceReceivedId
,lcData->core
- ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend));
+ ,getFriend(env,my_friend));
+ handle_possible_java_exception(env, lcData->listener);
}
- static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *my_friend, const char* url) {
+ static void new_subscription_requested(LinphoneCore *lc, LinphoneFriend *my_friend, const char* url) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
env->CallVoidMethod(lcData->listener
,lcData->newSubscriptionRequestId
,lcData->core
- ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend)
+ ,getFriend(env,my_friend)
,url ? env->NewStringUTF(url) : NULL);
+ handle_possible_java_exception(env, lcData->listener);
}
static void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
env->CallVoidMethod(lcData->listener
,lcData->dtmfReceivedId
,lcData->core
- ,lcData->getCall(env,call)
+ ,getCall(env,call)
,dtmf);
+ handle_possible_java_exception(env, lcData->listener);
}
- static void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) {
+ static void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
+ JNIEnv *env = 0;
+ jobject jmsg;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ /*note: we call linphone_chat_message_ref() because the application does not acquire the object when invoked from a callback*/
+ env->CallVoidMethod(lcData->listener
+ ,lcData->messageReceivedId
+ ,lcData->core
+ ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room)
+ ,(jmsg = getChatMessage(env, msg)));
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
env->CallVoidMethod(lcData->listener
- ,lcData->textReceivedId
+ ,lcData->isComposingReceivedId
,lcData->core
- ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room)
- ,env->NewObject(lcData->addressClass,lcData->addressCtrId,(jlong)from)
- ,message ? env->NewStringUTF(message) : NULL);
+ ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room));
+ handle_possible_java_exception(env, lcData->listener);
}
- static void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
- JNIEnv *env = 0;
- jint result = jvm->AttachCurrentThread(&env,NULL);
- if (result != 0) {
- ms_error("cannot attach VM\n");
- return;
- }
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
- env->CallVoidMethod(lcData->listener
- ,lcData->messageReceivedId
- ,lcData->core
- ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room)
- ,env->NewObject(lcData->chatMessageClass,lcData->chatMessageCtrId,(jlong)msg));
- }
static void ecCalibrationStatus(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data) {
JNIEnv *env = 0;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
- env->CallVoidMethod(lcData->listener
- ,lcData->ecCalibrationStatusId
- ,lcData->core
- ,env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass,lcData->ecCalibratorStatusFromIntId,(jint)status)
- ,delay_ms
- ,data ? data : NULL);
- if (data != NULL &&status !=LinphoneEcCalibratorInProgress ) {
- //final state, releasing global ref
- env->DeleteGlobalRef((jobject)data);
+
+ LinphoneCoreVTable *table = (LinphoneCoreVTable*) data;
+ if (table) {
+ LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table);
+ if (lcData->ecCalibrationStatusId) {
+ jobject state = env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass, lcData->ecCalibratorStatusFromIntId, (jint)status);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->ecCalibrationStatusId
+ ,lcData->core
+ ,state
+ ,delay_ms
+ ,NULL);
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ if (status != LinphoneEcCalibratorInProgress) {
+ linphone_core_v_table_destroy(table);
+ }
}
}
@@ -487,31 +849,280 @@ public:
jobject callobj;
jint result = jvm->AttachCurrentThread(&env,NULL);
if (result != 0) {
- ms_error("cannot attach VM\n");
+ ms_error("cannot attach VM");
return;
}
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
statsobj = env->NewObject(lcData->callStatsClass, lcData->callStatsId, (jlong)call, (jlong)stats);
- callobj = lcData->getCall(env, call);
+ callobj = getCall(env, call);
if (stats->type == LINPHONE_CALL_STATS_AUDIO)
env->CallVoidMethod(callobj, lcData->callSetAudioStatsId, statsobj);
else
env->CallVoidMethod(callobj, lcData->callSetVideoStatsId, statsobj);
env->CallVoidMethod(lcData->listener, lcData->callStatsUpdatedId, lcData->core, callobj, statsobj);
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void transferStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState remote_call_state){
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ jobject jcall;
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->transferStateId
+ ,lcData->core
+ ,(jcall=getCall(env,call))
+ ,env->CallStaticObjectMethod(lcData->callStateClass,lcData->callStateFromIntId,(jint)remote_call_state)
+ );
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void infoReceived(LinphoneCore *lc, LinphoneCall*call, const LinphoneInfoMessage *info){
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneInfoMessage *copy_info=linphone_info_message_copy(info);
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->infoReceivedId
+ ,lcData->core
+ ,getCall(env,call)
+ ,env->NewObject(lcData->infoMessageClass,lcData->infoMessageCtor,(jlong)copy_info)
+ );
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void subscriptionStateChanged(LinphoneCore *lc, LinphoneEvent *ev, LinphoneSubscriptionState state){
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ jobject jevent;
+ jobject jstate;
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jevent=getEvent(env,ev);
+ jstate=env->CallStaticObjectMethod(lcData->subscriptionStateClass,lcData->subscriptionStateFromIntId,(jint)state);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->subscriptionStateId
+ ,lcData->core
+ ,jevent
+ ,jstate
+ );
+ handle_possible_java_exception(env, lcData->listener);
+ if (state==LinphoneSubscriptionTerminated){
+ /*loose the java reference */
+ linphone_event_set_user_data(ev,NULL);
+ env->DeleteGlobalRef(jevent);
+ }
+ }
+ static void publishStateChanged(LinphoneCore *lc, LinphoneEvent *ev, LinphonePublishState state){
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ jobject jevent;
+ jobject jstate;
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jevent=getEvent(env,ev);
+ jstate=env->CallStaticObjectMethod(lcData->publishStateClass,lcData->publishStateFromIntId,(jint)state);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->publishStateId
+ ,lcData->core
+ ,jevent
+ ,jstate
+ );
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void notifyReceived(LinphoneCore *lc, LinphoneEvent *ev, const char *evname, const LinphoneContent *content){
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ jobject jevent;
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jevent=getEvent(env,ev);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->notifyRecvId
+ ,lcData->core
+ ,jevent
+ ,env->NewStringUTF(evname)
+ ,content ? create_java_linphone_content(env,content) : NULL
+ );
+ handle_possible_java_exception(env, lcData->listener);
}
+ static void configuringStatus(LinphoneCore *lc, LinphoneConfiguringState status, const char *message) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ env->CallVoidMethod(lcData->listener, lcData->configuringStateId, lcData->core, env->CallStaticObjectMethod(lcData->configuringStateClass,lcData->configuringStateFromIntId,(jint)status), message ? env->NewStringUTF(message) : NULL);
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void fileTransferProgressIndication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) {
+ JNIEnv *env = 0;
+ jobject jmsg;
+ size_t progress = (offset * 100) / total;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+ env->CallVoidMethod(lcData->listener,
+ lcData->fileTransferProgressIndicationId,
+ lcData->core,
+ (jmsg = getChatMessage(env, message)),
+ jcontent,
+ progress);
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+ handle_possible_java_exception(env, lcData->listener);
+ }
+
+ static void fileTransferSend(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size) {
+ JNIEnv *env = 0;
+ jobject jmsg;
+ size_t asking = *size;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+ jobject jbuffer = buff ? env->NewDirectByteBuffer(buff, asking) : NULL;
+ *size = env->CallIntMethod(lcData->listener,
+ lcData->fileTransferSendId,
+ lcData->core,
+ (jmsg = getChatMessage(env, message)),
+ jcontent,
+ jbuffer,
+ asking);
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+ if (jbuffer) {
+ env->DeleteLocalRef(jbuffer);
+ }
+ handle_possible_java_exception(env, lcData->listener);
+ }
+
+ static void fileTransferRecv(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size) {
+ JNIEnv *env = 0;
+ jobject jmsg;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+
+ jbyteArray jbytes = env->NewByteArray(size);
+ env->SetByteArrayRegion(jbytes, 0, size, (jbyte*)buff);
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+
+ env->CallVoidMethod(lcData->listener,
+ lcData->fileTransferRecvId,
+ lcData->core,
+ (jmsg = getChatMessage(env, message)),
+ jcontent,
+ jbytes,
+ size);
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void logCollectionUploadProgressIndication(LinphoneCore *lc, size_t offset, size_t total) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ env->CallVoidMethod(lcData->listener
+ ,lcData->logCollectionUploadProgressId
+ ,lcData->core
+ ,(jlong)offset
+ ,(jlong)total);
+ handle_possible_java_exception(env, lcData->listener);
+ }
+ static void logCollectionUploadStateChange(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM");
+ return;
+ }
+ LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
+ LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
+ jstring msg = info ? env->NewStringUTF(info) : NULL;
+ env->CallVoidMethod(lcData->listener
+ ,lcData->logCollectionUploadStateId
+ ,lcData->core
+ ,env->CallStaticObjectMethod(lcData->logCollectionUploadStateClass,lcData->logCollectionUploadStateFromIntId,(jint)state),
+ msg);
+ handle_possible_java_exception(env, lcData->listener);
+ if (msg) {
+ env->DeleteLocalRef(msg);
+ }
+ }
+
+private:
+ static inline void handle_possible_java_exception(JNIEnv *env, jobject listener)
+ {
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ ms_error("Listener %p raised an exception",listener);
+ }
+ }
};
+
extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* env
- ,jobject thiz
+ ,jobject thiz
,jobject jlistener
,jstring juserConfig
,jstring jfactoryConfig
- ,jobject juserdata){
+ ,jobject juserdata){
const char* userConfig = juserConfig?env->GetStringUTFChars(juserConfig, NULL):NULL;
const char* factoryConfig = jfactoryConfig?env->GetStringUTFChars(jfactoryConfig, NULL):NULL;
- LinphoneCoreData* ldata = new LinphoneCoreData(env,thiz,jlistener,juserdata);
+
+ LinphoneCoreVTable *vTable = linphone_core_v_table_new();
+ LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, jlistener);
+ linphone_core_v_table_set_user_data(vTable, ldata);
+
+ ms_init(); // Initialize mediastreamer2 before loading the plugins
#ifdef HAVE_ILBC
libmsilbc_init(); // requires an fpu
@@ -519,6 +1130,9 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv*
#ifdef HAVE_X264
libmsx264_init();
#endif
+#ifdef HAVE_OPENH264
+ libmsopenh264_init();
+#endif
#ifdef HAVE_AMR
libmsamr_init();
#endif
@@ -528,37 +1142,145 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv*
#ifdef HAVE_G729
libmsbcg729_init();
#endif
- jlong nativePtr = (jlong)linphone_core_new( &ldata->vTable
- ,userConfig
- ,factoryConfig
- ,ldata);
+#ifdef HAVE_WEBRTC
+ libmswebrtc_init();
+#endif
+#ifdef HAVE_CODEC2
+ libmscodec2_init();
+#endif
+
+ jobject core = env->NewGlobalRef(thiz);
+ jlong nativePtr = (jlong)linphone_core_new(vTable, userConfig, factoryConfig, core);
if (userConfig) env->ReleaseStringUTFChars(juserConfig, userConfig);
if (factoryConfig) env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig);
return nativePtr;
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env
- ,jobject thiz
- ,jlong lc) {
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc);
- linphone_core_destroy((LinphoneCore*)lc);
- delete lcData;
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env, jobject thiz, jlong native_ptr) {
+ LinphoneCore *lc=(LinphoneCore*)native_ptr;
+ jobject core = (jobject)linphone_core_get_user_data(lc);
+
+ jobject multicast_lock = lc->multicast_lock;
+ jobject multicast_lock_class = lc->multicast_lock_class;
+ jobject wifi_lock = lc->wifi_lock;
+ jobject wifi_lock_class = lc->wifi_lock_class;
+
+ linphone_core_destroy(lc);
+ ms_exit();
+
+ if (wifi_lock) env->DeleteGlobalRef(wifi_lock);
+ if (wifi_lock_class) env->DeleteGlobalRef(wifi_lock_class);
+ if (multicast_lock) env->DeleteGlobalRef(multicast_lock);
+ if (multicast_lock_class) env->DeleteGlobalRef(multicast_lock_class);
+
+ if (core) {
+ env->DeleteGlobalRef(core);
+ }
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addListener(JNIEnv* env, jobject thiz, jlong lc, jobject jlistener) {
+ LinphoneCoreVTable *vTable = linphone_core_v_table_new();
+ LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, jlistener);
+ linphone_core_v_table_set_user_data(vTable, ldata);
+ linphone_core_add_listener((LinphoneCore*)lc, vTable);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeListener(JNIEnv* env, jobject thiz, jlong lc, jobject jlistener) {
+ MSList* iterator;
+ LinphoneCore *core = (LinphoneCore*)lc;
+ //jobject listener = env->NewGlobalRef(jlistener);
+ for (iterator = core->vtable_refs; iterator != NULL; ) {
+ VTableReference *ref=(VTableReference*)(iterator->data);
+ LinphoneCoreVTable *vTable = ref->valid ? ref->vtable : NULL;
+ iterator = iterator->next; //Because linphone_core_remove_listener may change the list
+ if (vTable) {
+ LinphoneCoreData *data = (LinphoneCoreData*) linphone_core_v_table_get_user_data(vTable);
+ if (data && env->IsSameObject(data->listener, jlistener)) {
+ linphone_core_remove_listener(core, vTable);
+ delete data;
+ linphone_core_v_table_destroy(vTable);
+ }
+ }
+ }
+ //env->DeleteGlobalRef(listener);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_uploadLogCollection(JNIEnv* env, jobject thiz, jlong lc) {
+ LinphoneCore *core = (LinphoneCore*)lc;
+ linphone_core_upload_log_collection(core);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_resetLogCollection(JNIEnv* env, jobject thiz) {
+ linphone_core_reset_log_collection();
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_migrateToMultiTransport(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc) {
+ return (jint) linphone_core_migrate_to_multi_transport((LinphoneCore *)lc);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: createInfoMessage
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneCoreImpl_createInfoMessage(JNIEnv *, jobject jobj, jlong lcptr){
+ return (jlong) linphone_core_create_info_message((LinphoneCore*)lcptr);
+}
+
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCallImpl_sendInfoMessage(JNIEnv *env, jobject jobj, jlong callptr, jlong infoptr){
+ return linphone_call_send_info_message((LinphoneCall*)callptr,(LinphoneInfoMessage*)infoptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_stopRinging(JNIEnv* env, jobject thiz, jlong lc) {
+ linphone_core_stop_ringing((LinphoneCore*)lc);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setChatDatabasePath(JNIEnv* env, jobject thiz, jlong lc, jstring jpath) {
+ const char* path = env->GetStringUTFChars(jpath, NULL);
+ linphone_core_set_chat_database_path((LinphoneCore*)lc, path);
+ env->ReleaseStringUTFChars(jpath, path);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPrimaryContact2(JNIEnv* env, jobject thiz, jlong lc, jstring jcontact) {
+ const char* contact = env->GetStringUTFChars(jcontact, NULL);
+ linphone_core_set_primary_contact((LinphoneCore*)lc, contact);
+ env->ReleaseStringUTFChars(jcontact, contact);
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getPrimaryContact(JNIEnv* env, jobject thiz, jlong lc) {
+ LinphoneAddress* identity = linphone_core_get_primary_contact_parsed((LinphoneCore*)lc);
+ return identity ? env->NewStringUTF(linphone_address_as_string(identity)) : NULL;
+}
+
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPrimaryContact(JNIEnv* env, jobject thiz, jlong lc, jstring jdisplayname, jstring jusername) {
- const char* displayname = env->GetStringUTFChars(jdisplayname, NULL);
- const char* username = env->GetStringUTFChars(jusername, NULL);
+ const char* displayname = jdisplayname ? env->GetStringUTFChars(jdisplayname, NULL) : NULL;
+ const char* username = jusername ? env->GetStringUTFChars(jusername, NULL) : NULL;
LinphoneAddress *parsed = linphone_core_get_primary_contact_parsed((LinphoneCore*)lc);
- if (parsed != NULL) {
- linphone_address_set_display_name(parsed, displayname);
- linphone_address_set_username(parsed, username);
- char *contact = linphone_address_as_string(parsed);
+ if (parsed != NULL) {
+ linphone_address_set_display_name(parsed, displayname);
+ linphone_address_set_username(parsed, username);
+ char *contact = linphone_address_as_string(parsed);
linphone_core_set_primary_contact((LinphoneCore*)lc, contact);
}
- env->ReleaseStringUTFChars(jdisplayname, displayname);
- env->ReleaseStringUTFChars(jusername, username);
+ if (jdisplayname) env->ReleaseStringUTFChars(jdisplayname, displayname);
+ if (jusername) env->ReleaseStringUTFChars(jusername, username);
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getPrimaryContactUsername(JNIEnv* env, jobject thiz, jlong lc) {
+ LinphoneAddress* identity = linphone_core_get_primary_contact_parsed((LinphoneCore*)lc);
+ const char * username = linphone_address_get_username(identity);
+ return username ? env->NewStringUTF(username) : NULL;
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getPrimaryContactDisplayName(JNIEnv* env, jobject thiz, jlong lc) {
+ LinphoneAddress* identity = linphone_core_get_primary_contact_parsed((LinphoneCore*)lc);
+ const char * displayname = linphone_address_get_display_name(identity);
+ return displayname ? env->NewStringUTF(displayname) : NULL;
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearProxyConfigs(JNIEnv* env, jobject thiz,jlong lc) {
@@ -571,27 +1293,35 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDefaultProxyConfig( J
,jlong pc) {
linphone_core_set_default_proxy((LinphoneCore*)lc,(LinphoneProxyConfig*)pc);
}
-extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getDefaultProxyConfig( JNIEnv* env
+
+extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getDefaultProxyConfig(JNIEnv* env
,jobject thiz
,jlong lc) {
LinphoneProxyConfig *config=0;
linphone_core_get_default_proxy((LinphoneCore*)lc,&config);
- return (jlong)config;
+ if(config != 0) {
+ jobject jproxy = getProxy(env,config,thiz);
+ return jproxy;
+ } else {
+ return NULL;
+ }
}
-extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_getProxyConfigList(JNIEnv* env, jobject thiz, jlong lc) {
+extern "C" jobjectArray Java_org_linphone_core_LinphoneCoreImpl_getProxyConfigList(JNIEnv* env, jobject thiz, jlong lc) {
const MSList* proxies = linphone_core_get_proxy_config_list((LinphoneCore*)lc);
int proxyCount = ms_list_size(proxies);
- jlongArray jProxies = env->NewLongArray(proxyCount);
- jlong *jInternalArray = env->GetLongArrayElements(jProxies, NULL);
+ jclass cls = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl"));
+ jobjectArray jProxies = env->NewObjectArray(proxyCount,cls,NULL);
for (int i = 0; i < proxyCount; i++ ) {
- jInternalArray[i] = (unsigned long) (proxies->data);
+ LinphoneProxyConfig* proxy = (LinphoneProxyConfig*)proxies->data;
+ jobject jproxy = getProxy(env,proxy,thiz);
+ if(jproxy != NULL){
+ env->SetObjectArrayElement(jProxies, i, jproxy);
+ }
proxies = proxies->next;
}
-
- env->ReleaseLongArrayElements(jProxies, jInternalArray, 0);
-
+ env->DeleteGlobalRef(cls);
return jProxies;
}
@@ -601,9 +1331,46 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv*
,jlong lc
,jlong pc) {
LinphoneProxyConfig* proxy = (LinphoneProxyConfig*)pc;
- linphone_proxy_config_set_user_data(proxy, env->NewGlobalRef(jproxyCfg));
+ return (jint)linphone_core_add_proxy_config((LinphoneCore*)lc,proxy);
+}
- return (jint)linphone_core_add_proxy_config((LinphoneCore*)lc,(LinphoneProxyConfig*)pc);
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeProxyConfig(JNIEnv* env, jobject thiz, jlong lc, jlong proxy) {
+ linphone_core_remove_proxy_config((LinphoneCore*)lc, (LinphoneProxyConfig*)proxy);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeAuthInfo(JNIEnv* env, jobject thiz, jlong lc, jlong authInfo) {
+ linphone_core_remove_auth_info((LinphoneCore*)lc, (LinphoneAuthInfo*)authInfo);
+}
+
+extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_getAuthInfosList(JNIEnv* env, jobject thiz,jlong lc) {
+ const MSList* authInfos = linphone_core_get_auth_info_list((LinphoneCore*)lc);
+ int listCount = ms_list_size(authInfos);
+ jlongArray jAuthInfos = env->NewLongArray(listCount);
+ jlong *jInternalArray = env->GetLongArrayElements(jAuthInfos, NULL);
+
+ for (int i = 0; i < listCount; i++ ) {
+ jInternalArray[i] = (unsigned long) (authInfos->data);
+ authInfos = authInfos->next;
+ }
+
+ env->ReleaseLongArrayElements(jAuthInfos, jInternalArray, 0);
+
+ return jAuthInfos;
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_findAuthInfos(JNIEnv* env, jobject thiz, jlong lc, jstring jusername, jstring jrealm, jstring jdomain) {
+ const char* username = env->GetStringUTFChars(jusername, NULL);
+ const char* realm = jrealm ? env->GetStringUTFChars(jrealm, NULL) : NULL;
+ const char* domain = jdomain ? env->GetStringUTFChars(jdomain, NULL) : NULL;
+ const LinphoneAuthInfo *authInfo = linphone_core_find_auth_info((LinphoneCore*)lc, realm, username, domain);
+
+ if (realm)
+ env->ReleaseStringUTFChars(jrealm, realm);
+ if (domain)
+ env->ReleaseStringUTFChars(jdomain, domain);
+ env->ReleaseStringUTFChars(jusername, username);
+
+ return (jlong) authInfo;
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearAuthInfos(JNIEnv* env, jobject thiz,jlong lc) {
@@ -614,67 +1381,65 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_refreshRegisters(JNIEnv*
linphone_core_refresh_registers((LinphoneCore*)lc);
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addAuthInfo( JNIEnv* env
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addAuthInfo(JNIEnv* env
,jobject thiz
,jlong lc
,jlong pc) {
linphone_core_add_auth_info((LinphoneCore*)lc,(LinphoneAuthInfo*)pc);
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_iterate( JNIEnv* env
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_iterate(JNIEnv* env
,jobject thiz
,jlong lc) {
linphone_core_iterate((LinphoneCore*)lc);
}
-extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_invite( JNIEnv* env
+extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_invite(JNIEnv* env
,jobject thiz
,jlong lc
,jstring juri) {
const char* uri = env->GetStringUTFChars(juri, NULL);
- LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc);
LinphoneCall* lCall = linphone_core_invite((LinphoneCore*)lc,uri);
env->ReleaseStringUTFChars(juri, uri);
- return lcd->getCall(env,lCall);
+ return getCall(env,lCall);
}
-extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_inviteAddress( JNIEnv* env
+extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_inviteAddress(JNIEnv* env
,jobject thiz
,jlong lc
,jlong to) {
- LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc);
- return lcd->getCall(env, linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to));
+ return getCall(env, linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to));
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateCall( JNIEnv* env
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateCall(JNIEnv* env
,jobject thiz
,jlong lc
,jlong call) {
linphone_core_terminate_call((LinphoneCore*)lc,(LinphoneCall*)call);
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_declineCall( JNIEnv* env
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_declineCall(JNIEnv* env
,jobject thiz
,jlong lc
,jlong call, jint reason) {
linphone_core_decline_call((LinphoneCore*)lc,(LinphoneCall*)call,(LinphoneReason)reason);
}
-extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getRemoteAddress( JNIEnv* env
+extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getRemoteAddress(JNIEnv* env
,jobject thiz
,jlong lc) {
return (jlong)linphone_core_get_current_call_remote_address((LinphoneCore*)lc);
}
-extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInCall( JNIEnv* env
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInCall(JNIEnv* env
,jobject thiz
,jlong lc) {
return (jboolean)linphone_core_in_call((LinphoneCore*)lc);
}
-extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInComingInvitePending( JNIEnv* env
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInComingInvitePending(JNIEnv* env
,jobject thiz
,jlong lc) {
return (jboolean)linphone_core_inc_invite_pending((LinphoneCore*)lc);
}
-extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCall( JNIEnv* env
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCall(JNIEnv* env
,jobject thiz
,jlong lc
,jlong call) {
@@ -705,6 +1470,21 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_deferCallUpdate(JNIEnv *
linphone_core_defer_call_update((LinphoneCore*)lc,(LinphoneCall*)call);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_acceptEarlyMedia(JNIEnv *env, jobject thiz, jlong lc, jlong c) {
+ LinphoneCore *core = (LinphoneCore *)lc;
+ LinphoneCall *call = (LinphoneCall *)c;
+ int ret = linphone_core_accept_early_media(core, call);
+ return (jboolean) ret == 0;
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_acceptEarlyMediaWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong c, jlong params) {
+ LinphoneCore *core = (LinphoneCore *)lc;
+ LinphoneCall *call = (LinphoneCall *)c;
+ const LinphoneCallParams *call_params = (LinphoneCallParams *) params;
+ int ret = linphone_core_accept_early_media_with_params(core, call, call_params);
+ return (jboolean) ret == 0;
+}
+
extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCallLog( JNIEnv* env
,jobject thiz
,jlong lc
@@ -716,6 +1496,20 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getNumberOfCallLogs( JNI
,jlong lc) {
return (jint)ms_list_size(linphone_core_get_call_logs((LinphoneCore*)lc));
}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMtu(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jint mtu) {
+ linphone_core_set_mtu((LinphoneCore*)lc,mtu);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getMtu(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc) {
+ return linphone_core_get_mtu((LinphoneCore*)lc);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setNetworkStateReachable( JNIEnv* env
,jobject thiz
,jlong lc
@@ -823,6 +1617,7 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_findPayloadType(JNIEnv*
env->ReleaseStringUTFChars(jmime, mime);
return result;
}
+
extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listVideoPayloadTypes(JNIEnv* env
,jobject thiz
,jlong lc) {
@@ -841,6 +1636,18 @@ extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listVideoPayloadTy
return jCodecs;
}
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoCodecs(JNIEnv *env, jobject thiz, jlong lc, jlongArray jCodecs) {
+ MSList *pts = NULL;
+ int codecsCount = env->GetArrayLength(jCodecs);
+ jlong *codecs = env->GetLongArrayElements(jCodecs, NULL);
+ for (int i = 0; i < codecsCount; i++) {
+ PayloadType *pt = (PayloadType *)codecs[i];
+ ms_list_append(pts, pt);
+ }
+ linphone_core_set_video_codecs((LinphoneCore *)lc, pts);
+ env->ReleaseLongArrayElements(jCodecs, codecs, 0);
+}
+
extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listAudioPayloadTypes(JNIEnv* env
,jobject thiz
,jlong lc) {
@@ -859,6 +1666,18 @@ extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listAudioPayloadTy
return jCodecs;
}
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioCodecs(JNIEnv *env, jobject thiz, jlong lc, jlongArray jCodecs) {
+ MSList *pts = NULL;
+ int codecsCount = env->GetArrayLength(jCodecs);
+ jlong *codecs = env->GetLongArrayElements(jCodecs, NULL);
+ for (int i = 0; i < codecsCount; i++) {
+ PayloadType *pt = (PayloadType *)codecs[i];
+ pts = ms_list_append(pts, pt);
+ }
+ linphone_core_set_audio_codecs((LinphoneCore *)lc, pts);
+ env->ReleaseLongArrayElements(jCodecs, codecs, 0);
+}
+
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_enablePayloadType(JNIEnv* env
,jobject thiz
,jlong lc
@@ -866,6 +1685,86 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_enablePayloadType(JNIEnv
,jboolean enable) {
return (jint)linphone_core_enable_payload_type((LinphoneCore*)lc,(PayloadType*)pt,enable);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isPayloadTypeEnabled(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt) {
+ return (jboolean) linphone_core_payload_type_enabled((LinphoneCore*)lc, (PayloadType*)pt);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_payloadTypeIsVbr(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt) {
+ return (jboolean) linphone_core_payload_type_is_vbr((LinphoneCore*)lc, (PayloadType*)pt);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPayloadTypeBitrate(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt
+ ,jint bitrate) {
+ linphone_core_set_payload_type_bitrate((LinphoneCore*)lc,(PayloadType*)pt,bitrate);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getPayloadTypeBitrate(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt) {
+ return (jint)linphone_core_get_payload_type_bitrate((LinphoneCore*)lc,(PayloadType*)pt);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPayloadTypeNumber(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt
+ ,jint number) {
+ linphone_core_set_payload_type_number((LinphoneCore*)lc,(PayloadType*)pt,number);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getPayloadTypeNumber(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jlong pt) {
+ return (jint)linphone_core_get_payload_type_number((LinphoneCore*)lc,(PayloadType*)pt);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableAdaptiveRateControl(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jboolean enable) {
+ linphone_core_enable_adaptive_rate_control((LinphoneCore*)lc, enable);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isAdaptiveRateControlEnabled(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ) {
+ return (jboolean)linphone_core_adaptive_rate_control_enabled((LinphoneCore*)lc);
+}
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getAdaptiveRateAlgorithm(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ) {
+ const char* alg = linphone_core_get_adaptive_rate_algorithm((LinphoneCore*)lc);
+ if (alg) {
+ return env->NewStringUTF(alg);
+ } else {
+ return NULL;
+ }
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAdaptiveRateAlgorithm(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jstring jalg) {
+ const char* alg = jalg?env->GetStringUTFChars(jalg, NULL):NULL;
+ linphone_core_set_adaptive_rate_algorithm((LinphoneCore*)lc,alg);
+ if (alg) env->ReleaseStringUTFChars(jalg, alg);
+
+}
+
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableEchoCancellation(JNIEnv* env
,jobject thiz
,jlong lc
@@ -896,9 +1795,7 @@ extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getCurrentCall(JNIEnv
,jobject thiz
,jlong lc
) {
- LinphoneCoreData *lcdata=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc);
-
- return lcdata->getCall(env,linphone_core_get_current_call((LinphoneCore*)lc));
+ return getCall(env,linphone_core_get_current_call((LinphoneCore*)lc));
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addFriend(JNIEnv* env
,jobject thiz
@@ -907,24 +1804,70 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addFriend(JNIEnv* env
) {
linphone_core_add_friend((LinphoneCore*)lc,(LinphoneFriend*)aFriend);
}
+extern "C" jobjectArray Java_org_linphone_core_LinphoneCoreImpl_getFriendList(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc) {
+ const MSList* friends = linphone_core_get_friend_list((LinphoneCore*)lc);
+ int friendsSize = ms_list_size(friends);
+ jclass cls = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneFriendImpl"));
+ jobjectArray jFriends = env->NewObjectArray(friendsSize,cls,NULL);
+
+ for (int i = 0; i < friendsSize; i++) {
+ LinphoneFriend* lfriend = (LinphoneFriend*)friends->data;
+ jobject jfriend = getFriend(env,lfriend);
+ if(jfriend != NULL){
+ env->SetObjectArrayElement(jFriends, i, jfriend);
+ }
+ friends = friends->next;
+ }
+
+ env->DeleteGlobalRef(cls);
+ return jFriends;
+}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPresenceInfo(JNIEnv* env
,jobject thiz
,jlong lc
- ,jint minute_away
+ ,jint minutes_away
,jstring jalternative_contact
,jint status) {
const char* alternative_contact = jalternative_contact?env->GetStringUTFChars(jalternative_contact, NULL):NULL;
- linphone_core_set_presence_info((LinphoneCore*)lc,minute_away,alternative_contact,(LinphoneOnlineStatus)status);
+ linphone_core_set_presence_info((LinphoneCore*)lc,minutes_away,alternative_contact,(LinphoneOnlineStatus)status);
if (alternative_contact) env->ReleaseStringUTFChars(jalternative_contact, alternative_contact);
}
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getPresenceInfo(JNIEnv *env, jobject thiz, jlong lc) {
+ return (jint)linphone_core_get_presence_info((LinphoneCore *)lc);
+}
-extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* env
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: setPresenceModel
+ * Signature: (JILjava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setPresenceModel(JNIEnv *env, jobject jobj, jlong ptr, jlong modelPtr) {
+ LinphoneCore *lc = (LinphoneCore *)ptr;
+ LinphonePresenceModel *model = (LinphonePresenceModel *)modelPtr;
+ linphone_core_set_presence_model(lc, model);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: getPresenceModel
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCoreImpl_getPresenceModel(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphoneCore *lc = (LinphoneCore *)ptr;
+ LinphonePresenceModel *model = linphone_core_get_presence_model(lc);
+ if (model == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceModelImpl", linphone_presence_model, model)
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getOrCreateChatRoom(JNIEnv* env
,jobject thiz
,jlong lc
,jstring jto) {
const char* to = env->GetStringUTFChars(jto, NULL);
- LinphoneChatRoom* lResult = linphone_core_create_chat_room((LinphoneCore*)lc,to);
+ LinphoneChatRoom* lResult = linphone_core_get_or_create_chat_room((LinphoneCore*)lc,to);
env->ReleaseStringUTFChars(jto, to);
return (jlong)lResult;
}
@@ -942,6 +1885,14 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isVideoEnabled(JNIEn
,jlong lc) {
return (jboolean)linphone_core_video_enabled((LinphoneCore*)lc);
}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isVideoSupported(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc) {
+ return (jboolean)linphone_core_video_supported((LinphoneCore*)lc);
+}
+
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPlayFile(JNIEnv* env
,jobject thiz
,jlong lc
@@ -969,6 +1920,25 @@ extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getRing(JNIEnv* env
return NULL;
}
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setTone(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jint toneid
+ ,jstring jpath) {
+ const char* path = jpath ? env->GetStringUTFChars(jpath, NULL) : NULL;
+ linphone_core_set_tone((LinphoneCore *)lc, (LinphoneToneID)toneid, path);
+ if (path) env->ReleaseStringUTFChars(jpath, path);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setCallErrorTone(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jint reason
+ ,jstring jpath) {
+ const char* path = jpath ? env->GetStringUTFChars(jpath, NULL) : NULL;
+ linphone_core_set_call_error_tone((LinphoneCore *)lc, (LinphoneReason)reason, path);
+ if (path) env->ReleaseStringUTFChars(jpath, path);
+}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRootCA(JNIEnv* env
,jobject thiz
,jlong lc
@@ -977,6 +1947,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRootCA(JNIEnv* env
linphone_core_set_root_ca((LinphoneCore*)lc,path);
if (path) env->ReleaseStringUTFChars(jpath, path);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRingback(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jstring jpath) {
+ const char* path = jpath?env->GetStringUTFChars(jpath, NULL):NULL;
+ linphone_core_set_ringback((LinphoneCore*)lc,path);
+ if (path) env->ReleaseStringUTFChars(jpath, path);
+
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableKeepAlive(JNIEnv* env
,jobject thiz
,jlong lc
@@ -994,22 +1974,46 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNI
,jobject thiz
,jlong lc
,jobject data) {
- return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc
- , LinphoneCoreData::ecCalibrationStatus
- , data?env->NewGlobalRef(data):NULL);
+ LinphoneCoreVTable *vTable = linphone_core_v_table_new();
+ LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, data);
+ linphone_core_v_table_set_user_data(vTable, ldata);
+
+ return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc, ldata->ecCalibrationStatus, NULL, NULL, vTable);
}
-extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_needsEchoCalibration(JNIEnv *env, jobject thiz, jlong lc){
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_needsEchoCalibration(JNIEnv *env, jobject thiz, jlong lc) {
MSSndCard *sndcard;
- MSSndCardManager *m=ms_snd_card_manager_get();
- const char *card=linphone_core_get_capture_device((LinphoneCore*)lc);
- sndcard=ms_snd_card_manager_get_card(m,card);
- if (sndcard == NULL){
- ms_error("Could not get soundcard.");
+ MSSndCardManager *m = ms_snd_card_manager_get();
+ const char *card = linphone_core_get_capture_device((LinphoneCore*)lc);
+ sndcard = ms_snd_card_manager_get_card(m, card);
+ if (sndcard == NULL) {
+ ms_error("Could not get soundcard %s", card);
return TRUE;
}
- return (ms_snd_card_get_capabilities(sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) || (ms_snd_card_get_minimal_latency(sndcard)>0);
+
+ SoundDeviceDescription *sound_description = sound_device_description_get();
+ if(sound_description != NULL && sound_description == &genericSoundDeviceDescriptor){
+ return TRUE;
+ }
+
+ if (ms_snd_card_get_capabilities(sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) return FALSE;
+ if (ms_snd_card_get_minimal_latency(sndcard) != 0) return FALSE;
+ return TRUE;
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_hasBuiltInEchoCanceler(JNIEnv *env, jobject thiz, jlong lc) {
+ MSSndCard *sndcard;
+ MSSndCardManager *m = ms_snd_card_manager_get();
+ const char *card = linphone_core_get_capture_device((LinphoneCore*)lc);
+ sndcard = ms_snd_card_manager_get_card(m, card);
+ if (sndcard == NULL) {
+ ms_error("Could not get soundcard %s", card);
+ return FALSE;
+ }
+
+ if (ms_snd_card_get_capabilities(sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) return TRUE;
+ return FALSE;
}
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getMediaEncryption(JNIEnv* env
@@ -1048,16 +2052,50 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMediaEncryptionMandat
linphone_core_set_media_encryption_mandatory((LinphoneCore*)lc, yesno);
}
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: disableChat
+ * Signature: (JI)V
+ */
+extern "C" JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_disableChat(JNIEnv *env, jobject jobj, jlong ptr, jint reason){
+ linphone_core_disable_chat((LinphoneCore*)ptr,(LinphoneReason)reason);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: enableChat
+ * Signature: (J)V
+ */
+extern "C" JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableChat(JNIEnv *env, jobject jobj, jlong ptr){
+ linphone_core_enable_chat((LinphoneCore*)ptr);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: chatEnabled
+ * Signature: (J)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_chatEnabled(JNIEnv *env, jobject jobj, jlong ptr){
+ return (jboolean) linphone_core_chat_enabled((LinphoneCore*)ptr);
+}
+
+
//ProxyConfig
-extern "C" jlong Java_org_linphone_core_LinphoneProxyConfigImpl_newLinphoneProxyConfig(JNIEnv* env,jobject thiz) {
- LinphoneProxyConfig* proxy = linphone_proxy_config_new();
- return (jlong) proxy;
+extern "C" jlong Java_org_linphone_core_LinphoneProxyConfigImpl_createProxyConfig(JNIEnv* env, jobject thiz, jlong lc) {
+ LinphoneProxyConfig* proxy = linphone_core_create_proxy_config((LinphoneCore *)lc);
+ linphone_proxy_config_set_user_data(proxy,env->NewWeakGlobalRef(thiz));
+ return (jlong) proxy;
}
-extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_delete(JNIEnv* env,jobject thiz,jlong ptr) {
- linphone_proxy_config_destroy((LinphoneProxyConfig*)ptr);
+extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_finalize(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ LinphoneProxyConfig *proxy=(LinphoneProxyConfig*)ptr;
+ linphone_proxy_config_set_user_data(proxy,NULL);
+ linphone_proxy_config_unref(proxy);
}
+
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setIdentity(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jidentity) {
const char* identity = env->GetStringUTFChars(jidentity, NULL);
linphone_proxy_config_set_identity((LinphoneProxyConfig*)proxyCfg,identity);
@@ -1086,10 +2124,25 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getProxy(JNIEn
}
}
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setContactParameters(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jparams) {
- const char* params = env->GetStringUTFChars(jparams, NULL);
+ const char* params = jparams ? env->GetStringUTFChars(jparams, NULL) : NULL;
linphone_proxy_config_set_contact_parameters((LinphoneProxyConfig*)proxyCfg, params);
- env->ReleaseStringUTFChars(jparams, params);
+ if (jparams) env->ReleaseStringUTFChars(jparams, params);
}
+extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setContactUriParameters(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jparams) {
+ const char* params = jparams ? env->GetStringUTFChars(jparams, NULL) : NULL;
+ linphone_proxy_config_set_contact_uri_parameters((LinphoneProxyConfig*)proxyCfg, params);
+ if (jparams) env->ReleaseStringUTFChars(jparams, params);
+}
+extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getContactParameters(JNIEnv* env,jobject thiz,jlong proxyCfg) {
+ const char* params = linphone_proxy_config_get_contact_parameters((LinphoneProxyConfig*)proxyCfg);
+ return params ? env->NewStringUTF(params) : NULL;
+}
+extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getContactUriParameters(JNIEnv* env,jobject thiz,jlong proxyCfg) {
+ const char* params = linphone_proxy_config_get_contact_uri_parameters((LinphoneProxyConfig*)proxyCfg);
+ return params ? env->NewStringUTF(params) : NULL;
+}
+
+
extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_setRoute(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jroute) {
if (jroute != NULL) {
const char* route = env->GetStringUTFChars(jroute, NULL);
@@ -1124,6 +2177,7 @@ extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_edit(JNIEnv* env,
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_done(JNIEnv* env,jobject thiz,jlong proxyCfg) {
linphone_proxy_config_done((LinphoneProxyConfig*)proxyCfg);
}
+
extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_normalizePhoneNumber(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jnumber) {
if (jnumber == 0) {
ms_error("cannot normalized null number");
@@ -1162,10 +2216,15 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getDomain(JNIE
return NULL;
}
}
+
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setDialEscapePlus(JNIEnv* env,jobject thiz,jlong proxyCfg,jboolean value) {
linphone_proxy_config_set_dial_escape_plus((LinphoneProxyConfig*)proxyCfg,value);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneProxyConfigImpl_getDialEscapePlus(JNIEnv* env,jobject thiz,jlong proxyCfg) {
+ return (jboolean) linphone_proxy_config_get_dial_escape_plus((LinphoneProxyConfig*)proxyCfg);
+}
+
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setDialPrefix(JNIEnv* env
,jobject thiz
,jlong proxyCfg
@@ -1174,6 +2233,12 @@ extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setDialPrefix(JNI
linphone_proxy_config_set_dial_prefix((LinphoneProxyConfig*)proxyCfg,prefix);
env->ReleaseStringUTFChars(jprefix, prefix);
}
+
+extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getDialPrefix(JNIEnv* env,jobject thiz,jlong proxyCfg) {
+ const char * prefix = linphone_proxy_config_get_dial_prefix((LinphoneProxyConfig*)proxyCfg);
+ return prefix ? env->NewStringUTF(prefix) : NULL;
+}
+
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_enablePublish(JNIEnv* env
,jobject thiz
,jlong proxyCfg
@@ -1184,36 +2249,197 @@ extern "C" jboolean Java_org_linphone_core_LinphoneProxyConfigImpl_publishEnable
return (jboolean)linphone_proxy_config_publish_enabled((LinphoneProxyConfig*)proxyCfg);
}
+extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getError(JNIEnv* env,jobject thiz,jlong ptr) {
+ return linphone_proxy_config_get_error((LinphoneProxyConfig *) ptr);
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneProxyConfigImpl_getErrorInfo(JNIEnv* env,jobject thiz,jlong ptr) {
+ return (jlong)linphone_proxy_config_get_error_info((LinphoneProxyConfig *) ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getPublishExpires(JNIEnv* env,jobject thiz,jlong ptr) {
+ return (jint)linphone_proxy_config_get_publish_expires((LinphoneProxyConfig *) ptr);
+}
+extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setPublishExpires(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,jint jval) {
+ linphone_proxy_config_set_publish_expires((LinphoneProxyConfig *) ptr, jval);
+}
//Auth Info
extern "C" jlong Java_org_linphone_core_LinphoneAuthInfoImpl_newLinphoneAuthInfo(JNIEnv* env
- , jobject thiz
- , jstring jusername
- , jstring juserid
- , jstring jpassword
- , jstring jha1
- , jstring jrealm) {
-
- const char* username = env->GetStringUTFChars(jusername, NULL);
- const char* userid = env->GetStringUTFChars(juserid, NULL);
- const char* password = env->GetStringUTFChars(jpassword, NULL);
- const char* ha1 = env->GetStringUTFChars(jha1, NULL);
- const char* realm = env->GetStringUTFChars(jrealm, NULL);
- jlong auth = (jlong)linphone_auth_info_new(username,userid,password,ha1,realm);
-
- env->ReleaseStringUTFChars(jusername, username);
- env->ReleaseStringUTFChars(juserid, userid);
- env->ReleaseStringUTFChars(jpassword, password);
- env->ReleaseStringUTFChars(jha1, ha1);
- env->ReleaseStringUTFChars(jrealm, realm);
- return auth;
-
+ , jobject thiz ) {
+ return (jlong)linphone_auth_info_new(NULL,NULL,NULL,NULL,NULL,NULL);
}
extern "C" void Java_org_linphone_core_LinphoneAuthInfoImpl_delete(JNIEnv* env
, jobject thiz
, jlong ptr) {
linphone_auth_info_destroy((LinphoneAuthInfo*)ptr);
}
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getPassword
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getPassword
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* passwd = linphone_auth_info_get_passwd((LinphoneAuthInfo*)auth_info);
+ if (passwd) {
+ return env->NewStringUTF(passwd);
+ } else {
+ return NULL;
+ }
+}
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getRealm
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getRealm
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* realm = linphone_auth_info_get_realm((LinphoneAuthInfo*)auth_info);
+ if (realm) {
+ return env->NewStringUTF(realm);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getDomain
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getDomain
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* domain = linphone_auth_info_get_domain((LinphoneAuthInfo*)auth_info);
+ if (domain) {
+ return env->NewStringUTF(domain);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getUsername
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getUsername
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* username = linphone_auth_info_get_username((LinphoneAuthInfo*)auth_info);
+ if (username) {
+ return env->NewStringUTF(username);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setPassword
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setPassword
+(JNIEnv *env, jobject, jlong auth_info, jstring jpassword) {
+ const char* password = jpassword?env->GetStringUTFChars(jpassword, NULL):NULL;
+ linphone_auth_info_set_passwd((LinphoneAuthInfo*)auth_info,password);
+ if (password) env->ReleaseStringUTFChars(jpassword, password);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setRealm
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setRealm
+(JNIEnv *env, jobject, jlong auth_info, jstring jrealm) {
+ const char* realm = jrealm?env->GetStringUTFChars(jrealm, NULL):NULL;
+ linphone_auth_info_set_realm((LinphoneAuthInfo*)auth_info,realm);
+ if (realm) env->ReleaseStringUTFChars(jrealm, realm);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setDomain
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setDomain
+(JNIEnv *env, jobject, jlong auth_info, jstring jdomain) {
+ const char* domain = jdomain ? env->GetStringUTFChars(jdomain, NULL) : NULL;
+ linphone_auth_info_set_domain((LinphoneAuthInfo*)auth_info, domain);
+ if (domain)
+ env->ReleaseStringUTFChars(jdomain, domain);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setUsername
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setUsername
+(JNIEnv *env, jobject, jlong auth_info, jstring jusername) {
+ const char* username = jusername?env->GetStringUTFChars(jusername, NULL):NULL;
+ linphone_auth_info_set_username((LinphoneAuthInfo*)auth_info,username);
+ if (username) env->ReleaseStringUTFChars(jusername, username);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setAuthUserId
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setUserId
+(JNIEnv *env, jobject, jlong auth_info, jstring juserid) {
+ const char* userid = juserid?env->GetStringUTFChars(juserid, NULL):NULL;
+ linphone_auth_info_set_userid((LinphoneAuthInfo*)auth_info,userid);
+ if (userid) env->ReleaseStringUTFChars(juserid, userid);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getAuthUserId
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getUserId
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* userid = linphone_auth_info_get_userid((LinphoneAuthInfo*)auth_info);
+ if (userid) {
+ return env->NewStringUTF(userid);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: setHa1
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_setHa1
+(JNIEnv *env, jobject, jlong auth_info, jstring jha1) {
+ const char* ha1 = jha1?env->GetStringUTFChars(jha1, NULL):NULL;
+ linphone_auth_info_set_ha1((LinphoneAuthInfo*)auth_info,ha1);
+ if (ha1) env->ReleaseStringUTFChars(jha1, ha1);
+}
+
+
+/*
+ * Class: org_linphone_core_LinphoneAuthInfoImpl
+ * Method: getHa1
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneAuthInfoImpl_getHa1
+(JNIEnv *env , jobject, jlong auth_info) {
+ const char* ha1 = linphone_auth_info_get_ha1((LinphoneAuthInfo*)auth_info);
+ if (ha1) {
+ return env->NewStringUTF(ha1);
+ } else {
+ return NULL;
+ }
+}
+
//LinphoneAddress
@@ -1221,21 +2447,34 @@ extern "C" jlong Java_org_linphone_core_LinphoneAddressImpl_newLinphoneAddressIm
,jobject thiz
,jstring juri
,jstring jdisplayName) {
- const char* uri = env->GetStringUTFChars(juri, NULL);
+ const char* uri = juri?env->GetStringUTFChars(juri, NULL):NULL;
LinphoneAddress* address = linphone_address_new(uri);
if (jdisplayName && address) {
const char* displayName = env->GetStringUTFChars(jdisplayName, NULL);
linphone_address_set_display_name(address,displayName);
env->ReleaseStringUTFChars(jdisplayName, displayName);
}
- env->ReleaseStringUTFChars(juri, uri);
+ if (uri) env->ReleaseStringUTFChars(juri, uri);
return (jlong) address;
}
-extern "C" void Java_org_linphone_core_LinphoneAddressImpl_delete(JNIEnv* env
+
+extern "C" jlong Java_org_linphone_core_LinphoneAddressImpl_ref(JNIEnv* env
,jobject thiz
,jlong ptr) {
- linphone_address_destroy((LinphoneAddress*)ptr);
+ return (jlong)linphone_address_ref((LinphoneAddress*)ptr);
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneAddressImpl_clone(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jlong) (ptr ? linphone_address_clone((const LinphoneAddress*)ptr) : NULL);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneAddressImpl_unref(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_address_unref((LinphoneAddress*)ptr);
}
extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDisplayName(JNIEnv* env
@@ -1268,7 +2507,18 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDomain(JNIEnv*
return NULL;
}
}
-
+extern "C" jint Java_org_linphone_core_LinphoneAddressImpl_getTransport(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ LinphoneTransportType transporttype = linphone_address_get_transport((LinphoneAddress*)ptr);
+ return (jint)transporttype;
+}
+extern "C" jint Java_org_linphone_core_LinphoneAddressImpl_getPort(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ int port = linphone_address_get_port((LinphoneAddress*)ptr);
+ return (jint)port;
+}
extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_toString(JNIEnv* env
,jobject thiz
,jlong ptr) {
@@ -1293,7 +2543,34 @@ extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setDisplayName(JNIEnv
linphone_address_set_display_name((LinphoneAddress*)address,displayName);
if (displayName != NULL) env->ReleaseStringUTFChars(jdisplayName, displayName);
}
-
+extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setUserName(JNIEnv* env
+ ,jobject thiz
+ ,jlong address
+ ,jstring juserName) {
+ const char* userName = juserName!= NULL?env->GetStringUTFChars(juserName, NULL):NULL;
+ linphone_address_set_username((LinphoneAddress*)address,userName);
+ if (userName != NULL) env->ReleaseStringUTFChars(juserName, userName);
+}
+extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setDomain(JNIEnv* env
+ ,jobject thiz
+ ,jlong address
+ ,jstring jdomain) {
+ const char* domain = jdomain!= NULL?env->GetStringUTFChars(jdomain, NULL):NULL;
+ linphone_address_set_domain((LinphoneAddress*)address,domain);
+ if (domain != NULL) env->ReleaseStringUTFChars(jdomain, domain);
+}
+extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setTransport(JNIEnv* env
+ ,jobject thiz
+ ,jlong address
+ ,jint jtransport) {
+ linphone_address_set_transport((LinphoneAddress*)address, (LinphoneTransportType) jtransport);
+}
+extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setPort(JNIEnv* env
+ ,jobject thiz
+ ,jlong address
+ ,jint jport) {
+ linphone_address_set_port((LinphoneAddress*)address, (LinphoneTransportType) jport);
+}
//CallLog
extern "C" jlong Java_org_linphone_core_LinphoneCallLogImpl_getFrom(JNIEnv* env
@@ -1348,95 +2625,21 @@ extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getUploadBandwidt
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) {
const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
- const report_block_t *srb = NULL;
-
- if (!stats || !stats->sent_rtcp)
- return (jfloat)0.0;
- /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
- if (stats->sent_rtcp->b_cont != NULL)
- msgpullup(stats->sent_rtcp, -1);
- if (rtcp_is_SR(stats->sent_rtcp))
- srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
- else if (rtcp_is_RR(stats->sent_rtcp))
- srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
- if (!srb)
- return (jfloat)0.0;
- return (jfloat)(100.0 * report_block_get_fraction_lost(srb) / 256.0);
+ return (jfloat) linphone_call_stats_get_sender_loss_rate(stats);
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) {
const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
- const report_block_t *rrb = NULL;
-
- if (!stats || !stats->received_rtcp)
- return (jfloat)0.0;
- /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
- if (stats->received_rtcp->b_cont != NULL)
- msgpullup(stats->received_rtcp, -1);
- if (rtcp_is_RR(stats->received_rtcp))
- rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
- else if (rtcp_is_SR(stats->received_rtcp))
- rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
- if (!rrb)
- return (jfloat)0.0;
- return (jfloat)(100.0 * report_block_get_fraction_lost(rrb) / 256.0);
+ return (jfloat) linphone_call_stats_get_receiver_loss_rate(stats);
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) {
LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
LinphoneCall *call = (LinphoneCall *)call_ptr;
- const LinphoneCallParams *params;
- const PayloadType *pt;
- const report_block_t *srb = NULL;
-
- if (!stats || !call || !stats->sent_rtcp)
- return (jfloat)0.0;
- params = linphone_call_get_current_params(call);
- if (!params)
- return (jfloat)0.0;
- /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
- if (stats->sent_rtcp->b_cont != NULL)
- msgpullup(stats->sent_rtcp, -1);
- if (rtcp_is_SR(stats->sent_rtcp))
- srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
- else if (rtcp_is_RR(stats->sent_rtcp))
- srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
- if (!srb)
- return (jfloat)0.0;
- if (stats->type == LINPHONE_CALL_STATS_AUDIO)
- pt = linphone_call_params_get_used_audio_codec(params);
- else
- pt = linphone_call_params_get_used_video_codec(params);
- if (!pt || (pt->clock_rate == 0))
- return (jfloat)0.0;
- return (jfloat)((float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate);
+ return (jfloat) linphone_call_stats_get_sender_interarrival_jitter(stats, call);
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) {
LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
LinphoneCall *call = (LinphoneCall *)call_ptr;
- const LinphoneCallParams *params;
- const PayloadType *pt;
- const report_block_t *rrb = NULL;
-
- if (!stats || !call || !stats->received_rtcp)
- return (jfloat)0.0;
- params = linphone_call_get_current_params(call);
- if (!params)
- return (jfloat)0.0;
- /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */
- if (stats->received_rtcp->b_cont != NULL)
- msgpullup(stats->received_rtcp, -1);
- if (rtcp_is_SR(stats->received_rtcp))
- rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
- else if (rtcp_is_RR(stats->received_rtcp))
- rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
- if (!rrb)
- return (jfloat)0.0;
- if (stats->type == LINPHONE_CALL_STATS_AUDIO)
- pt = linphone_call_params_get_used_audio_codec(params);
- else
- pt = linphone_call_params_get_used_video_codec(params);
- if (!pt || (pt->clock_rate == 0))
- return (jfloat)0.0;
- return (jfloat)((float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate);
+ return (jfloat) linphone_call_stats_get_receiver_interarrival_jitter(stats, call);
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getRoundTripDelay(JNIEnv *env, jobject thiz, jlong stats_ptr) {
return (jfloat)((LinphoneCallStats *)stats_ptr)->round_trip_delay;
@@ -1444,23 +2647,29 @@ extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getRoundTripDelay
extern "C" jlong Java_org_linphone_core_LinphoneCallStatsImpl_getLatePacketsCumulativeNumber(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) {
LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
LinphoneCall *call = (LinphoneCall *)call_ptr;
- rtp_stats_t rtp_stats;
-
- if (!stats || !call)
- return (jlong)0;
- memset(&rtp_stats, 0, sizeof(rtp_stats));
- if (stats->type == LINPHONE_CALL_STATS_AUDIO)
- audio_stream_get_local_rtp_stats(call->audiostream, &rtp_stats);
-#ifdef VIDEO_ENABLED
- else
- video_stream_get_local_rtp_stats(call->videostream, &rtp_stats);
-#endif
- return (jlong)rtp_stats.outoftime;
+ return (jlong) linphone_call_stats_get_late_packets_cumulative_number(stats, call);
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getJitterBufferSize(JNIEnv *env, jobject thiz, jlong stats_ptr) {
return (jfloat)((LinphoneCallStats *)stats_ptr)->jitter_stats.jitter_buffer_size_ms;
}
+extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getLocalLossRate(JNIEnv *env, jobject thiz,jlong stats_ptr) {
+ const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
+ return stats->local_loss_rate;
+}
+
+extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getLocalLateRate(JNIEnv *env, jobject thiz, jlong stats_ptr) {
+ const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr;
+ return stats->local_late_rate;
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCallStatsImpl_updateStats(JNIEnv *env, jobject thiz, jlong call_ptr, jint mediatype) {
+ if (mediatype==LINPHONE_CALL_STATS_AUDIO)
+ linphone_call_get_audio_stats((LinphoneCall*)call_ptr);
+ else
+ linphone_call_get_video_stats((LinphoneCall*)call_ptr);
+}
+
/*payloadType*/
extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_toString(JNIEnv* env,jobject thiz,jlong ptr) {
PayloadType* pt = (PayloadType*)ptr;
@@ -1521,6 +2730,18 @@ extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteAddress( JNIEn
return (jlong)linphone_call_get_remote_address((LinphoneCall*)ptr);
}
+extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getErrorInfo( JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jlong)linphone_call_get_error_info((LinphoneCall*)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getReason( JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jint)linphone_call_get_reason((LinphoneCall*)ptr);
+}
+
extern "C" jstring Java_org_linphone_core_LinphoneCallImpl_getRemoteUserAgent(JNIEnv *env, jobject thiz, jlong ptr) {
LinphoneCall *call = (LinphoneCall *)ptr;
const char *value=linphone_call_get_remote_user_agent(call);
@@ -1542,6 +2763,39 @@ extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getState( JNIEnv* env
,jlong ptr) {
return (jint)linphone_call_get_state((LinphoneCall*)ptr);
}
+
+/*
+ * Class: org_linphone_core_LinphoneCallImpl
+ * Method: getTransferState
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCallImpl_getTransferState(JNIEnv *, jobject jobj, jlong callptr){
+ LinphoneCall *call=(LinphoneCall*)callptr;
+ return linphone_call_get_transfer_state(call);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCallImpl
+ * Method: getTransfererCall
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCallImpl_getTransfererCall(JNIEnv *env, jobject jCall, jlong callptr){
+ LinphoneCall *call=(LinphoneCall*)callptr;
+ LinphoneCall *ret=linphone_call_get_transferer_call(call);
+ return getCall(env,ret);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCallImpl
+ * Method: getTransferTargetCall
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCallImpl_getTransferTargetCall(JNIEnv *env, jobject jCall, jlong callptr){
+ LinphoneCall *call=(LinphoneCall*)callptr;
+ LinphoneCall *ret=linphone_call_get_transfer_target_call(call);
+ return getCall(env,ret);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCallImpl_enableEchoCancellation( JNIEnv* env
,jobject thiz
,jlong ptr
@@ -1569,8 +2823,7 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isEchoLimiterEnabled
extern "C" jobject Java_org_linphone_core_LinphoneCallImpl_getReplacedCall( JNIEnv* env
,jobject thiz
,jlong ptr) {
- LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data(linphone_call_get_core((LinphoneCall*)ptr));
- return lcd->getCall(env,linphone_call_get_replaced_call((LinphoneCall*)ptr));
+ return getCall(env,linphone_call_get_replaced_call((LinphoneCall*)ptr));
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getCurrentQuality( JNIEnv* env
@@ -1585,6 +2838,15 @@ extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getAverageQuality( JNI
return (jfloat)linphone_call_get_average_quality((LinphoneCall*)ptr);
}
+extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getPlayer(JNIEnv *env, jobject thiz, jlong callPtr) {
+ return (jlong)linphone_call_get_player((LinphoneCall *)callPtr);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_mediaInProgress( JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jboolean) linphone_call_media_in_progress((LinphoneCall*)ptr);
+}
//LinphoneFriend
extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env
@@ -1594,10 +2856,12 @@ extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNI
if (jFriendUri) {
const char* friendUri = env->GetStringUTFChars(jFriendUri, NULL);
- lResult= linphone_friend_new_with_addr(friendUri);
+ lResult = linphone_friend_new_with_address(friendUri);
+ linphone_friend_set_user_data(lResult,env->NewWeakGlobalRef(thiz));
env->ReleaseStringUTFChars(jFriendUri, friendUri);
} else {
lResult = linphone_friend_new();
+ linphone_friend_set_user_data(lResult,env->NewWeakGlobalRef(thiz));
}
return (jlong)lResult;
}
@@ -1605,7 +2869,7 @@ extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setAddress(JNIEnv* en
,jobject thiz
,jlong ptr
,jlong linphoneAddress) {
- linphone_friend_set_addr((LinphoneFriend*)ptr,(LinphoneAddress*)linphoneAddress);
+ linphone_friend_set_address((LinphoneFriend*)ptr,(LinphoneAddress*)linphoneAddress);
}
extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_getAddress(JNIEnv* env
,jobject thiz
@@ -1639,6 +2903,52 @@ extern "C" jint Java_org_linphone_core_LinphoneFriendImpl_getStatus(JNIEnv* env
,jlong ptr) {
return (jint)linphone_friend_get_status((LinphoneFriend*)ptr);
}
+extern "C" jobject Java_org_linphone_core_LinphoneFriendImpl_getCore(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ LinphoneCore *lc=linphone_friend_get_core((LinphoneFriend*)ptr);
+ if (lc!=NULL){
+ jobject core = (jobject)linphone_core_get_user_data(lc);
+ return core;
+ }
+ return NULL;
+}
+extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setRefKey(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,jstring jkey) {
+ const char* key = env->GetStringUTFChars(jkey, NULL);
+ linphone_friend_set_ref_key((LinphoneFriend*)ptr,key);
+ env->ReleaseStringUTFChars(jkey, key);
+}
+extern "C" jstring Java_org_linphone_core_LinphoneFriendImpl_getRefKey(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ const char * key = linphone_friend_get_ref_key((LinphoneFriend *)ptr);
+ return key ? env->NewStringUTF(key) : NULL;
+}
+
+
+extern "C" void Java_org_linphone_core_LinphoneFriendImpl_finalize(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ LinphoneFriend *lfriend=(LinphoneFriend*)ptr;
+ linphone_friend_set_user_data(lfriend,NULL);
+ linphone_friend_unref(lfriend);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneFriendImpl
+ * Method: getPresenceModel
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneFriendImpl_getPresenceModel(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphoneFriend *lf = (LinphoneFriend *)ptr;
+ LinphonePresenceModel *model = (LinphonePresenceModel *)linphone_friend_get_presence_model(lf);
+ if (model == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceModelImpl", linphone_presence_model, model);
+}
+
extern "C" void Java_org_linphone_core_LinphoneFriendImpl_edit(JNIEnv* env
,jobject thiz
,jlong ptr) {
@@ -1655,16 +2965,55 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeFriend(JNIEnv* en
,jlong lf) {
linphone_core_remove_friend((LinphoneCore*)ptr, (LinphoneFriend*)lf);
}
-extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getFriendByAddress(JNIEnv* env
+extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getFriendByAddress(JNIEnv* env
,jobject thiz
,jlong ptr
,jstring jaddress) {
const char* address = env->GetStringUTFChars(jaddress, NULL);
LinphoneFriend *lf = linphone_core_get_friend_by_address((LinphoneCore*)ptr, address);
env->ReleaseStringUTFChars(jaddress, address);
- return (jlong) lf;
+ if(lf != NULL) {
+ jobject jfriend = getFriend(env,lf);
+ return jfriend;
+ } else {
+ return NULL;
+ }
+}
+
+extern "C" jlongArray _LinphoneChatRoomImpl_getHistory(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,MSList* history) {
+ int historySize = ms_list_size(history);
+ jlongArray jHistory = env->NewLongArray(historySize);
+ jlong *jInternalArray = env->GetLongArrayElements(jHistory, NULL);
+
+ for (int i = 0; i < historySize; i++) {
+ jInternalArray[i] = (unsigned long) (history->data);
+ history = history->next;
+ }
+
+ ms_list_free(history);
+
+ env->ReleaseLongArrayElements(jHistory, jInternalArray, 0);
+
+ return jHistory;
+}
+extern "C" jlongArray Java_org_linphone_core_LinphoneChatRoomImpl_getHistoryRange(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,jint start
+ ,jint end) {
+ MSList* history = linphone_chat_room_get_history_range((LinphoneChatRoom*)ptr, start, end);
+ return _LinphoneChatRoomImpl_getHistory(env, thiz, ptr, history);
+}
+extern "C" jlongArray Java_org_linphone_core_LinphoneChatRoomImpl_getHistory(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,jint limit) {
+ MSList* history = linphone_chat_room_get_history((LinphoneChatRoom*)ptr, limit);
+ return _LinphoneChatRoomImpl_getHistory(env, thiz, ptr, history);
}
-//LinphoneChatRoom
extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_getPeerAddress(JNIEnv* env
,jobject thiz
,jlong ptr) {
@@ -1680,17 +3029,150 @@ extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_createLinphoneChatM
return (jlong) chatMessage;
}
-extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setUserData(JNIEnv* env
- ,jobject thiz
- ,jlong ptr) {
- jobject ud = env->NewGlobalRef(thiz);
- linphone_chat_message_set_user_data((LinphoneChatMessage*)ptr,(void*) ud);
+extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_createLinphoneChatMessage2(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr
+ ,jstring jmessage
+ ,jstring jurl
+ ,jint state
+ ,jlong time
+ ,jboolean read
+ ,jboolean incoming) {
+ const char* message = jmessage?env->GetStringUTFChars(jmessage, NULL):NULL;
+ const char* url = jurl?env->GetStringUTFChars(jurl, NULL):NULL;
+
+ LinphoneChatMessage *chatMessage = linphone_chat_room_create_message_2(
+ (LinphoneChatRoom *)ptr, message, url, (LinphoneChatMessageState)state,
+ (time_t)time, read, incoming);
+
+ if (jmessage != NULL)
+ env->ReleaseStringUTFChars(jmessage, message);
+ if (jurl != NULL)
+ env->ReleaseStringUTFChars(jurl, url);
+
+ return (jlong) chatMessage;
}
-extern "C" jstring Java_org_linphone_core_LinphoneChatMessageImpl_getText(JNIEnv* env
+extern "C" jint Java_org_linphone_core_LinphoneChatRoomImpl_getHistorySize (JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jint) linphone_chat_room_get_history_size((LinphoneChatRoom*)ptr);
+}
+extern "C" jint Java_org_linphone_core_LinphoneChatRoomImpl_getUnreadMessagesCount(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jint) linphone_chat_room_get_unread_messages_count((LinphoneChatRoom*)ptr);
+}
+extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_deleteHistory(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_room_delete_history((LinphoneChatRoom*)ptr);
+}
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneChatRoomImpl_compose(JNIEnv *env, jobject thiz, jlong ptr) {
+ linphone_chat_room_compose((LinphoneChatRoom *)ptr);
+}
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneChatRoomImpl_isRemoteComposing(JNIEnv *env, jobject thiz, jlong ptr) {
+ return (jboolean)linphone_chat_room_is_remote_composing((LinphoneChatRoom *)ptr);
+}
+extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_deleteMessage(JNIEnv* env
+ ,jobject thiz
+ ,jlong room
+ ,jlong msg) {
+ linphone_chat_room_delete_message((LinphoneChatRoom*)room, (LinphoneChatMessage*)msg);
+}
+extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_markAsRead(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_room_mark_as_read((LinphoneChatRoom*)ptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_destroy(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_room_destroy((LinphoneChatRoom*)ptr);
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_createFileTransferMessage(JNIEnv* env, jobject thiz, jlong ptr, jstring jname, jstring jtype, jstring jsubtype, jint data_size) {
+ LinphoneContentPrivate content = {0};
+ LinphoneChatMessage *message = NULL;
+
+ content.type = (char*)env->GetStringUTFChars(jtype, NULL);
+ content.subtype = (char*)env->GetStringUTFChars(jsubtype, NULL);
+ content.name = (char*)env->GetStringUTFChars(jname, NULL);
+ content.size = data_size;
+ message = linphone_chat_room_create_file_transfer_message((LinphoneChatRoom *)ptr, LINPHONE_CONTENT(&content));
+ env->ReleaseStringUTFChars(jtype, content.type);
+ env->ReleaseStringUTFChars(jsubtype, content.subtype);
+ env->ReleaseStringUTFChars(jname, content.name);
+
+ return (jlong) message;
+}
+
+
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_cancelFileTransfer(JNIEnv* env, jobject thiz, jlong ptr) {
+ linphone_chat_message_cancel_file_transfer((LinphoneChatMessage *)ptr);
+}
+
+extern "C" jobject Java_org_linphone_core_LinphoneChatMessageImpl_getFileTransferInformation(JNIEnv* env, jobject thiz, jlong ptr) {
+ const LinphoneContent *content = linphone_chat_message_get_file_transfer_information((LinphoneChatMessage *)ptr);
+ if (content)
+ return create_java_linphone_content(env, content);
+ return NULL;
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneChatMessageImpl_getAppData(JNIEnv* env, jobject thiz, jlong ptr) {
+ const char * app_data = linphone_chat_message_get_appdata((LinphoneChatMessage *)ptr);
+ return app_data ? env->NewStringUTF(app_data) : NULL;
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setAppData(JNIEnv* env, jobject thiz, jlong ptr, jstring appdata) {
+ const char * data = appdata ? env->GetStringUTFChars(appdata, NULL) : NULL;
+ linphone_chat_message_set_appdata((LinphoneChatMessage *)ptr, data);
+ if (appdata)
+ env->ReleaseStringUTFChars(appdata, data);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setFileTransferServer(JNIEnv* env, jobject thiz, jlong ptr, jstring server_url) {
+ const char * url = server_url ? env->GetStringUTFChars(server_url, NULL) : NULL;
+ linphone_core_set_file_transfer_server((LinphoneCore *)ptr, url);
+ if (server_url)
+ env->ReleaseStringUTFChars(server_url, url);
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getFileTransferServer(JNIEnv* env, jobject thiz, jlong ptr) {
+ const char * server_url = linphone_core_get_file_transfer_server((LinphoneCore *)ptr);
+ return server_url ? env->NewStringUTF(server_url) : NULL;
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_store(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_message_store((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jbyteArray Java_org_linphone_core_LinphoneChatMessageImpl_getText(JNIEnv* env
,jobject thiz
,jlong ptr) {
- jstring jvalue =env->NewStringUTF(linphone_chat_message_get_text((LinphoneChatMessage*)ptr));
- return jvalue;
+ const char *message = linphone_chat_message_get_text((LinphoneChatMessage*)ptr);
+ if (message){
+ size_t length = strlen(message);
+ jbyteArray array = env->NewByteArray(length);
+ env->SetByteArrayRegion(array, 0, length, (const jbyte*)message);
+ return array;
+ }
+ return NULL;
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneChatMessageImpl_getReason(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return linphone_chat_message_get_reason((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getErrorInfo(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jlong)linphone_chat_message_get_error_info((LinphoneChatMessage*)ptr);
}
extern "C" jstring Java_org_linphone_core_LinphoneChatMessageImpl_getCustomHeader(JNIEnv* env
@@ -1732,12 +3214,195 @@ extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getFrom(JNIEnv*
return (jlong) linphone_chat_message_get_from((LinphoneChatMessage*)ptr);
}
+extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getTo(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jlong) linphone_chat_message_get_to((LinphoneChatMessage*)ptr);
+}
+
extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getPeerAddress(JNIEnv* env
,jobject thiz
,jlong ptr) {
return (jlong) linphone_chat_message_get_peer_address((LinphoneChatMessage*)ptr);
}
+extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getTime(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jlong) linphone_chat_message_get_time((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneChatMessageImpl_getStatus(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jint) linphone_chat_message_get_state((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneChatMessageImpl_isRead(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jboolean) linphone_chat_message_is_read((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneChatMessageImpl_isOutgoing(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jboolean) linphone_chat_message_is_outgoing((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneChatMessageImpl_getStorageId(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jint) linphone_chat_message_get_storage_id((LinphoneChatMessage*)ptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setFileTransferFilepath(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr, jstring jpath) {
+ const char* path = env->GetStringUTFChars(jpath, NULL);
+ linphone_chat_message_set_file_transfer_filepath((LinphoneChatMessage*)ptr, path);
+ env->ReleaseStringUTFChars(jpath, path);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_downloadFile(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_message_download_file((LinphoneChatMessage*)ptr);
+}
+
+static void message_state_changed(LinphoneChatMessage* msg, LinphoneChatMessageState state) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM\n");
+ return;
+ }
+
+ jobject listener = (jobject) msg->cb_ud;
+ jclass clazz = (jclass) env->GetObjectClass(listener);
+ jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageStateChanged","(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneChatMessage$State;)V");
+ jobject jmessage = getChatMessage(env, msg);
+ env->DeleteLocalRef(clazz);
+
+ jclass chatMessageStateClass = (jclass)env->FindClass("org/linphone/core/LinphoneChatMessage$State");
+ jmethodID chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass, "fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;");
+ env->CallVoidMethod(listener, method, jmessage, env->CallStaticObjectMethod(chatMessageStateClass, chatMessageStateFromIntId, (jint)state));
+
+ if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateNotDelivered) {
+ env->DeleteGlobalRef(listener);
+ }
+ env->DeleteLocalRef(chatMessageStateClass);
+}
+
+static void file_transfer_progress_indication(LinphoneChatMessage *msg, const LinphoneContent* content, size_t offset, size_t total) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM\n");
+ return;
+ }
+
+ jobject listener = (jobject) msg->cb_ud;
+ jclass clazz = (jclass) env->GetObjectClass(listener);
+ jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageFileTransferProgressChanged", "(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;II)V");
+ env->DeleteLocalRef(clazz);
+ jobject jmessage = getChatMessage(env, msg);
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+ env->CallVoidMethod(listener, method, jmessage, jcontent, offset, total);
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+}
+
+static void file_transfer_recv(LinphoneChatMessage *msg, const LinphoneContent* content, const LinphoneBuffer *buffer) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ if (result != 0) {
+ ms_error("cannot attach VM\n");
+ return;
+ }
+
+ jobject listener = (jobject) msg->cb_ud;
+ jclass clazz = (jclass) env->GetObjectClass(listener);
+ jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageFileTransferReceived", "(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Lorg/linphone/core/LinphoneBuffer;)V");
+ env->DeleteLocalRef(clazz);
+
+ jobject jmessage = getChatMessage(env, msg);
+ jobject jbuffer = buffer ? create_java_linphone_buffer(env, buffer) : NULL;
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+ env->CallVoidMethod(listener, method, jmessage, jcontent, jbuffer);
+ if (jbuffer) {
+ env->DeleteLocalRef(jbuffer);
+ }
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+}
+
+static LinphoneBuffer* file_transfer_send(LinphoneChatMessage *msg, const LinphoneContent* content, size_t offset, size_t size) {
+ JNIEnv *env = 0;
+ jint result = jvm->AttachCurrentThread(&env,NULL);
+ LinphoneBuffer *buffer = NULL;
+ if (result != 0) {
+ ms_error("cannot attach VM\n");
+ return buffer;
+ }
+
+ jobject listener = (jobject) msg->cb_ud;
+ jclass clazz = (jclass) env->GetObjectClass(listener);
+ jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageFileTransferSent","(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;IILorg/linphone/core/LinphoneBuffer;)V");
+ env->DeleteLocalRef(clazz);
+
+ jobject jmessage = getChatMessage(env, msg);
+ jobject jbuffer = create_java_linphone_buffer(env, NULL);
+ jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
+ env->CallVoidMethod(listener, method, jmessage, jcontent, offset, size, jbuffer);
+ if (jcontent) {
+ env->DeleteLocalRef(jcontent);
+ }
+
+ buffer = create_c_linphone_buffer_from_java_linphone_buffer(env, jbuffer);
+ env->DeleteLocalRef(jbuffer);
+ return buffer;
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
+ jobject listener = env->NewGlobalRef(jlistener);
+ LinphoneChatMessage *message = (LinphoneChatMessage *)ptr;
+ LinphoneChatMessageCbs *cbs;
+
+ message->cb_ud = listener;
+ cbs = linphone_chat_message_get_callbacks(message);
+ linphone_chat_message_cbs_set_msg_state_changed(cbs, message_state_changed);
+ linphone_chat_message_cbs_set_file_transfer_progress_indication(cbs, file_transfer_progress_indication);
+ linphone_chat_message_cbs_set_file_transfer_recv(cbs, file_transfer_recv);
+ linphone_chat_message_cbs_set_file_transfer_send(cbs, file_transfer_send);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_unref(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ linphone_chat_message_unref((LinphoneChatMessage*)ptr);
+}
+
+extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_getChatRooms(JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ MSList* chats = linphone_core_get_chat_rooms((LinphoneCore*)ptr);
+ int chatsSize = ms_list_size(chats);
+ jlongArray jChats = env->NewLongArray(chatsSize);
+ jlong *jInternalArray = env->GetLongArrayElements(jChats, NULL);
+
+ for (int i = 0; i < chatsSize; i++) {
+ jInternalArray[i] = (unsigned long) (chats->data);
+ chats = chats->next;
+ }
+
+ env->ReleaseLongArrayElements(jChats, jInternalArray, 0);
+
+ return jChats;
+}
+
extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage(JNIEnv* env
,jobject thiz
,jlong ptr
@@ -1758,39 +3423,70 @@ static void chat_room_impl_callback(LinphoneChatMessage* msg, LinphoneChatMessag
jobject listener = (jobject) ud;
jclass clazz = (jclass) env->GetObjectClass(listener);
jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageStateChanged","(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneChatMessage$State;)V");
+ jobject jmessage=(jobject)linphone_chat_message_get_user_data(msg);
- LinphoneCore *lc = linphone_chat_room_get_lc(linphone_chat_message_get_chat_room(msg));
- LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc);
+ jclass chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State"));
+ jmethodID chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;");
env->CallVoidMethod(
listener,
method,
- (jobject)linphone_chat_message_get_user_data(msg),
- env->CallStaticObjectMethod(lcData->chatMessageStateClass,lcData->chatMessageStateFromIntId,(jint)state));
+ jmessage,
+ env->CallStaticObjectMethod(chatMessageStateClass,chatMessageStateFromIntId,(jint)state));
if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateNotDelivered) {
env->DeleteGlobalRef(listener);
+ env->DeleteGlobalRef(jmessage);
+ env->DeleteGlobalRef(chatMessageStateClass);
+ linphone_chat_message_set_user_data(msg,NULL);
}
}
+
+extern "C" jobject Java_org_linphone_core_LinphoneChatRoomImpl_getCore(JNIEnv* env
+ ,jobject thiz
+ ,jlong chatroom_ptr){
+ LinphoneCore *lc=linphone_chat_room_get_core((LinphoneChatRoom*)chatroom_ptr);
+ jobject core = (jobject)linphone_core_get_user_data(lc);
+ return core;
+}
+
extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage2(JNIEnv* env
,jobject thiz
- ,jlong ptr
- ,jlong jmessage
+ ,jlong chatroom_ptr
+ ,jobject message
+ ,jlong messagePtr
,jobject jlistener) {
jobject listener = env->NewGlobalRef(jlistener);
- linphone_chat_room_send_message2((LinphoneChatRoom*)ptr, (LinphoneChatMessage*)jmessage, chat_room_impl_callback, (void*)listener);
+ message = env->NewGlobalRef(message);
+ linphone_chat_message_ref((LinphoneChatMessage*)messagePtr);
+ linphone_chat_message_set_user_data((LinphoneChatMessage*)messagePtr, message);
+ linphone_chat_room_send_message2((LinphoneChatRoom*)chatroom_ptr, (LinphoneChatMessage*)messagePtr, chat_room_impl_callback, (void*)listener);
}
+
+extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendChatMessage(JNIEnv* env
+ ,jobject thiz
+ ,jlong chatroom_ptr
+ ,jobject message
+ ,jlong messagePtr) {
+ message = env->NewGlobalRef(message);
+ linphone_chat_message_ref((LinphoneChatMessage*)messagePtr);
+ linphone_chat_message_set_user_data((LinphoneChatMessage*)messagePtr, message);
+ linphone_chat_room_send_chat_message((LinphoneChatRoom*)chatroom_ptr, (LinphoneChatMessage*)messagePtr);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(JNIEnv* env
,jobject thiz
,jlong lc
,jobject obj) {
jobject oldWindow = (jobject) linphone_core_get_native_video_window_id((LinphoneCore*)lc);
- if (oldWindow != NULL) {
- env->DeleteGlobalRef(oldWindow);
- }
if (obj != NULL) {
obj = env->NewGlobalRef(obj);
+ ms_message("Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(): NewGlobalRef(%p)",obj);
+ }else ms_message("Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(): setting to NULL");
+ linphone_core_set_native_video_window_id((LinphoneCore*)lc,(void *)obj);
+ if (oldWindow != NULL) {
+ ms_message("Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(): DeleteGlobalRef(%p)",oldWindow);
+ env->DeleteGlobalRef(oldWindow);
}
- linphone_core_set_native_video_window_id((LinphoneCore*)lc,(unsigned long)obj);
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreviewWindowId(JNIEnv* env
@@ -1798,13 +3494,15 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreviewWindowId(JNIEn
,jlong lc
,jobject obj) {
jobject oldWindow = (jobject) linphone_core_get_native_preview_window_id((LinphoneCore*)lc);
- if (oldWindow != NULL) {
- env->DeleteGlobalRef(oldWindow);
- }
if (obj != NULL) {
obj = env->NewGlobalRef(obj);
+ ms_message("Java_org_linphone_core_LinphoneCoreImpl_setPreviewWindowId(): NewGlobalRef(%p)",obj);
+ }else ms_message("Java_org_linphone_core_LinphoneCoreImpl_setPreviewWindowId(): setting to NULL");
+ linphone_core_set_native_preview_window_id((LinphoneCore*)lc,(void *)obj);
+ if (oldWindow != NULL) {
+ ms_message("Java_org_linphone_core_LinphoneCoreImpl_setPreviewWindowId(): DeleteGlobalRef(%p)",oldWindow);
+ env->DeleteGlobalRef(oldWindow);
}
- linphone_core_set_native_preview_window_id((LinphoneCore*)lc,(unsigned long)obj);
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDeviceRotation(JNIEnv* env
@@ -1814,6 +3512,19 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDeviceRotation(JNIEnv
linphone_core_set_device_rotation((LinphoneCore*)lc,rotation);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRemoteRingbackTone(JNIEnv *env, jobject thiz, jlong lc, jstring jtone){
+ const char* tone = NULL;
+ if (jtone) tone=env->GetStringUTFChars(jtone, NULL);
+ linphone_core_set_remote_ringback_tone((LinphoneCore*)lc,tone);
+ if (tone) env->ReleaseStringUTFChars(jtone,tone);
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getRemoteRingbackTone(JNIEnv *env, jobject thiz, jlong lc){
+ const char *ret= linphone_core_get_remote_ringback_tone((LinphoneCore*)lc);
+ if (ret==NULL) return NULL;
+ jstring jvalue =env->NewStringUTF(ret);
+ return jvalue;
+}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setFirewallPolicy(JNIEnv *env, jobject thiz, jlong lc, jint enum_value){
linphone_core_set_firewall_policy((LinphoneCore*)lc,(LinphoneFirewallPolicy)enum_value);
@@ -1868,6 +3579,37 @@ extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setMediaEncryption
linphone_call_params_set_media_encryption((LinphoneCallParams*)cp,(LinphoneMediaEncryption)jmenc);
}
+extern "C" jint Java_org_linphone_core_LinphoneCallParamsImpl_getPrivacy(JNIEnv* env
+ ,jobject thiz
+ ,jlong cp
+ ) {
+ return (jint)linphone_call_params_get_privacy((LinphoneCallParams*)cp);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setPrivacy(JNIEnv* env
+ ,jobject thiz
+ ,jlong cp
+ ,jint privacy) {
+ linphone_call_params_set_privacy((LinphoneCallParams*)cp,privacy);
+}
+
+extern "C" jstring Java_org_linphone_core_LinphoneCallParamsImpl_getSessionName(JNIEnv* env
+ ,jobject thiz
+ ,jlong cp
+ ) {
+ const char* name = linphone_call_params_get_session_name((LinphoneCallParams*)cp);
+ return name ? env->NewStringUTF(name) : NULL;
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setSessionName(JNIEnv* env
+ ,jobject thiz
+ ,jlong cp
+ ,jstring jname) {
+ const char *name = jname ? env->GetStringUTFChars(jname,NULL) : NULL;
+ linphone_call_params_set_session_name((LinphoneCallParams*)cp,name);
+ if (name) env->ReleaseStringUTFChars(jname,name);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_audioBandwidth(JNIEnv *env, jobject thiz, jlong lcp, jint bw){
linphone_call_params_set_audio_bandwidth_limit((LinphoneCallParams*)lcp, bw);
}
@@ -1880,8 +3622,21 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_getVideoEnable
return (jboolean)linphone_call_params_video_enabled((LinphoneCallParams*)lcp);
}
+extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableVideoMulticast(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){
+ linphone_call_params_enable_video_multicast((LinphoneCallParams*)lcp, b);
+}
+extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_videoMulticastEnabled(JNIEnv *env, jobject thiz, jlong lcp){
+ return (jboolean)linphone_call_params_video_multicast_enabled((LinphoneCallParams*)lcp);
+}
+extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableAudioMulticast(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){
+ linphone_call_params_enable_audio_multicast((LinphoneCallParams*)lcp, b);
+}
+extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_audioMulticastEnabled(JNIEnv *env, jobject thiz, jlong lcp){
+ return (jboolean)linphone_call_params_audio_multicast_enabled((LinphoneCallParams*)lcp);
+}
+
extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_localConferenceMode(JNIEnv *env, jobject thiz, jlong lcp){
- return (jboolean)linphone_call_params_local_conference_mode((LinphoneCallParams*)lcp);
+ return (jboolean)linphone_call_params_get_local_conference_mode((LinphoneCallParams*)lcp);
}
extern "C" jstring Java_org_linphone_core_LinphoneCallParamsImpl_getCustomHeader(JNIEnv *env, jobject thiz, jlong lcp, jstring jheader_name){
@@ -1907,6 +3662,24 @@ extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setRecordFile(JNIE
}else linphone_call_params_set_record_file((LinphoneCallParams*)lcp,NULL);
}
+extern "C" jintArray Java_org_linphone_core_LinphoneCallParamsImpl_getSentVideoSize(JNIEnv *env, jobject thiz, jlong lcp) {
+ const LinphoneCallParams *params = (LinphoneCallParams *) lcp;
+ MSVideoSize vsize = linphone_call_params_get_sent_video_size(params);
+ jintArray arr = env->NewIntArray(2);
+ int tVsize [2]= {vsize.width,vsize.height};
+ env->SetIntArrayRegion(arr, 0, 2, tVsize);
+ return arr;
+}
+
+extern "C" jintArray Java_org_linphone_core_LinphoneCallParamsImpl_getReceivedVideoSize(JNIEnv *env, jobject thiz, jlong lcp) {
+ const LinphoneCallParams *params = (LinphoneCallParams *) lcp;
+ MSVideoSize vsize = linphone_call_params_get_received_video_size(params);
+ jintArray arr = env->NewIntArray(2);
+ int tVsize [2]= {vsize.width,vsize.height};
+ env->SetIntArrayRegion(arr, 0, 2, tVsize);
+ return arr;
+}
+
extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_destroy(JNIEnv *env, jobject thiz, jlong lc){
return linphone_call_params_destroy((LinphoneCallParams*)lc);
}
@@ -1942,8 +3715,7 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_stopRecording(JNIEnv *en
}
extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_inviteAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong addr, jlong params){
- LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc);
- return lcd->getCall(env,linphone_core_invite_address_with_params((LinphoneCore *)lc, (const LinphoneAddress *)addr, (const LinphoneCallParams *)params));
+ return getCall(env,linphone_core_invite_address_with_params((LinphoneCore *)lc, (const LinphoneAddress *)addr, (const LinphoneCallParams *)params));
}
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong call, jlong params){
@@ -1961,18 +3733,40 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSize(JN
linphone_core_set_preferred_video_size((LinphoneCore *)lc, vsize);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc, jfloat framerate){
+ linphone_core_set_preferred_framerate((LinphoneCore *)lc, framerate);
+}
+
+extern "C" float Java_org_linphone_core_LinphoneCoreImpl_getPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc){
+ return linphone_core_get_preferred_framerate((LinphoneCore *)lc);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSizeByName(JNIEnv *env, jobject thiz, jlong lc, jstring jName) {
+ const char* cName = env->GetStringUTFChars(jName, NULL);
+ linphone_core_set_preferred_video_size_by_name((LinphoneCore *)lc, cName);
+ env->ReleaseStringUTFChars(jName, cName);
+}
+
extern "C" jintArray Java_org_linphone_core_LinphoneCoreImpl_getPreferredVideoSize(JNIEnv *env, jobject thiz, jlong lc){
MSVideoSize vsize = linphone_core_get_preferred_video_size((LinphoneCore *)lc);
- jintArray arr = env->NewIntArray(2);
+ jintArray arr = env->NewIntArray(2);
int tVsize [2]= {vsize.width,vsize.height};
- env->SetIntArrayRegion(arr, 0, 2, tVsize);
- return arr;
+ env->SetIntArrayRegion(arr, 0, 2, tVsize);
+ return arr;
+}
+
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getDownloadBandwidth(JNIEnv *env, jobject thiz, jlong lc) {
+ return (jint) linphone_core_get_download_bandwidth((LinphoneCore *)lc);
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){
linphone_core_set_download_bandwidth((LinphoneCore *)lc, (int) bw);
}
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getUploadBandwidth(JNIEnv *env, jobject thiz, jlong lc) {
+ return (jint) linphone_core_get_upload_bandwidth((LinphoneCore *)lc);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){
linphone_core_set_upload_bandwidth((LinphoneCore *)lc, (int) bw);
}
@@ -1981,10 +3775,18 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUseSipInfoForDtmfs(JN
linphone_core_set_use_info_for_dtmf((LinphoneCore *)lc, (bool) use);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_getUseSipInfoForDtmfs(JNIEnv *env, jobject thiz, jlong lc){
+ return linphone_core_get_use_info_for_dtmf((LinphoneCore *)lc);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUseRfc2833ForDtmfs(JNIEnv *env, jobject thiz, jlong lc, jboolean use){
linphone_core_set_use_rfc2833_for_dtmf((LinphoneCore *)lc, (bool) use);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_getUseRfc2833ForDtmfs(JNIEnv *env, jobject thiz, jlong lc){
+ return (jboolean) linphone_core_get_use_rfc2833_for_dtmf((LinphoneCore *)lc);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadPtime(JNIEnv *env, jobject thiz, jlong lc, jint ptime){
linphone_core_set_download_ptime((LinphoneCore *)lc, (int) ptime);
}
@@ -1996,14 +3798,106 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadPtime(JNIEnv *e
extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getState(JNIEnv* env,jobject thiz,jlong ptr) {
return (jint) linphone_proxy_config_get_state((const LinphoneProxyConfig *) ptr);
}
+
extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setExpires(JNIEnv* env,jobject thiz,jlong ptr,jint delay) {
- linphone_proxy_config_expires((LinphoneProxyConfig *) ptr, (int) delay);
+ linphone_proxy_config_set_expires((LinphoneProxyConfig *) ptr, (int) delay);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getExpires(JNIEnv* env,jobject thiz,jlong ptr) {
+ return linphone_proxy_config_get_expires((LinphoneProxyConfig *) ptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setPrivacy(JNIEnv* env,jobject thiz,jlong ptr,jint privacy) {
+ linphone_proxy_config_set_privacy((LinphoneProxyConfig *) ptr, (int) privacy);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getPrivacy(JNIEnv* env,jobject thiz,jlong ptr) {
+ return linphone_proxy_config_get_privacy((LinphoneProxyConfig *) ptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_enableAvpf(JNIEnv *env, jobject thiz, jlong ptr, jboolean enable) {
+ linphone_proxy_config_enable_avpf((LinphoneProxyConfig *)ptr, (bool)enable);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_avpfEnabled(JNIEnv *env, jobject thiz, jlong ptr) {
+ return linphone_proxy_config_avpf_enabled((LinphoneProxyConfig *)ptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setAvpfRRInterval(JNIEnv *env, jobject thiz, jlong ptr, jint interval) {
+ linphone_proxy_config_set_avpf_rr_interval((LinphoneProxyConfig *)ptr, (uint8_t)interval);
+}
+
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_getAvpfRRInterval(JNIEnv *env, jobject thiz, jlong ptr) {
+ return (jint)linphone_proxy_config_get_avpf_rr_interval((LinphoneProxyConfig *)ptr);
+}
+
+
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_enableQualityReporting(JNIEnv *env, jobject thiz, jlong ptr, jboolean enable) {
+ linphone_proxy_config_enable_quality_reporting((LinphoneProxyConfig *)ptr, (bool)enable);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_quality_reportingEnabled(JNIEnv *env, jobject thiz, jlong ptr) {
+ return linphone_proxy_config_quality_reporting_enabled((LinphoneProxyConfig *)ptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setQualityReportingInterval(JNIEnv *env, jobject thiz, jlong ptr, jint interval) {
+ linphone_proxy_config_set_quality_reporting_interval((LinphoneProxyConfig *)ptr, (uint8_t)interval);
+}
+
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_getQualityReportingInterval(JNIEnv *env, jobject thiz, jlong ptr) {
+ return (jint)linphone_proxy_config_get_quality_reporting_interval((LinphoneProxyConfig *)ptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setQualityReportingCollector(JNIEnv *env, jobject thiz, jlong ptr, jstring jcollector) {
+ if (jcollector){
+ const char *collector=env->GetStringUTFChars(jcollector, NULL);
+ linphone_proxy_config_set_quality_reporting_collector((LinphoneProxyConfig *)ptr, collector);
+ env->ReleaseStringUTFChars(jcollector,collector);
+ }
+}
+
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_getQualityReportingCollector(JNIEnv *env, jobject thiz, jlong ptr) {
+ jstring jvalue = env->NewStringUTF(linphone_proxy_config_get_quality_reporting_collector((LinphoneProxyConfig *)ptr));
+ return jvalue;
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setRealm(JNIEnv *env, jobject thiz, jlong ptr, jstring jrealm) {
+ if (jrealm){
+ const char *realm=env->GetStringUTFChars(jrealm, NULL);
+ linphone_proxy_config_set_realm((LinphoneProxyConfig *)ptr, realm);
+ env->ReleaseStringUTFChars(jrealm,realm);
+ }
+}
+
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_getRealm(JNIEnv *env, jobject thiz, jlong ptr) {
+ jstring jvalue = env->NewStringUTF(linphone_proxy_config_get_realm((LinphoneProxyConfig *)ptr));
+ return jvalue;
+}
+
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_isPhoneNumber(JNIEnv *env, jobject thiz, jlong ptr, jstring jusername) {
+ if(jusername){
+ const char *username=env->GetStringUTFChars(jusername, NULL);
+ bool_t res = linphone_proxy_config_is_phone_number((LinphoneProxyConfig *)ptr, username);
+ env->ReleaseStringUTFChars(jusername,username);
+ return (jboolean) res;
+ } else {
+ return JNI_FALSE;
+ }
}
extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getDuration(JNIEnv* env,jobject thiz,jlong ptr) {
return (jint)linphone_call_get_duration((LinphoneCall *) ptr);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setSipDscp(JNIEnv* env,jobject thiz,jlong ptr, jint dscp){
+ linphone_core_set_sip_dscp((LinphoneCore*)ptr,dscp);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getSipDscp(JNIEnv* env,jobject thiz,jlong ptr){
+ return linphone_core_get_sip_dscp((LinphoneCore*)ptr);
+}
+
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getSignalingTransportPort(JNIEnv* env,jobject thiz,jlong ptr, jint code) {
LCSipTransports tr;
linphone_core_get_sip_transports((LinphoneCore *) ptr, &tr);
@@ -2030,12 +3924,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setSignalingTransportPor
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableIpv6(JNIEnv* env,jobject thiz
- ,jlong lc, jboolean enable) {
- linphone_core_enable_ipv6((LinphoneCore*)lc,enable);
+ ,jlong lc, jboolean enable) {
+ linphone_core_enable_ipv6((LinphoneCore*)lc,enable);
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isIpv6Enabled(JNIEnv* env,jobject thiz, jlong lc) {
+ return (jboolean)linphone_core_ipv6_enabled((LinphoneCore*)lc);
}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_adjustSoftwareVolume(JNIEnv* env,jobject thiz
- ,jlong ptr, jint db) {
+ ,jlong ptr, jint db) {
linphone_core_set_playback_gain_db((LinphoneCore *) ptr, db);
}
@@ -2095,9 +3993,8 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateAllCalls(JNIEnv
linphone_core_terminate_all_calls((LinphoneCore *) pCore);
}
extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getCall(JNIEnv *env,jobject thiz,jlong pCore,jint position) {
- LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)pCore);
LinphoneCall* lCall = (LinphoneCall*) ms_list_nth_data(linphone_core_get_calls((LinphoneCore *) pCore),position);
- return lcd->getCall(env,lCall);
+ return getCall(env,lCall);
}
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getCallsNb(JNIEnv *env,jobject thiz,jlong pCore) {
return (jint)ms_list_size(linphone_core_get_calls((LinphoneCore *) pCore));
@@ -2113,6 +4010,10 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_transferCallToAnother(JN
return (jint)linphone_core_transfer_call_to_another((LinphoneCore *) pCore, (LinphoneCall *) pCall, (LinphoneCall *) pDestCall);
}
+extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_startReferedCall(JNIEnv *env, jobject thiz, jlong lc, jlong callptr, jlong params){
+ return getCall(env,linphone_core_start_refered_call((LinphoneCore *)lc, (LinphoneCall *)callptr, (const LinphoneCallParams *)params));
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setZrtpSecretsCache(JNIEnv *env,jobject thiz,jlong pCore, jstring jFile) {
if (jFile) {
const char* cFile=env->GetStringUTFChars(jFile, NULL);
@@ -2125,10 +4026,9 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setZrtpSecretsCache(JNIE
extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_findCallFromUri(JNIEnv *env,jobject thiz,jlong pCore, jstring jUri) {
const char* cUri=env->GetStringUTFChars(jUri, NULL);
- LinphoneCall *call= (LinphoneCall *) linphone_core_find_call_from_uri((LinphoneCore *) pCore,cUri);
+ const LinphoneCall *call=linphone_core_find_call_from_uri((const LinphoneCore *) pCore,cUri);
env->ReleaseStringUTFChars(jUri, cUri);
- LinphoneCoreData *lcdata=(LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)pCore);
- return (jobject) lcdata->getCall(env,call);
+ return (jobject) getCall(env,(LinphoneCall*)call);
}
@@ -2173,6 +4073,26 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getVideoDevice(JNIEnv *e
return 0;
}
+extern "C" jobjectArray Java_org_linphone_core_LinphoneCoreImpl_listSupportedVideoResolutions(JNIEnv *env, jobject thiz, jlong lc) {
+ const MSVideoSizeDef *pdef = linphone_core_get_supported_video_sizes((LinphoneCore *)lc);
+ int count = 0;
+ int i = 0;
+ for (; pdef->name!=NULL; pdef++) {
+ i++;
+ }
+ count = i;
+
+ jobjectArray resolutions = (jobjectArray) env->NewObjectArray(count, env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ pdef = linphone_core_get_supported_video_sizes((LinphoneCore *)lc);
+ i = 0;
+ for (; pdef->name!=NULL; pdef++) {
+ env->SetObjectArrayElement(resolutions, i, env->NewStringUTF(pdef->name));
+ i++;
+ }
+
+ return resolutions;
+}
+
extern "C" jstring Java_org_linphone_core_LinphoneCallImpl_getAuthenticationToken(JNIEnv* env,jobject thiz,jlong ptr) {
LinphoneCall *call = (LinphoneCall *) ptr;
const char* token = linphone_call_get_authentication_token(call);
@@ -2232,6 +4152,62 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelAddServerAndMirror
env->ReleaseStringUTFChars(jHost, cHost);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelAddServer(JNIEnv *env, jobject thiz, jlong pCore, jobject config) {
+ LinphoneTunnel *tunnel = linphone_core_get_tunnel((LinphoneCore *)pCore);
+ if(tunnel != NULL) {
+ jclass TunnelConfigClass = env->FindClass("org/linphone/core/TunnelConfig");
+ jmethodID getHostMethod = env->GetMethodID(TunnelConfigClass, "getHost", "()Ljava/lang/String;");
+ jmethodID getPortMethod = env->GetMethodID(TunnelConfigClass, "getPort", "()I");
+ jmethodID getRemoteUdpMirrorPortMethod = env->GetMethodID(TunnelConfigClass, "getRemoteUdpMirrorPort", "()I");
+ jmethodID getDelayMethod = env->GetMethodID(TunnelConfigClass, "getDelay", "()I");
+ jstring hostString = (jstring)env->CallObjectMethod(config, getHostMethod);
+ const char *host = env->GetStringUTFChars(hostString, NULL);
+ if(host == NULL || strlen(host)==0) {
+ ms_error("LinphoneCore.tunnelAddServer(): no tunnel host defined");
+ }
+ LinphoneTunnelConfig *tunnelConfig = linphone_tunnel_config_new();
+ linphone_tunnel_config_set_host(tunnelConfig, host);
+ linphone_tunnel_config_set_port(tunnelConfig, env->CallIntMethod(config, getPortMethod));
+ linphone_tunnel_config_set_remote_udp_mirror_port(tunnelConfig, env->CallIntMethod(config, getRemoteUdpMirrorPortMethod));
+ linphone_tunnel_config_set_delay(tunnelConfig, env->CallIntMethod(config, getDelayMethod));
+ linphone_tunnel_add_server(tunnel, tunnelConfig);
+ env->ReleaseStringUTFChars(hostString, host);
+ env->DeleteLocalRef(TunnelConfigClass);
+ } else {
+ ms_error("LinphoneCore.tunnelAddServer(): tunnel feature is not enabled");
+ }
+}
+
+extern "C" jobjectArray Java_org_linphone_core_LinphoneCoreImpl_tunnelGetServers(JNIEnv *env, jobject thiz, jlong pCore) {
+ LinphoneTunnel *tunnel = linphone_core_get_tunnel((LinphoneCore *)pCore);
+ jclass TunnelConfigClass = env->FindClass("org/linphone/core/TunnelConfig");
+ jmethodID setHostMethod = env->GetMethodID(TunnelConfigClass, "setHost", "(Ljava/lang/String;)V");
+ jmethodID setPortMethod = env->GetMethodID(TunnelConfigClass, "setPort", "(I)V");
+ jmethodID setRemoteUdpMirrorPortMethod = env->GetMethodID(TunnelConfigClass, "setRemoteUdpMirrorPort", "(I)V");
+ jmethodID setDelayMethod = env->GetMethodID(TunnelConfigClass, "setDelay", "(I)V");
+ jobjectArray tunnelConfigArray = NULL;
+
+ if(tunnel != NULL) {
+ const MSList *servers = linphone_tunnel_get_servers(tunnel);
+ const MSList *it;
+ int i;
+ ms_message("servers=%p", (void *)servers);
+ ms_message("taille=%i", ms_list_size(servers));
+ tunnelConfigArray = env->NewObjectArray(ms_list_size(servers), TunnelConfigClass, NULL);
+ for(it = servers, i=0; it != NULL; it = it->next, i++) {
+ const LinphoneTunnelConfig *conf = (const LinphoneTunnelConfig *)it->data;
+ jobject elt = env->AllocObject(TunnelConfigClass);
+ env->CallVoidMethod(elt, setHostMethod, env->NewStringUTF(linphone_tunnel_config_get_host(conf)));
+ env->CallVoidMethod(elt, setPortMethod, linphone_tunnel_config_get_port(conf));
+ env->CallVoidMethod(elt, setRemoteUdpMirrorPortMethod, linphone_tunnel_config_get_remote_udp_mirror_port(conf));
+ env->CallVoidMethod(elt, setDelayMethod, linphone_tunnel_config_get_delay(conf));
+ env->SetObjectArrayElement(tunnelConfigArray, i, elt);
+ env->DeleteLocalRef(TunnelConfigClass);
+ }
+ }
+ return tunnelConfigArray;
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelSetHttpProxy(JNIEnv *env,jobject thiz,jlong pCore,
jstring jHost, jint port, jstring username, jstring password) {
@@ -2262,6 +4238,37 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelEnable(JNIEnv *env
linphone_tunnel_enable(tunnel, enable);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelSetMode(JNIEnv *env, jobject thiz, jlong pCore, jint mode) {
+ LinphoneTunnel *tunnel = ((LinphoneCore *)pCore)->tunnel;
+ if(tunnel != NULL) {
+ linphone_tunnel_set_mode(tunnel, (LinphoneTunnelMode)mode);
+ }
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_tunnelGetMode(JNIEnv *env, jobject thiz, jlong pCore) {
+ LinphoneTunnel *tunnel = ((LinphoneCore *)pCore)->tunnel;
+ if(tunnel != NULL) {
+ return (jint)linphone_tunnel_get_mode(tunnel);
+ } else {
+ return 0;
+ }
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelEnableSip(JNIEnv *env, jobject thiz, jlong pCore, jboolean enable) {
+ LinphoneTunnel *tunnel = ((LinphoneCore *)pCore)->tunnel;
+ if(tunnel != NULL) {
+ linphone_tunnel_enable_sip(tunnel, (bool_t)enable);
+ }
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_tunnelSipEnabled(JNIEnv *env, jobject thiz, jlong pCore) {
+ LinphoneTunnel *tunnel = ((LinphoneCore *)pCore)->tunnel;
+ if(tunnel != NULL) {
+ return (jboolean)linphone_tunnel_sip_enabled(tunnel);
+ } else {
+ return JNI_FALSE;
+ }
+}
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUserAgent(JNIEnv *env,jobject thiz,jlong pCore, jstring name, jstring version){
const char* cname=env->GetStringUTFChars(name, NULL);
@@ -2282,6 +4289,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPolicy(JNIEnv *e
linphone_core_set_video_policy((LinphoneCore *)lc, &vpol);
}
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_getVideoAutoInitiatePolicy(JNIEnv *env, jobject thiz, jlong lc){
+ const LinphoneVideoPolicy *vpol = linphone_core_get_video_policy((LinphoneCore *)lc);
+ return (jboolean) vpol->automatically_initiate;
+}
+
+extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_getVideoAutoAcceptPolicy(JNIEnv *env, jobject thiz, jlong lc){
+ const LinphoneVideoPolicy *vpol = linphone_core_get_video_policy((LinphoneCore *)lc);
+ return (jboolean) vpol->automatically_accept;
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setStaticPicture(JNIEnv *env, jobject thiz, jlong lc, jstring path) {
const char *cpath = env->GetStringUTFChars(path, NULL);
linphone_core_set_static_picture((LinphoneCore *)lc, cpath);
@@ -2292,6 +4309,14 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setCpuCountNative(JNIEnv
ms_set_cpu_count(count);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAudioJittcomp(JNIEnv *env, jobject thiz, jlong lc, jint value) {
+ linphone_core_set_audio_jittcomp((LinphoneCore *)lc, value);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoJittcomp(JNIEnv *env, jobject thiz, jlong lc, jint value) {
+ linphone_core_set_video_jittcomp((LinphoneCore *)lc, value);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAudioPort(JNIEnv *env, jobject thiz, jlong lc, jint port) {
linphone_core_set_audio_port((LinphoneCore *)lc, port);
}
@@ -2308,6 +4333,70 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPortRange(JNIEnv
linphone_core_set_video_port_range((LinphoneCore *)lc, min_port, max_port);
}
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAudioDscp(JNIEnv* env,jobject thiz,jlong ptr, jint dscp){
+ linphone_core_set_audio_dscp((LinphoneCore*)ptr,dscp);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidPowerManager(JNIEnv *env, jclass cls, jobject pm) {
+#ifdef ANDROID
+ if(pm != NULL) bellesip_wake_lock_init(env, pm);
+ else bellesip_wake_lock_uninit(env);
+#endif
+}
+
+/*released in Java_org_linphone_core_LinphoneCoreImpl_delete*/
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidWifiLock(JNIEnv *env, jobject thiz, jlong ptr, jobject wifi_lock) {
+#ifdef ANDROID
+ LinphoneCore *lc=(LinphoneCore*)ptr;
+ if (lc->wifi_lock) {
+ env->DeleteGlobalRef(lc->wifi_lock);
+ env->DeleteGlobalRef(lc->wifi_lock_class);
+ }
+ if (wifi_lock != NULL) {
+ lc->wifi_lock=env->NewGlobalRef(wifi_lock);
+ lc->wifi_lock_class = env->FindClass("android/net/wifi/WifiManager$WifiLock");
+ lc->wifi_lock_class = (jclass)env->NewGlobalRef(lc->wifi_lock_class); /*to make sure methodid are preserved*/
+ lc->wifi_lock_acquire_id = env->GetMethodID(lc->wifi_lock_class, "acquire", "()V");
+ lc->wifi_lock_release_id = env->GetMethodID(lc->wifi_lock_class, "release", "()V");
+ } else {
+ lc->wifi_lock=NULL;
+ lc->wifi_lock_class=NULL;
+ }
+#endif
+}
+/*released in Java_org_linphone_core_LinphoneCoreImpl_delete*/
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidMulticastLock(JNIEnv *env, jobject thiz, jlong ptr, jobject multicast_lock) {
+#ifdef ANDROID
+ LinphoneCore *lc=(LinphoneCore*)ptr;
+ if (lc->multicast_lock) {
+ env->DeleteGlobalRef(lc->multicast_lock);
+ env->DeleteGlobalRef(lc->multicast_lock_class);
+ }
+ if (multicast_lock != NULL) {
+ lc->multicast_lock=env->NewGlobalRef(multicast_lock);
+ lc->multicast_lock_class = env->FindClass("android/net/wifi/WifiManager$MulticastLock");
+ lc->multicast_lock_class = (jclass)env->NewGlobalRef(lc->multicast_lock_class);/*to make sure methodid are preserved*/
+ lc->multicast_lock_acquire_id = env->GetMethodID(lc->multicast_lock_class, "acquire", "()V");
+ lc->multicast_lock_release_id = env->GetMethodID(lc->multicast_lock_class, "release", "()V");
+ } else {
+ lc->multicast_lock=NULL;
+ lc->multicast_lock_class=NULL;
+ }
+#endif
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getAudioDscp(JNIEnv* env,jobject thiz,jlong ptr){
+ return linphone_core_get_audio_dscp((LinphoneCore*)ptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoDscp(JNIEnv* env,jobject thiz,jlong ptr, jint dscp){
+ linphone_core_set_video_dscp((LinphoneCore*)ptr,dscp);
+}
+
+extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getVideoDscp(JNIEnv* env,jobject thiz,jlong ptr){
+ return linphone_core_get_video_dscp((LinphoneCore*)ptr);
+}
+
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setIncomingTimeout(JNIEnv *env, jobject thiz, jlong lc, jint timeout) {
linphone_core_set_inc_timeout((LinphoneCore *)lc, timeout);
}
@@ -2326,7 +4415,7 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getConfig(JNIEnv *env,
}
extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_upnpAvailable(JNIEnv *env, jobject thiz, jlong lc) {
- return (jboolean) linphone_core_upnp_available((LinphoneCore *)lc);
+ return (jboolean) linphone_core_upnp_available();
}
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getUpnpState(JNIEnv *env, jobject thiz, jlong lc) {
@@ -2338,9 +4427,81 @@ extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getUpnpExternalIpaddr
return jvalue;
}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: subscribe
+ * Signature: (JJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCoreImpl_subscribe(JNIEnv *env, jobject jcore, jlong coreptr, jlong addrptr,
+ jstring jevname, jint expires, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding){
+ LinphoneCore *lc=(LinphoneCore*)coreptr;
+ LinphoneAddress *addr=(LinphoneAddress*)addrptr;
+ LinphoneContentPrivate content={0};
+ LinphoneEvent *ev;
+ jobject jev=NULL;
+ const char *evname=env->GetStringUTFChars(jevname,NULL);
+
+ if (jtype){
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.encoding=jencoding ? (char*)env->GetStringUTFChars(jencoding,NULL) : NULL;
+ content.data=(void*)env->GetByteArrayElements(jdata,NULL);
+ content.size=env->GetArrayLength(jdata);
+ }
+ ev=linphone_core_subscribe(lc,addr,evname,expires,content.type ? LINPHONE_CONTENT(&content) : NULL);
+ if (jtype){
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding,content.encoding);
+ env->ReleaseByteArrayElements(jdata,(jbyte*)content.data,JNI_ABORT);
+ }
+ env->ReleaseStringUTFChars(jevname,evname);
+ if (ev){
+ jev=getEvent(env,ev);
+ }
+ return jev;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: publish
+ * Signature: (JJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCoreImpl_publish(JNIEnv *env, jobject jobj, jlong coreptr, jlong addrptr, jstring jevname, jint expires,
+ jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding){
+ LinphoneCore *lc=(LinphoneCore*)coreptr;
+ LinphoneAddress *addr=(LinphoneAddress*)addrptr;
+ LinphoneContentPrivate content={0};
+ LinphoneEvent *ev;
+ jobject jev=NULL;
+ const char *evname=env->GetStringUTFChars(jevname,NULL);
+
+ if (jtype){
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.encoding=jencoding ? (char*)env->GetStringUTFChars(jencoding,NULL) : NULL;
+ content.data=(void*)env->GetByteArrayElements(jdata,NULL);
+ content.size=env->GetArrayLength(jdata);
+ }
+ ev=linphone_core_publish(lc,addr,evname,expires,content.type ? LINPHONE_CONTENT(&content) : NULL);
+ if (jtype){
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding,content.encoding);
+ env->ReleaseByteArrayElements(jdata,(jbyte*)content.data,JNI_ABORT);
+ }
+ env->ReleaseStringUTFChars(jevname,evname);
+ if (ev){
+ jev=getEvent(env,ev);
+ }
+ return jev;
+}
+
+// LpConfig
extern "C" jlong Java_org_linphone_core_LpConfigImpl_newLpConfigImpl(JNIEnv *env, jobject thiz, jstring file) {
- const char *cfile = env->GetStringUTFChars(file, NULL);
- LpConfig *lp = lp_config_new(cfile);
+ const char *cfile = env->GetStringUTFChars(file, NULL);
+ LpConfig *lp = lp_config_new(cfile);
env->ReleaseStringUTFChars(file, cfile);
return (jlong) lp;
}
@@ -2357,10 +4518,1669 @@ extern "C" void Java_org_linphone_core_LpConfigImpl_delete(JNIEnv *env, jobject
extern "C" void Java_org_linphone_core_LpConfigImpl_setInt(JNIEnv *env, jobject thiz, jlong lpc,
jstring section, jstring key, jint value) {
- const char *csection = env->GetStringUTFChars(section, NULL);
- const char *ckey = env->GetStringUTFChars(key, NULL);
- lp_config_set_int((LpConfig *)lpc, csection, ckey, (int) value);
- env->ReleaseStringUTFChars(section, csection);
- env->ReleaseStringUTFChars(key, ckey);
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ lp_config_set_int((LpConfig *)lpc, csection, ckey, (int) value);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
}
+extern "C" jint Java_org_linphone_core_LpConfigImpl_getInt(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jint defaultValue) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, (int) defaultValue);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ return (jint) returnValue;
+}
+
+extern "C" void Java_org_linphone_core_LpConfigImpl_setFloat(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jfloat value) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ lp_config_set_float((LpConfig *)lpc, csection, ckey, (float) value);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+}
+
+extern "C" jfloat Java_org_linphone_core_LpConfigImpl_getFloat(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jfloat defaultValue) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ float returnValue = lp_config_get_float((LpConfig *)lpc, csection, ckey, (float) defaultValue);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ return (jfloat) returnValue;
+}
+
+extern "C" void Java_org_linphone_core_LpConfigImpl_setBool(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jboolean value) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ lp_config_set_int((LpConfig *)lpc, csection, ckey, value ? 1 : 0);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+}
+
+extern "C" jboolean Java_org_linphone_core_LpConfigImpl_getBool(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jboolean defaultValue) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ int returnValue = lp_config_get_int((LpConfig *)lpc, csection, ckey, defaultValue ? 1 : 0);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ return (jboolean) returnValue == 1;
+}
+
+extern "C" void Java_org_linphone_core_LpConfigImpl_setString(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jstring value) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ const char *cvalue = value ? env->GetStringUTFChars(value, NULL) : NULL;
+ lp_config_set_string((LpConfig *)lpc, csection, ckey, cvalue);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ if (value) env->ReleaseStringUTFChars(value, cvalue);
+}
+
+extern "C" jstring Java_org_linphone_core_LpConfigImpl_getString(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jstring defaultValue) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ const char *cvalue = defaultValue ? env->GetStringUTFChars(defaultValue, NULL) : NULL;
+
+ const char *returnValue = lp_config_get_string((LpConfig *)lpc, csection, ckey, cvalue);
+
+ jstring jreturnValue = NULL;
+ if (returnValue)
+ jreturnValue = env->NewStringUTF(returnValue);
+
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ if (cvalue)
+ env->ReleaseStringUTFChars(defaultValue, cvalue);
+
+ return jreturnValue;
+}
+extern "C" void Java_org_linphone_core_LpConfigImpl_setIntRange(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jint min, jint max) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ lp_config_set_range((LpConfig *)lpc, csection, ckey, min, max);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+}
+
+extern "C" jintArray Java_org_linphone_core_LpConfigImpl_getIntRange(JNIEnv *env, jobject thiz, jlong lpc,
+ jstring section, jstring key, jint defaultmin, jint defaultmax) {
+ const char *csection = env->GetStringUTFChars(section, NULL);
+ const char *ckey = env->GetStringUTFChars(key, NULL);
+ int *values = (int*)calloc(2, sizeof(int));
+ lp_config_get_range((LpConfig *)lpc, csection, ckey, &values[0], &values[1], defaultmin, defaultmax);
+ jintArray returnValues = env->NewIntArray(2);
+ env->SetIntArrayRegion(returnValues, 0, 2, values);
+ ms_free(values);
+ env->ReleaseStringUTFChars(section, csection);
+ env->ReleaseStringUTFChars(key, ckey);
+ return returnValues;
+}
+
+static jobject create_java_linphone_content(JNIEnv *env, const LinphoneContent *icontent){
+ jclass contentClass;
+ jmethodID ctor;
+ jstring jtype, jsubtype, jencoding, jname;
+ jbyteArray jdata = NULL;
+ jint jsize = 0;
+ const LinphoneContentPrivate *content = LINPHONE_CONTENT_PRIVATE(icontent);
+
+ contentClass = (jclass)env->FindClass("org/linphone/core/LinphoneContentImpl");
+ ctor = env->GetMethodID(contentClass,"", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BLjava/lang/String;I)V");
+
+ jtype = env->NewStringUTF(content->type);
+ jsubtype = env->NewStringUTF(content->subtype);
+ jencoding = content->encoding ? env->NewStringUTF(content->encoding) : NULL;
+ jname = content->name ? env->NewStringUTF(content->name) : NULL;
+ jsize = (jint) content->size;
+
+ if (content->data){
+ jdata = env->NewByteArray(content->size);
+ env->SetByteArrayRegion(jdata, 0, content->size, (jbyte*)content->data);
+ }
+
+ jobject jobj = env->NewObject(contentClass, ctor, jname, jtype, jsubtype, jdata, jencoding, jsize);
+
+ env->DeleteLocalRef(contentClass);
+ env->DeleteLocalRef(jtype);
+ env->DeleteLocalRef(jsubtype);
+ if (jencoding) {
+ env->DeleteLocalRef(jencoding);
+ }
+ if (jname) {
+ env->DeleteLocalRef(jname);
+ }
+
+ return jobj;
+}
+
+static jobject create_java_linphone_buffer(JNIEnv *env, const LinphoneBuffer *buffer) {
+ jclass bufferClass;
+ jmethodID ctor;
+ jbyteArray jdata = NULL;
+ jint jsize = 0;
+
+ bufferClass = (jclass)env->FindClass("org/linphone/core/LinphoneBufferImpl");
+ ctor = env->GetMethodID(bufferClass,"", "([BI)V");
+ jsize = buffer ? (jint) buffer->size : 0;
+
+ if (buffer && buffer->content) {
+ jdata = env->NewByteArray(buffer->size);
+ env->SetByteArrayRegion(jdata, 0, buffer->size, (jbyte*)buffer->content);
+ }
+
+ jobject jobj = env->NewObject(bufferClass, ctor, jdata, jsize);
+ env->DeleteLocalRef(bufferClass);
+ return jobj;
+}
+
+static LinphoneBuffer* create_c_linphone_buffer_from_java_linphone_buffer(JNIEnv *env, jobject jbuffer) {
+ jclass bufferClass;
+ jmethodID getSizeMethod, getDataMethod;
+ LinphoneBuffer *buffer = NULL;
+ jint jsize;
+ jobject jdata;
+ jbyteArray jcontent;
+ uint8_t *content;
+
+ bufferClass = (jclass)env->FindClass("org/linphone/core/LinphoneBufferImpl");
+ getSizeMethod = env->GetMethodID(bufferClass, "getSize", "()I");
+ getDataMethod = env->GetMethodID(bufferClass, "getContent", "()[B");
+
+ jsize = env->CallIntMethod(jbuffer, getSizeMethod);
+ jdata = env->CallObjectMethod(jbuffer, getDataMethod);
+ jcontent = reinterpret_cast(jdata);
+ if (jcontent != NULL) {
+ content = (uint8_t*)env->GetByteArrayElements(jcontent, NULL);
+ buffer = linphone_buffer_new_from_data(content, (size_t)jsize);
+ env->ReleaseByteArrayElements(jcontent, (jbyte*)content, JNI_ABORT);
+ }
+ env->DeleteLocalRef(bufferClass);
+
+ return buffer;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneInfoMessageImpl
+ * Method: getContent
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_getContent(JNIEnv *env, jobject jobj, jlong infoptr){
+ const LinphoneContent *content=linphone_info_message_get_content((LinphoneInfoMessage*)infoptr);
+ if (content){
+ return create_java_linphone_content(env,content);
+ }
+ return NULL;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneInfoMessageImpl
+ * Method: setContent
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_setContent(JNIEnv *env, jobject jobj, jlong infoptr, jstring jtype, jstring jsubtype, jstring jdata){
+ LinphoneContentPrivate content={0};
+
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.data=(void*)env->GetStringUTFChars(jdata,NULL);
+ content.size=strlen((char*)content.data);
+ linphone_info_message_set_content((LinphoneInfoMessage*)infoptr,LINPHONE_CONTENT(&content));
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ env->ReleaseStringUTFChars(jdata,(char*)content.data);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneInfoMessageImpl
+ * Method: addHeader
+ * Signature: (JLjava/lang/String;Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_addHeader(JNIEnv *env, jobject jobj, jlong infoptr, jstring jname, jstring jvalue){
+ const char *name=NULL,*value=NULL;
+ name=env->GetStringUTFChars(jname,NULL);
+ value=env->GetStringUTFChars(jvalue,NULL);
+ linphone_info_message_add_header((LinphoneInfoMessage*)infoptr,name,value);
+ env->ReleaseStringUTFChars(jname,name);
+ env->ReleaseStringUTFChars(jvalue,value);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneInfoMessageImpl
+ * Method: getHeader
+ * Signature: (JLjava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_getHeader(JNIEnv *env, jobject jobj, jlong infoptr, jstring jname){
+ const char *name=env->GetStringUTFChars(jname,NULL);
+ const char *ret=linphone_info_message_get_header((LinphoneInfoMessage*)infoptr,name);
+ env->ReleaseStringUTFChars(jname,name);
+ return ret ? env->NewStringUTF(ret) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneInfoMessageImpl
+ * Method: delete
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_delete(JNIEnv *env, jobject jobj , jlong infoptr){
+ linphone_info_message_destroy((LinphoneInfoMessage*)infoptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreFactoryImpl__1setLogHandler(JNIEnv *env, jobject jfactory, jobject jhandler){
+ static int init_done=FALSE;
+
+ if (!init_done){
+ handler_class=(jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneLogHandler"));
+ loghandler_id=env->GetMethodID(handler_class,"log", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V");
+ if (loghandler_id==NULL) ms_fatal("log method not found");
+ init_done=TRUE;
+ }
+ if (handler_obj) {
+ env->DeleteGlobalRef(handler_obj);
+ handler_obj=NULL;
+ }
+ if (jhandler){
+ handler_obj=env->NewGlobalRef(jhandler);
+ }
+}
+
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneEventImpl_getCore(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneCore *lc=linphone_event_get_core((LinphoneEvent*)evptr);
+ jobject core = (jobject)linphone_core_get_user_data(lc);
+ return core;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: getEventName
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneEventImpl_getEventName(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ const char *evname=linphone_event_get_name(ev);
+ return evname ? env->NewStringUTF(evname) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: acceptSubscription
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_acceptSubscription(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return linphone_event_accept_subscription(ev);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: denySubscription
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_denySubscription(JNIEnv *env, jobject jobj, jlong evptr, int reason){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return linphone_event_deny_subscription(ev,(LinphoneReason)reason);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: notify
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_notify(JNIEnv *env, jobject jobj, jlong evptr, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding){
+ LinphoneContentPrivate content={0};
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ jint err;
+
+ if (jtype){
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.encoding=jencoding ? (char*)env->GetStringUTFChars(jsubtype,NULL) : NULL;
+ content.data=(void*)env->GetByteArrayElements(jdata,NULL);
+ content.size=env->GetArrayLength(jdata);
+ }
+
+ err=linphone_event_notify(ev,content.type ? LINPHONE_CONTENT(&content) : NULL);
+
+ if (jtype){
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding,content.encoding);
+ env->ReleaseByteArrayElements(jdata,(jbyte*)content.data,JNI_ABORT);
+ }
+ return err;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: updateSubscribe
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_updateSubscribe(JNIEnv *env, jobject jobj, jlong evptr, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding){
+ LinphoneContentPrivate content={0};
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ jint err;
+
+ if (jtype){
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.encoding=jencoding ? (char*)env->GetStringUTFChars(jsubtype,NULL) : NULL;
+ content.data=(void*)env->GetByteArrayElements(jdata,NULL);
+ content.size=env->GetArrayLength(jdata);
+ }
+
+ err=linphone_event_update_subscribe(ev,content.type ? LINPHONE_CONTENT(&content) : NULL);
+
+ if (jtype){
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding,content.encoding);
+ env->ReleaseByteArrayElements(jdata,(jbyte*)content.data,JNI_ABORT);
+ }
+ return err;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: updatePublish
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_updatePublish(JNIEnv *env, jobject jobj, jlong evptr, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding){
+ LinphoneContentPrivate content={0};
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ jint err;
+
+ if (jtype){
+ content.type=(char*)env->GetStringUTFChars(jtype,NULL);
+ content.subtype=(char*)env->GetStringUTFChars(jsubtype,NULL);
+ content.encoding=jencoding ? (char*)env->GetStringUTFChars(jsubtype,NULL) : NULL;
+ content.data=(void*)env->GetByteArrayElements(jdata,NULL);
+ content.size=env->GetArrayLength(jdata);
+ }
+
+ err=linphone_event_update_publish(ev,content.type ? LINPHONE_CONTENT(&content) : NULL);
+
+ if (jtype){
+ env->ReleaseStringUTFChars(jtype,content.type);
+ env->ReleaseStringUTFChars(jsubtype,content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding,content.encoding);
+ env->ReleaseByteArrayElements(jdata,(jbyte*)content.data,JNI_ABORT);
+ }
+ return err;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: terminate
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_terminate(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ linphone_event_terminate(ev);
+ return 0;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: getReason
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_getReason(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return linphone_event_get_reason(ev);
+}
+
+JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneEventImpl_getErrorInfo(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return (jlong)linphone_event_get_error_info(ev);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: getSubscriptionDir
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_getSubscriptionDir(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return linphone_event_get_subscription_dir(ev);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: getSubscriptionState
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneEventImpl_getSubscriptionState(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ return linphone_event_get_subscription_state(ev);
+}
+
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCoreImpl_createSubscribe(JNIEnv *env, jobject thiz, jlong jcore, jlong jaddr, jstring jeventname, jint expires) {
+ LinphoneCore *lc = (LinphoneCore*) jcore;
+ LinphoneAddress *addr = (LinphoneAddress*) jaddr;
+ LinphoneEvent *event;
+ jobject jevent = NULL;
+ const char *event_name = env->GetStringUTFChars(jeventname, NULL);
+
+ event = linphone_core_create_subscribe(lc, addr, event_name, expires);
+ env->ReleaseStringUTFChars(jeventname, event_name);
+ if (event) {
+ jevent = getEvent(env, event);
+ }
+ return jevent;
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneEventImpl_sendSubscribe(JNIEnv *env, jobject thiz, jlong jevent, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding) {
+ LinphoneContentPrivate content = {0};
+ if (jtype) {
+ content.type = (char*) env->GetStringUTFChars(jtype, NULL);
+ content.subtype = (char*) env->GetStringUTFChars(jsubtype, NULL);
+ content.encoding = jencoding ? (char*) env->GetStringUTFChars(jencoding, NULL) : NULL;
+ content.data = (void*) env->GetByteArrayElements(jdata, NULL);
+ content.size = env->GetArrayLength(jdata);
+ }
+ linphone_event_send_subscribe((LinphoneEvent*) jevent, content.type ? LINPHONE_CONTENT(&content) : NULL);
+ if (jtype) {
+ env->ReleaseStringUTFChars(jtype, content.type);
+ env->ReleaseStringUTFChars(jsubtype, content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding, content.encoding);
+ env->ReleaseByteArrayElements(jdata, (jbyte*) content.data, JNI_ABORT);
+ }
+}
+
+JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneCoreImpl_createPublish(JNIEnv *env, jobject thiz, jlong jcore, jlong jaddr, jstring jeventname, jint expires) {
+ LinphoneCore *lc = (LinphoneCore*) jcore;
+ LinphoneAddress *addr = (LinphoneAddress*) jaddr;
+ LinphoneEvent *event;
+ jobject jevent = NULL;
+ const char *event_name = env->GetStringUTFChars(jeventname, NULL);
+
+ event = linphone_core_create_publish(lc, addr, event_name, expires);
+ env->ReleaseStringUTFChars(jeventname, event_name);
+ if (event) {
+ jevent = getEvent(env, event);
+ }
+ return jevent;
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneEventImpl_sendPublish(JNIEnv *env, jobject thiz, jlong jevent, jstring jtype, jstring jsubtype, jbyteArray jdata, jstring jencoding) {
+ LinphoneContentPrivate content = {0};
+ if (jtype) {
+ content.type = (char*) env->GetStringUTFChars(jtype, NULL);
+ content.subtype = (char*) env->GetStringUTFChars(jsubtype, NULL);
+ content.encoding = jencoding ? (char*) env->GetStringUTFChars(jencoding, NULL) : NULL;
+ content.data = (void*) env->GetByteArrayElements(jdata, NULL);
+ content.size = env->GetArrayLength(jdata);
+ }
+ linphone_event_send_publish((LinphoneEvent*) jevent, content.type ? LINPHONE_CONTENT(&content) : NULL);
+ if (jtype) {
+ env->ReleaseStringUTFChars(jtype, content.type);
+ env->ReleaseStringUTFChars(jsubtype, content.subtype);
+ if (jencoding) env->ReleaseStringUTFChars(jencoding, content.encoding);
+ env->ReleaseByteArrayElements(jdata, (jbyte*) content.data, JNI_ABORT);
+ }
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneEventImpl_addCustomHeader(JNIEnv *env, jobject thiz, jlong jevent, jstring jname, jstring jvalue) {
+ const char *name = jname ? env->GetStringUTFChars(jname, NULL) : NULL;
+ const char *value = jvalue ? env->GetStringUTFChars(jvalue, NULL) : NULL;
+ linphone_event_add_custom_header((LinphoneEvent*) jevent, name, value);
+ if (jname) env->ReleaseStringUTFChars(jname, name);
+ if (jvalue) env->ReleaseStringUTFChars(jvalue, value);
+}
+
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneEventImpl_getCustomHeader(JNIEnv *env, jobject thiz, jlong jevent, jstring jname) {
+ const char *name = jname ? env->GetStringUTFChars(jname, NULL) : NULL;
+ const char *header = linphone_event_get_custom_header((LinphoneEvent*) jevent, name);
+ jstring jheader = header ? env->NewStringUTF(header) : NULL;
+ if (jname) env->ReleaseStringUTFChars(jname, name);
+ return jheader;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneEventImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneEventImpl_unref(JNIEnv *env, jobject jobj, jlong evptr){
+ LinphoneEvent *ev=(LinphoneEvent*)evptr;
+ linphone_event_unref(ev);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: newPresenceModelImpl
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_newPresenceModelImpl__(JNIEnv *env, jobject jobj) {
+ LinphonePresenceModel *model = linphone_presence_model_new();
+ model = linphone_presence_model_ref(model);
+ return (jlong)model;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: newPresenceModelImpl
+ * Signature: (ILjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_newPresenceModelImpl__ILjava_lang_String_2(JNIEnv *env, jobject jobj, jint type, jstring description) {
+ LinphonePresenceModel *model;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ model = linphone_presence_model_new_with_activity((LinphonePresenceActivityType)type, cdescription);
+ model = linphone_presence_model_ref(model);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ return (jlong)model;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: newPresenceModelImpl
+ * Signature: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_newPresenceModelImpl__ILjava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2(
+ JNIEnv *env, jobject jobj, jint type, jstring description, jstring note, jstring lang) {
+ LinphonePresenceModel *model;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ const char *cnote = note ? env->GetStringUTFChars(note, NULL) : NULL;
+ const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL;
+ model = linphone_presence_model_new_with_activity_and_note((LinphonePresenceActivityType)type, cdescription, cnote, clang);
+ model = linphone_presence_model_ref(model);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ if (cnote) env->ReleaseStringUTFChars(note, cnote);
+ if (clang) env->ReleaseStringUTFChars(lang, clang);
+ return (jlong)model;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresenceModelImpl_unref(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ linphone_presence_model_unref(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getBasicStatus
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_getBasicStatus(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_get_basic_status(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: setBasicStatus
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_setBasicStatus(JNIEnv *env, jobject jobj, jlong ptr, jint basic_status) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_set_basic_status(model, (LinphonePresenceBasicStatus)basic_status);
+}
+
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getTimestamp
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_getTimestamp(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jlong)linphone_presence_model_get_timestamp(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getContact
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceModelImpl_getContact(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ char *ccontact = linphone_presence_model_get_contact(model);
+ jstring jcontact = ccontact ? env->NewStringUTF(ccontact) : NULL;
+ if (ccontact) ms_free(ccontact);
+ return jcontact;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: setContact
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresenceModelImpl_setContact(JNIEnv *env, jobject jobj, jlong ptr, jstring contact) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ const char *ccontact = contact ? env->GetStringUTFChars(contact, NULL) : NULL;
+ linphone_presence_model_set_contact(model, ccontact);
+ if (ccontact) env->ReleaseStringUTFChars(contact, ccontact);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getActivity
+ * Signature: (J)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceModelImpl_getActivity(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ LinphonePresenceActivity *activity = linphone_presence_model_get_activity(model);
+ if (activity == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceActivityImpl", linphone_presence_activity, activity)
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: setActivity
+ * Signature: (JILjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_setActivity(JNIEnv *env, jobject jobj, jlong ptr, jint acttype, jstring description) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ jint res = (jint)linphone_presence_model_set_activity(model, (LinphonePresenceActivityType)acttype, cdescription);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ return res;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNbActivities
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_getNbActivities(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jlong)linphone_presence_model_get_nb_activities(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNthActivity
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceModelImpl_getNthActivity(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ LinphonePresenceActivity *activity = linphone_presence_model_get_nth_activity(model, (unsigned int)idx);
+ if (activity == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceActivityImpl", linphone_presence_activity, activity)
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: addActivity
+ * Signature: (JILjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_addActivity(JNIEnv *env, jobject jobj, jlong ptr, jlong activityPtr) {
+ return (jint)linphone_presence_model_add_activity((LinphonePresenceModel *)ptr, (LinphonePresenceActivity *)activityPtr);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: clearActivities
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_clearActivities(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_clear_activities(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNote
+ * Signature: (JLjava/lang/String;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceModelImpl_getNote(JNIEnv *env , jobject jobj, jlong ptr, jstring lang) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL;
+ LinphonePresenceNote *note = linphone_presence_model_get_note(model, clang);
+ if (clang) env->ReleaseStringUTFChars(lang, clang);
+ if (note == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceNoteImpl", linphone_presence_note, note)
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: addNote
+ * Signature: (JLjava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_addNote(JNIEnv *env, jobject jobj, jlong ptr, jstring description, jstring lang) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL;
+ jint res = (jint)linphone_presence_model_add_note(model, cdescription, clang);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ if (clang) env->ReleaseStringUTFChars(lang, clang);
+ return res;
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: clearNotes
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_clearNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_clear_notes(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNbServices
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_getNbServices(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jlong)linphone_presence_model_get_nb_services(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNthService
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceModelImpl_getNthService(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ LinphonePresenceService *service = linphone_presence_model_get_nth_service(model, (unsigned int)idx);
+ if (service == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceServiceImpl", linphone_presence_service, service)
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: addService
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_addService(JNIEnv *env, jobject jobj, jlong ptr, jlong servicePtr) {
+ return (jint)linphone_presence_model_add_service((LinphonePresenceModel *)ptr, (LinphonePresenceService *)servicePtr);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: clearServices
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_clearServices(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_clear_services(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNbPersons
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceModelImpl_getNbPersons(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jlong)linphone_presence_model_get_nb_persons(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: getNthPerson
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceModelImpl_getNthPerson(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ LinphonePresencePerson *person = linphone_presence_model_get_nth_person(model, (unsigned int)idx);
+ if (person == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresencePersonImpl", linphone_presence_person, person)
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: addPerson
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_addPerson(JNIEnv *env, jobject jobj, jlong ptr, jlong personPtr) {
+ return (jint)linphone_presence_model_add_person((LinphonePresenceModel *)ptr, (LinphonePresencePerson *)personPtr);
+}
+
+/*
+ * Class: org_linphone_core_PresenceModelImpl
+ * Method: clearPersons
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceModelImpl_clearPersons(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceModel *model = (LinphonePresenceModel *)ptr;
+ return (jint)linphone_presence_model_clear_persons(model);
+}
+
+/*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: newPresenceActivityImpl
+ * Signature: (ILjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceActivityImpl_newPresenceActivityImpl(JNIEnv *env, jobject jobj, jint type, jstring description) {
+ LinphonePresenceActivity *activity;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ activity = linphone_presence_activity_new((LinphonePresenceActivityType)type, cdescription);
+ activity = linphone_presence_activity_ref(activity);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ return (jlong)activity;
+}
+
+ /*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresenceActivityImpl_unref(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ linphone_presence_activity_unref(activity);
+}
+
+ /*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: toString
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceActivityImpl_toString(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ char *cactstr = linphone_presence_activity_to_string(activity);
+ jstring jactstr = cactstr ? env->NewStringUTF(cactstr) : NULL;
+ if (cactstr) ms_free(cactstr);
+ return jactstr;
+}
+
+/*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: getType
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceActivityImpl_getType(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ return (jint)linphone_presence_activity_get_type(activity);
+}
+
+/*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: setType
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceActivityImpl_setType(JNIEnv *env, jobject jobj, jlong ptr, jint type) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ return (jint)linphone_presence_activity_set_type(activity, (LinphonePresenceActivityType)type);
+}
+
+/*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: getDescription
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceActivityImpl_getDescription(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ const char *cdescription = linphone_presence_activity_get_description(activity);
+ return cdescription ? env->NewStringUTF(cdescription) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_PresenceActivityImpl
+ * Method: setDescription
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceActivityImpl_setDescription(JNIEnv *env, jobject jobj, jlong ptr, jstring description) {
+ LinphonePresenceActivity *activity = (LinphonePresenceActivity *)ptr;
+ const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL;
+ linphone_presence_activity_set_description(activity, cdescription);
+ if (cdescription) env->ReleaseStringUTFChars(description, cdescription);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: newPresenceServiceImpl
+ * Signature: (Ljava/lang/String;ILjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceServiceImpl_newPresenceServiceImpl(JNIEnv *env, jobject jobj, jstring id, jint basic_status, jstring contact) {
+ LinphonePresenceService *service;
+ const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL;
+ const char *ccontact = contact ? env->GetStringUTFChars(contact, NULL) : NULL;
+ service = linphone_presence_service_new(cid, (LinphonePresenceBasicStatus)basic_status, ccontact);
+ service = linphone_presence_service_ref(service);
+ if (cid) env->ReleaseStringUTFChars(id, cid);
+ if (ccontact) env->ReleaseStringUTFChars(contact, ccontact);
+ return (jlong)service;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresenceServiceImpl_unref(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ linphone_presence_service_unref(service);
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: getId
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceServiceImpl_getId(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ char *cid = linphone_presence_service_get_id(service);
+ jstring jid = cid ? env->NewStringUTF(cid) : NULL;
+ if (cid) ms_free(cid);
+ return jid;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: setId
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_setId(JNIEnv *env, jobject jobj, jlong ptr, jstring id) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL;
+ linphone_presence_service_set_id(service, cid);
+ if (cid) env->ReleaseStringUTFChars(id, cid);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: getBasicStatus
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_getBasicStatus(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ return (jint)linphone_presence_service_get_basic_status(service);
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: setBasicStatus
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_setBasicStatus(JNIEnv *env, jobject jobj, jlong ptr, jint basic_status) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ return (jint)linphone_presence_service_set_basic_status(service, (LinphonePresenceBasicStatus)basic_status);
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: getContact
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceServiceImpl_getContact(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ char *ccontact = linphone_presence_service_get_contact(service);
+ jstring jcontact = ccontact ? env->NewStringUTF(ccontact) : NULL;
+ if (ccontact) ms_free(ccontact);
+ return jcontact;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: setContact
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_setContact(JNIEnv *env, jobject jobj, jlong ptr, jstring contact) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ const char *ccontact = contact ? env->GetStringUTFChars(contact, NULL) : NULL;
+ linphone_presence_service_set_contact(service, ccontact);
+ if (ccontact) env->ReleaseStringUTFChars(contact, ccontact);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: getNbNotes
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceServiceImpl_getNbNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ return (jlong)linphone_presence_service_get_nb_notes(service);
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: getNthNote
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresenceServiceImpl_getNthNote(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ LinphonePresenceNote *note = linphone_presence_service_get_nth_note(service, (unsigned int)idx);
+ if (note == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceNoteImpl", linphone_presence_note, note)
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: addNote
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_addNote(JNIEnv *env, jobject jobj, jlong ptr, jlong notePtr) {
+ return (jint)linphone_presence_service_add_note((LinphonePresenceService *)ptr, (LinphonePresenceNote *)notePtr);
+}
+
+/*
+ * Class: org_linphone_core_PresenceServiceImpl
+ * Method: clearNotes
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_clearNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceService *service = (LinphonePresenceService *)ptr;
+ return (jint)linphone_presence_service_clear_notes(service);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: newPresencePersonImpl
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresencePersonImpl_newPresencePersonImpl(JNIEnv *env, jobject jobj, jstring id) {
+ LinphonePresencePerson *person;
+ const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL;
+ person = linphone_presence_person_new(cid);
+ person = linphone_presence_person_ref(person);
+ if (cid) env->ReleaseStringUTFChars(id, cid);
+ return (jlong)person;
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresencePersonImpl_unref(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ linphone_presence_person_unref(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getId
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresencePersonImpl_getId(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ char *cid = linphone_presence_person_get_id(person);
+ jstring jid = cid ? env->NewStringUTF(cid) : NULL;
+ if (cid) ms_free(cid);
+ return jid;
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: setId
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_setId(JNIEnv *env, jobject jobj, jlong ptr, jstring id) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL;
+ linphone_presence_person_set_id(person, cid);
+ if (cid) env->ReleaseStringUTFChars(id, cid);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNbActivities
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresencePersonImpl_getNbActivities(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jlong)linphone_presence_person_get_nb_activities(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNthActivity
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresencePersonImpl_getNthActivity(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ LinphonePresenceActivity *activity = linphone_presence_person_get_nth_activity(person, (unsigned int)idx);
+ if (activity == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceActivityImpl", linphone_presence_activity, activity)
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: addActivity
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_addActivity(JNIEnv *env, jobject jobj, jlong ptr, jlong activityPtr) {
+ return (jint)linphone_presence_person_add_activity((LinphonePresencePerson *)ptr, (LinphonePresenceActivity *)activityPtr);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: clearActivities
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_clearActivities(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jint)linphone_presence_person_clear_activities(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNbNotes
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresencePersonImpl_getNbNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jlong)linphone_presence_person_get_nb_notes(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNthNote
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresencePersonImpl_getNthNote(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ LinphonePresenceNote *note = linphone_presence_person_get_nth_note(person, (unsigned int)idx);
+ if (note == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceNoteImpl", linphone_presence_note, note)
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: addNote
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_addNote(JNIEnv *env, jobject jobj, jlong ptr, jlong notePtr) {
+ return (jint)linphone_presence_person_add_note((LinphonePresencePerson *)ptr, (LinphonePresenceNote *)notePtr);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: clearNotes
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_clearNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jint)linphone_presence_person_clear_notes(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNbActivitiesNotes
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresencePersonImpl_getNbActivitiesNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jlong)linphone_presence_person_get_nb_activities_notes(person);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: getNthActivitiesNote
+ * Signature: (JJ)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_org_linphone_core_PresencePersonImpl_getNthActivitiesNote(JNIEnv *env, jobject jobj, jlong ptr, jlong idx) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ LinphonePresenceNote *note = linphone_presence_person_get_nth_activities_note(person, (unsigned int)idx);
+ if (note == NULL) return NULL;
+ RETURN_USER_DATA_OBJECT("PresenceNoteImpl", linphone_presence_note, note)
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: addActivitiesNote
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_addActivitiesNote(JNIEnv *env, jobject jobj, jlong ptr, jlong notePtr) {
+ return (jint)linphone_presence_person_add_activities_note((LinphonePresencePerson *)ptr, (LinphonePresenceNote *)notePtr);
+}
+
+/*
+ * Class: org_linphone_core_PresencePersonImpl
+ * Method: clearActivitesNotes
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_clearActivitesNotes(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresencePerson *person = (LinphonePresencePerson *)ptr;
+ return (jint)linphone_presence_person_clear_activities_notes(person);
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: newPresenceNoteImpl
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_linphone_core_PresenceNoteImpl_newPresenceNoteImpl(JNIEnv *env, jobject jobj, jstring content, jstring lang) {
+ LinphonePresenceNote *note;
+ const char *ccontent = content ? env->GetStringUTFChars(content, NULL) : NULL;
+ const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL;
+ note = linphone_presence_note_new(ccontent, clang);
+ note = linphone_presence_note_ref(note);
+ if (clang) env->ReleaseStringUTFChars(lang, clang);
+ if (ccontent) env->ReleaseStringUTFChars(content, ccontent);
+ return (jlong)note;
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: unref
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PresenceNoteImpl_unref(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceNote *note = (LinphonePresenceNote *)ptr;
+ linphone_presence_note_unref(note);
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: getContent
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceNoteImpl_getContent(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceNote *note = (LinphonePresenceNote *)ptr;
+ const char *ccontent = linphone_presence_note_get_content(note);
+ return ccontent ? env->NewStringUTF(ccontent) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: setContent
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceNoteImpl_setContent(JNIEnv *env, jobject jobj, jlong ptr, jstring content) {
+ LinphonePresenceNote *note = (LinphonePresenceNote *)ptr;
+ const char *ccontent = content ? env->GetStringUTFChars(content, NULL) : NULL;
+ linphone_presence_note_set_content(note, ccontent);
+ if (ccontent) env->ReleaseStringUTFChars(content, ccontent);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: getLang
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PresenceNoteImpl_getLang(JNIEnv *env, jobject jobj, jlong ptr) {
+ LinphonePresenceNote *note = (LinphonePresenceNote *)ptr;
+ const char *clang = linphone_presence_note_get_lang(note);
+ return clang ? env->NewStringUTF(clang) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_PresenceNoteImpl
+ * Method: setLang
+ * Signature: (JLjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceNoteImpl_setLang(JNIEnv *env, jobject jobj, jlong ptr, jstring lang) {
+ LinphonePresenceNote *note = (LinphonePresenceNote *)ptr;
+ const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL;
+ linphone_presence_note_set_lang(note, clang);
+ if (clang) env->ReleaseStringUTFChars(lang, clang);
+ return (jint)0;
+}
+
+/*
+ * Class: org_linphone_core_PayloadTypeImpl
+ * Method: setRecvFmtp
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PayloadTypeImpl_setRecvFmtp(JNIEnv *env, jobject jobj, jlong ptr, jstring jfmtp){
+ PayloadType *pt=(PayloadType *)ptr;
+ const char *fmtp=jfmtp ? env->GetStringUTFChars(jfmtp,NULL) : NULL;
+ payload_type_set_recv_fmtp(pt,fmtp);
+ if (fmtp) env->ReleaseStringUTFChars(jfmtp,fmtp);
+}
+
+/*
+ * Class: org_linphone_core_PayloadTypeImpl
+ * Method: getRecvFmtp
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PayloadTypeImpl_getRecvFmtp(JNIEnv *env, jobject jobj, jlong ptr){
+ PayloadType *pt=(PayloadType *)ptr;
+ const char *fmtp=pt->recv_fmtp;
+ return fmtp ? env->NewStringUTF(fmtp) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_PayloadTypeImpl
+ * Method: setSendFmtp
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_linphone_core_PayloadTypeImpl_setSendFmtp(JNIEnv *env, jobject jobj, jlong ptr , jstring jfmtp){
+ PayloadType *pt=(PayloadType *)ptr;
+ const char *fmtp=jfmtp ? env->GetStringUTFChars(jfmtp,NULL) : NULL;
+ payload_type_set_send_fmtp(pt,fmtp);
+ if (fmtp) env->ReleaseStringUTFChars(jfmtp,fmtp);
+}
+
+/*
+ * Class: org_linphone_core_PayloadTypeImpl
+ * Method: getSendFmtp
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_PayloadTypeImpl_getSendFmtp(JNIEnv *env, jobject jobj, jlong ptr){
+ PayloadType *pt=(PayloadType *)ptr;
+ const char *fmtp=pt->send_fmtp;
+ return fmtp ? env->NewStringUTF(fmtp) : NULL;
+}
+
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableSdp200Ack(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc
+ ,jboolean enable) {
+ linphone_core_enable_sdp_200_ack((LinphoneCore*)lc,enable);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_isSdp200AckEnabled(JNIEnv* env
+ ,jobject thiz
+ ,jlong lc) {
+ return (jboolean)linphone_core_sdp_200_ack_enabled((const LinphoneCore*)lc);
+}
+
+/* Header for class org_linphone_core_ErrorInfoImpl */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_linphone_core_ErrorInfoImpl
+ * Method: getReason
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_ErrorInfoImpl_getReason(JNIEnv *env, jobject jobj, jlong ei){
+ return linphone_error_info_get_reason((const LinphoneErrorInfo*)ei);
+}
+
+/*
+ * Class: org_linphone_core_ErrorInfoImpl
+ * Method: getProtocolCode
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_linphone_core_ErrorInfoImpl_getProtocolCode(JNIEnv *env, jobject jobj, jlong ei){
+ return linphone_error_info_get_protocol_code((const LinphoneErrorInfo*)ei);
+}
+
+/*
+ * Class: org_linphone_core_ErrorInfoImpl
+ * Method: getPhrase
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_ErrorInfoImpl_getPhrase(JNIEnv *env, jobject jobj, jlong ei){
+ const char *tmp=linphone_error_info_get_phrase((const LinphoneErrorInfo*)ei);
+ return tmp ? env->NewStringUTF(tmp) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_ErrorInfoImpl
+ * Method: getDetails
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_linphone_core_ErrorInfoImpl_getDetails(JNIEnv *env, jobject jobj, jlong ei){
+ const char *tmp=linphone_error_info_get_details((const LinphoneErrorInfo*)ei);
+ return tmp ? env->NewStringUTF(tmp) : NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Linphone Player */
+struct LinphonePlayerData {
+ LinphonePlayerData(JNIEnv *env, jobject listener, jobject jLinphonePlayer) :
+ mListener(env->NewGlobalRef(listener)),
+ mJLinphonePlayer(env->NewGlobalRef(jLinphonePlayer))
+ {
+ mListenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass(listener));
+ mEndOfFileMethodID = env->GetMethodID(mListenerClass, "endOfFile", "(Lorg/linphone/core/LinphonePlayer;)V");
+ if(mEndOfFileMethodID == NULL) {
+ ms_error("Could not get endOfFile method ID");
+ env->ExceptionClear();
+ }
+ }
+
+ ~LinphonePlayerData() {
+ JNIEnv *env;
+ jvm->AttachCurrentThread(&env, NULL);
+ env->DeleteGlobalRef(mListener);
+ env->DeleteGlobalRef(mListenerClass);
+ env->DeleteGlobalRef(mJLinphonePlayer);
+ }
+
+ jobject mListener;
+ jclass mListenerClass;
+ jobject mJLinphonePlayer;
+ jmethodID mEndOfFileMethodID;
+};
+
+static void _eof_callback(LinphonePlayer *player, void *user_data) {
+ JNIEnv *env;
+ LinphonePlayerData *player_data = (LinphonePlayerData *)user_data;
+ jvm->AttachCurrentThread(&env, NULL);
+ env->CallVoidMethod(player_data->mListener, player_data->mEndOfFileMethodID, player_data->mJLinphonePlayer);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_open(JNIEnv *env, jobject jPlayer, jlong ptr, jstring filename, jobject listener) {
+ LinphonePlayerData *data = NULL;
+ LinphonePlayerEofCallback cb = NULL;
+ if(listener) {
+ data = new LinphonePlayerData(env, listener, jPlayer);
+ cb = _eof_callback;
+ }
+ if(linphone_player_open((LinphonePlayer *)ptr, env->GetStringUTFChars(filename, NULL), cb, data) == -1) {
+ if(data) delete data;
+ return -1;
+ }
+ return 0;
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_start(JNIEnv *env, jobject jobj, jlong ptr) {
+ return (jint)linphone_player_start((LinphonePlayer *)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_pause(JNIEnv *env, jobject jobj, jlong ptr) {
+ return (jint)linphone_player_pause((LinphonePlayer *)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_seek(JNIEnv *env, jobject jobj, jlong ptr, jint timeMs) {
+ return (jint)linphone_player_seek((LinphonePlayer *)ptr, timeMs);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getState(JNIEnv *env, jobject jobj, jlong ptr) {
+ return (jint)linphone_player_get_state((LinphonePlayer *)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getDuration(JNIEnv *env, jobject jobj, jlong ptr) {
+ return (jint)linphone_player_get_duration((LinphonePlayer *)ptr);
+}
+
+extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getCurrentPosition(JNIEnv *env, jobject jobj, jlong ptr) {
+ return (jint)linphone_player_get_current_position((LinphonePlayer *)ptr);
+}
+
+extern "C" void Java_org_linphone_core_LinphonePlayerImpl_close(JNIEnv *env, jobject playerPtr, jlong ptr) {
+ LinphonePlayer *player = (LinphonePlayer *)ptr;
+ if(player->user_data) {
+ LinphonePlayerData *data = (LinphonePlayerData *)player->user_data;
+ if(data) delete data;
+ player->user_data = NULL;
+ }
+ linphone_player_close(player);
+}
+
+extern "C" void Java_org_linphone_core_LinphonePlayerImpl_destroy(JNIEnv *env, jobject jobj, jlong playerPtr) {
+ LinphonePlayer *player = (LinphonePlayer *)playerPtr;
+ if(player == NULL) {
+ ms_error("Cannot destroy the LinphonePlayerImpl object. Native pointer is NULL");
+ return;
+ }
+ if(player->user_data) {
+ delete (LinphonePlayerData *)player->user_data;
+ }
+ jobject window_id = (jobject)ms_media_player_get_window_id((MSMediaPlayer *)player->impl);
+ if(window_id) env->DeleteGlobalRef(window_id);
+ linphone_player_destroy(player);
+}
+
+extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createLocalPlayer(JNIEnv *env, jobject jobj, jlong ptr, jobject window) {
+ jobject window_ref = NULL;
+ window_ref = env->NewGlobalRef(window);
+ LinphonePlayer *player = linphone_core_create_local_player((LinphoneCore *)ptr, NULL, "MSAndroidDisplay", (void *)window_ref);
+ if(player == NULL) {
+ ms_error("Fails to create a player");
+ if(window_ref) env->DeleteGlobalRef(window_ref);
+ return 0;
+ } else {
+ return (jlong)player;
+ }
+}
+
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: setAudioMulticastAddr
+ * Signature: (JLjava/lang/String;)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastAddr
+ (JNIEnv * env , jobject, jlong ptr, jstring value) {
+ const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL;
+ LinphoneCore *lc=(LinphoneCore*)ptr;
+ int result = linphone_core_set_audio_multicast_addr(lc,char_value);
+ if (char_value) env->ReleaseStringUTFChars(value, char_value);
+ return result;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: setVideoMulticastAddr
+ * Signature: (JLjava/lang/String;)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastAddr
+ (JNIEnv * env, jobject, jlong ptr, jstring value) {
+ const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL;
+ LinphoneCore *lc=(LinphoneCore*)ptr;
+ int result = linphone_core_set_video_multicast_addr(lc,char_value);
+ if (char_value) env->ReleaseStringUTFChars(value, char_value);
+ return result;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: getAudioMulticastAddr
+ * Signature: (J)Ljava/lang/String;
+ */
+extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastAddr
+ (JNIEnv *env , jobject, jlong ptr) {
+ const char *tmp=linphone_core_get_audio_multicast_addr((LinphoneCore*)ptr);
+ return tmp ? env->NewStringUTF(tmp) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: getVideoMulticastAddr
+ * Signature: (J)Ljava/lang/String;
+ */
+extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastAddr
+ (JNIEnv * env, jobject, jlong ptr) {
+ const char *tmp=linphone_core_get_video_multicast_addr((LinphoneCore*)ptr);
+ return tmp ? env->NewStringUTF(tmp) : NULL;
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: setAudioMulticastTtl
+ * Signature: (JI)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastTtl
+ (JNIEnv *, jobject, jlong ptr, jint value) {
+ return linphone_core_set_audio_multicast_ttl((LinphoneCore*)ptr,value);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: setVideoMulticastTtl
+ * Signature: (JI)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastTtl
+ (JNIEnv *, jobject, jlong ptr, jint value) {
+ return linphone_core_set_video_multicast_ttl((LinphoneCore*)ptr,value);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: getAudioMulticastTtl
+ * Signature: (J)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastTtl
+ (JNIEnv *, jobject, jlong ptr) {
+ return linphone_core_get_audio_multicast_ttl((LinphoneCore*)ptr);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: getVideoMulticastTtl
+ * Signature: (J)I
+ */
+extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastTtl
+ (JNIEnv *, jobject, jlong ptr) {
+ return linphone_core_get_video_multicast_ttl((LinphoneCore*)ptr);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: enableAudioMulticast
+ * Signature: (JZ)V
+ */
+extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableAudioMulticast
+ (JNIEnv *, jobject, jlong ptr, jboolean yesno) {
+ return linphone_core_enable_audio_multicast((LinphoneCore*)ptr,yesno);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: audioMulticastEnabled
+ * Signature: (J)Z
+ */
+extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_audioMulticastEnabled
+ (JNIEnv *, jobject, jlong ptr) {
+ return linphone_core_audio_multicast_enabled((LinphoneCore*)ptr);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: enableVideoMulticast
+ * Signature: (JZ)V
+ */
+extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableVideoMulticast
+ (JNIEnv *, jobject, jlong ptr, jboolean yesno) {
+ return linphone_core_enable_video_multicast((LinphoneCore*)ptr,yesno);
+}
+
+/*
+ * Class: org_linphone_core_LinphoneCoreImpl
+ * Method: videoMulticastEnabled
+ * Signature: (J)Z
+ */
+extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_videoMulticastEnabled
+ (JNIEnv *, jobject, jlong ptr) {
+ return linphone_core_video_multicast_enabled((LinphoneCore*)ptr);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableDnsSrv(JNIEnv *env, jobject thiz, jlong lc, jboolean yesno) {
+ linphone_core_enable_dns_srv((LinphoneCore *)lc, yesno);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_dnsSrvEnabled(JNIEnv *env, jobject thiz, jlong lc) {
+ return linphone_core_dns_srv_enabled((LinphoneCore *)lc);
+}
+
+JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoPreset(JNIEnv *env, jobject thiz, jlong lc, jstring preset) {
+ const char *char_preset = preset ? env->GetStringUTFChars(preset, NULL) : NULL;
+ linphone_core_set_video_preset((LinphoneCore *)lc, char_preset);
+ if (char_preset) env->ReleaseStringUTFChars(preset, char_preset);
+}
+
+JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoPreset(JNIEnv *env, jobject thiz, jlong lc) {
+ const char *tmp = linphone_core_get_video_preset((LinphoneCore *)lc);
+ return tmp ? env->NewStringUTF(tmp) : NULL;
+}
diff --git a/coreapi/linphonecore_utils.h b/coreapi/linphonecore_utils.h
index cc0a6f692..04069e504 100644
--- a/coreapi/linphonecore_utils.h
+++ b/coreapi/linphonecore_utils.h
@@ -64,12 +64,15 @@ typedef enum {
typedef void (*LinphoneEcCalibrationCallback)(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data);
+typedef void (*LinphoneEcCalibrationAudioInit)(void *data);
+typedef void (*LinphoneEcCalibrationAudioUninit)(void *data);
/**
*
* Start an echo calibration of the sound devices, in order to find adequate settings for the echo canceller automatically.
**/
-int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, void *cb_data);
+LINPHONE_PUBLIC int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb,
+ LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data);
/**
* @ingroup IOS
* Special function to warm up dtmf feeback stream. #linphone_core_stop_dtmf_stream must() be called before entering FG mode
@@ -93,14 +96,14 @@ void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook
*@param iso country code alpha2
*@return call country code or -1 if not found
*/
-int linphone_dial_plan_lookup_ccc_from_iso(const char* iso);
+LINPHONE_PUBLIC int linphone_dial_plan_lookup_ccc_from_iso(const char* iso);
/**
* @ingroup misc
*Function to get call country code from an e164 number, ex: +33952650121 will return 33
*@param e164 phone number
*@return call country code or -1 if not found
*/
-int linphone_dial_plan_lookup_ccc_from_e164(const char* e164);
+LINPHONE_PUBLIC int linphone_dial_plan_lookup_ccc_from_e164(const char* e164);
#ifdef __cplusplus
}
diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h
index 90491df5b..1a6535fed 100644
--- a/coreapi/linphonefriend.h
+++ b/coreapi/linphonefriend.h
@@ -19,22 +19,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef LINPHONEFRIEND_H_
#define LINPHONEFRIEND_H_
+
+#include "linphonepresence.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+
/**
* @addtogroup buddy_list
* @{
*/
/**
- * @ingroup buddy_list
* Enum controlling behavior for incoming subscription request.
* Use by linphone_friend_set_inc_subscribe_policy()
*/
-typedef enum {
+typedef enum _LinphoneSubscribePolicy {
/**
* Does not automatically accept an incoming subscription request.
- * This policy implies that a decision has to be taken for each incoming subscription request notified by callback LinphoneCoreVTable.new_subscription_request
+ * This policy implies that a decision has to be taken for each incoming subscription request notified by callback LinphoneCoreVTable.new_subscription_requested
*
*/
LinphoneSPWait,
@@ -46,10 +49,11 @@ typedef enum {
* Automatically accepts a subscription request.
*/
LinphoneSPAccept
-}LinphoneSubscribePolicy;
+} LinphoneSubscribePolicy;
/**
* Enum describing remote friend status
+ * @deprecated Use #LinphonePresenceModel and #LinphonePresenceActivity instead
*/
typedef enum _LinphoneOnlineStatus{
/**
@@ -96,6 +100,10 @@ typedef enum _LinphoneOnlineStatus{
* Pending
*/
LinphoneStatusPending,
+ /**
+ * Vacation
+ */
+ LinphoneStatusVacation,
LinphoneStatusEnd
}LinphoneOnlineStatus;
@@ -111,40 +119,69 @@ typedef struct _LinphoneFriend LinphoneFriend;
* Contructor
* @return a new empty #LinphoneFriend
*/
-LinphoneFriend * linphone_friend_new();
+LINPHONE_PUBLIC LinphoneFriend * linphone_friend_new(void);
+
/**
- * Contructor same as linphone_friend_new() + linphone_friend_set_addr()
+ * Contructor same as linphone_friend_new() + linphone_friend_set_address()
* @param addr a buddy address, must be a sip uri like sip:joe@sip.linphone.org
* @return a new #LinphoneFriend with \link linphone_friend_get_address() address initialized \endlink
*/
-LINPHONE_PUBLIC LinphoneFriend *linphone_friend_new_with_addr(const char *addr);
+LINPHONE_PUBLIC LinphoneFriend *linphone_friend_new_with_address(const char *addr);
/**
- * Destructor
- * @param lf #LinphoneFriend object
+ * Contructor same as linphone_friend_new() + linphone_friend_set_address()
+ * @deprecated Use #linphone_friend_new_with_address instead
*/
-void linphone_friend_destroy(LinphoneFriend *lf);
+#define linphone_friend_new_with_addr linphone_friend_new_with_address
/**
- * set #LinphoneAddress for this friend
+ * Destroy a LinphoneFriend.
+ * @param lf LinphoneFriend object
+ * @deprecated Use linphone_friend_unref() instead.
+ */
+LINPHONE_PUBLIC void linphone_friend_destroy(LinphoneFriend *lf);
+
+/**
+ * Set #LinphoneAddress for this friend
* @param fr #LinphoneFriend object
* @param address #LinphoneAddress
*/
-int linphone_friend_set_addr(LinphoneFriend *fr, const LinphoneAddress* address);
+LINPHONE_PUBLIC int linphone_friend_set_address(LinphoneFriend *fr, const LinphoneAddress* address);
/**
- * get address of this friend
+ * Set #LinphoneAddress for this friend
+ * @deprecated Use #linphone_friend_set_address instead
+ */
+#define linphone_friend_set_addr linphone_friend_set_address
+
+/**
+ * Get address of this friend
* @param lf #LinphoneFriend object
* @return #LinphoneAddress
*/
LINPHONE_PUBLIC const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf);
+
+/**
+ * Set the display name for this friend
+ * @param lf #LinphoneFriend object
+ * @param name
+ */
+LINPHONE_PUBLIC int linphone_friend_set_name(LinphoneFriend *lf, const char *name);
+
+/**
+ * Get the display name for this friend
+ * @param lf #LinphoneFriend object
+ * @return The display name of this friend
+ */
+LINPHONE_PUBLIC const char * linphone_friend_get_name(const LinphoneFriend *lf);
+
/**
* get subscription flag value
* @param lf #LinphoneFriend object
* @return returns true is subscription is activated for this friend
*
*/
-bool_t linphone_friend_subscribes_enabled(const LinphoneFriend *lf);
+LINPHONE_PUBLIC bool_t linphone_friend_subscribes_enabled(const LinphoneFriend *lf);
#define linphone_friend_get_send_subscribe linphone_friend_subscribes_enabled
/**
@@ -154,108 +191,226 @@ bool_t linphone_friend_subscribes_enabled(const LinphoneFriend *lf);
*/
LINPHONE_PUBLIC int linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val);
-
#define linphone_friend_send_subscribe linphone_friend_enable_subscribes
+
/**
* Configure incoming subscription policy for this friend.
* @param fr #LinphoneFriend object
* @param pol #LinphoneSubscribePolicy policy to apply.
*/
-int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol);
+LINPHONE_PUBLIC int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol);
+
/**
* get current subscription policy for this #LinphoneFriend
* @param lf #LinphoneFriend object
* @return #LinphoneSubscribePolicy
*
*/
-LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf);
+LINPHONE_PUBLIC LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf);
/**
* Starts editing a friend configuration.
*
* Because friend configuration must be consistent, applications MUST
* call linphone_friend_edit() before doing any attempts to modify
- * friend configuration (such as \link linphone_friend_set_addr() address \endlink or \link linphone_friend_set_inc_subscribe_policy() subscription policy\endlink and so on).
+ * friend configuration (such as \link linphone_friend_set_address() address \endlink or \link linphone_friend_set_inc_subscribe_policy() subscription policy\endlink and so on).
* Once the modifications are done, then the application must call
* linphone_friend_done() to commit the changes.
**/
LINPHONE_PUBLIC void linphone_friend_edit(LinphoneFriend *fr);
+
/**
* Commits modification made to the friend configuration.
* @param fr #LinphoneFriend object
**/
LINPHONE_PUBLIC void linphone_friend_done(LinphoneFriend *fr);
-
-
-
/**
- * get friend status
+ * Get the status of a friend
+ * @param[in] lf A #LinphoneFriend object
* @return #LinphoneOnlineStatus
+ * @deprecated Use linphone_friend_get_presence_model() instead
*/
-LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf);
-BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf);
-void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key);
-const char *linphone_friend_get_ref_key(const LinphoneFriend *lf);
-bool_t linphone_friend_in_list(const LinphoneFriend *lf);
-
-#define linphone_friend_url(lf) ((lf)->url)
+LINPHONE_PUBLIC LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf);
/**
- * return humain readable presence status
- * @param ss
+ * Get the presence model of a friend
+ * @param[in] lf A #LinphoneFriend object
+ * @return A #LinphonePresenceModel object, or NULL if the friend do not have presence information (in which case he is considered offline)
*/
-const char *linphone_online_status_to_string(LinphoneOnlineStatus ss);
+LINPHONE_PUBLIC const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend *lf);
+/**
+ * Store user pointer to friend object.
+**/
+LINPHONE_PUBLIC void linphone_friend_set_user_data(LinphoneFriend *lf, void *data);
+
+/**
+ * Retrieve user data associated with friend.
+**/
+LINPHONE_PUBLIC void* linphone_friend_get_user_data(const LinphoneFriend *lf);
+
+LINPHONE_PUBLIC BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf);
+
+/**
+ * Set the reference key of a friend.
+ * @param[in] lf #LinphoneFriend object.
+ * @param[in] key The reference key to use for the friend.
+**/
+LINPHONE_PUBLIC void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key);
+
+/**
+ * Get the reference key of a friend.
+ * @param[in] lf #LinphoneFriend object.
+ * @return The reference key of the friend.
+**/
+LINPHONE_PUBLIC const char *linphone_friend_get_ref_key(const LinphoneFriend *lf);
+
+/**
+ * Check that the given friend is in a friend list.
+ * @param[in] lf #LinphoneFriend object.
+ * @return TRUE if the friend is in a friend list, FALSE otherwise.
+**/
+LINPHONE_PUBLIC bool_t linphone_friend_in_list(const LinphoneFriend *lf);
+
+
+/**
+ * Return humain readable presence status
+ * @param ss
+ * @deprecated Use #LinphonePresenceModel, #LinphonePresenceActivity and linphone_presence_activity_to_string() instead.
+ */
+LINPHONE_PUBLIC const char *linphone_online_status_to_string(LinphoneOnlineStatus ss);
+
+
+/**
+ * Create a default LinphoneFriend.
+ * @param[in] lc #LinphoneCore object
+ * @return The created #LinphoneFriend object
+ */
+LINPHONE_PUBLIC LinphoneFriend * linphone_core_create_friend(LinphoneCore *lc);
+
+/**
+ * Create a LinphoneFriend from the given address.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] address A string containing the address to create the LinphoneFriend from
+ * @return The created #LinphoneFriend object
+ */
+LINPHONE_PUBLIC LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address);
/**
* Set my presence status
- * @param lc #LinphoneCore object
- * @param minutes_away how long in away
- * @param alternative_contact sip uri used to redirect call in state #LinphoneStatusMoved
- * @param os #LinphoneOnlineStatus
+ * @param[in] lc #LinphoneCore object
+ * @param[in] minutes_away how long in away
+ * @param[in] alternative_contact sip uri used to redirect call in state #LinphoneStatusMoved
+ * @param[in] os #LinphoneOnlineStatus
+ * @deprecated Use linphone_core_set_presence_model() instead
*/
-void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *alternative_contact,LinphoneOnlineStatus os);
-/**
- * get my presence status
- * @param lc #LinphoneCore object
- * @return #LinphoneOnlineStatus
- */
-LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *alternative_contact,LinphoneOnlineStatus os);
+
+/**
+ * Set my presence model
+ * @param[in] lc #LinphoneCore object
+ * @param[in] presence #LinphonePresenceModel
+ */
+LINPHONE_PUBLIC void linphone_core_set_presence_model(LinphoneCore *lc, LinphonePresenceModel *presence);
+
+/**
+ * Get my presence status
+ * @param[in] lc #LinphoneCore object
+ * @return #LinphoneOnlineStatus
+ * @deprecated Use linphone_core_get_presence_model() instead
+ */
+LINPHONE_PUBLIC LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc);
+
+/**
+ * Get my presence model
+ * @param[in] lc #LinphoneCore object
+ * @return A #LinphonePresenceModel object, or NULL if no presence model has been set.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_core_get_presence_model(const LinphoneCore *lc);
+
+/**
+ * @deprecated Use linphone_core_interpret_url() instead
+ */
+LINPHONE_PUBLIC void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result);
-void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result);
/**
* Add a friend to the current buddy list, if \link linphone_friend_enable_subscribes() subscription attribute \endlink is set, a SIP SUBSCRIBE message is sent.
* @param lc #LinphoneCore object
* @param fr #LinphoneFriend to add
*/
LINPHONE_PUBLIC void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *fr);
+
/**
* remove a friend from the buddy list
* @param lc #LinphoneCore object
* @param fr #LinphoneFriend to add
*/
-void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *fr);
+LINPHONE_PUBLIC void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *fr);
+
/**
* Black list a friend. same as linphone_friend_set_inc_subscribe_policy() with #LinphoneSPDeny policy;
* @param lc #LinphoneCore object
* @param lf #LinphoneFriend to add
*/
-void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf);
-/**
- * get Buddy list of LinphoneFriend
- * @param lc #LinphoneCore object
- * */
-LINPHONE_PUBLIC const MSList * linphone_core_get_friend_list(const LinphoneCore *lc);
-/**
- * notify all friends that have subscribed
- * @param lc #LinphoneCore object
- * @param os #LinphoneOnlineStatus to notify
- * */
-void linphone_core_notify_all_friends(LinphoneCore *lc, LinphoneOnlineStatus os);
-LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *addr);
-LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key);
+LINPHONE_PUBLIC void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf);
+/**
+ * Get Buddy list of LinphoneFriend
+ * @param[in] lc #LinphoneCore object
+ * @return \mslist{LinphoneFriend}
+ */
+LINPHONE_PUBLIC const MSList * linphone_core_get_friend_list(const LinphoneCore *lc);
+
+/**
+ * Notify all friends that have subscribed
+ * @param lc #LinphoneCore object
+ * @param presence #LinphonePresenceModel to notify
+ */
+LINPHONE_PUBLIC void linphone_core_notify_all_friends(LinphoneCore *lc, LinphonePresenceModel *presence);
+
+/**
+ * Search a LinphoneFriend by its address.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] addr The address to use to search the friend.
+ * @return The #LinphoneFriend object corresponding to the given address.
+ * @deprecated use linphone_core_find_friend() instead.
+ */
+LINPHONE_PUBLIC LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *addr);
+
+/**
+ * Search a LinphoneFriend by its address.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] addr The address to use to search the friend.
+ * @return The #LinphoneFriend object corresponding to the given address.
+ */
+LINPHONE_PUBLIC LinphoneFriend *linphone_core_find_friend(const LinphoneCore *lc, const LinphoneAddress *addr);
+
+/**
+ * Search a LinphoneFriend by its reference key.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] key The reference key to use to search the friend.
+ * @return The #LinphoneFriend object corresponding to the given reference key.
+ */
+LINPHONE_PUBLIC LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key);
+
+/**
+ * Acquire a reference to the linphone friend.
+ * @param[in] lf LinphoneFriend object
+ * @return The same LinphoneFriend object
+**/
+LINPHONE_PUBLIC LinphoneFriend * linphone_friend_ref(LinphoneFriend *lf);
+
+/**
+ * Release a reference to the linphone friend.
+ * @param[in] lf LinohoneFriend object
+**/
+LINPHONE_PUBLIC void linphone_friend_unref(LinphoneFriend *lf);
+
+/**
+ * Returns the LinphoneCore object managing this friend, if any.
+ */
+LINPHONE_PUBLIC LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr);
/**
* @}
*/
diff --git a/coreapi/linphonepresence.h b/coreapi/linphonepresence.h
new file mode 100644
index 000000000..c0168cb8b
--- /dev/null
+++ b/coreapi/linphonepresence.h
@@ -0,0 +1,946 @@
+/*
+linphonepresence.h
+Copyright (C) 2010-2013 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef LINPHONEPRESENCE_H_
+#define LINPHONEPRESENCE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @addtogroup buddy_list
+ * @{
+ */
+
+
+/** Basic status as defined in section 4.1.4 of RFC 3863 */
+typedef enum LinphonePresenceBasicStatus {
+ /** This value means that the associated contact element, if any, is ready to accept communication. */
+ LinphonePresenceBasicStatusOpen,
+
+ /** This value means that the associated contact element, if any, is unable to accept communication. */
+ LinphonePresenceBasicStatusClosed
+} LinphonePresenceBasicStatus;
+
+/** Activities as defined in section 3.2 of RFC 4480 */
+typedef enum LinphonePresenceActivityType {
+ /** This value is not defined in the RFC, it corresponds to no activity with a basic status of "closed". */
+ LinphonePresenceActivityOffline,
+
+ /** This value is not defined in the RFC, it corresponds to no activity with a basic status of "open". */
+ LinphonePresenceActivityOnline,
+
+ /** The person has a calendar appointment, without specifying exactly of what type. This activity is
+ * indicated if more detailed information is not available or the person chooses not to reveal more
+ * information. */
+ LinphonePresenceActivityAppointment,
+
+ /** The person is physically away from all interactive communication devices. */
+ LinphonePresenceActivityAway,
+
+ /** The person is eating the first meal of the day, usually eaten in the morning. */
+ LinphonePresenceActivityBreakfast,
+
+ /** The person is busy, without further details. */
+ LinphonePresenceActivityBusy,
+
+ /** The person is having his or her main meal of the day, eaten in the evening or at midday. */
+ LinphonePresenceActivityDinner,
+
+ /** This is a scheduled national or local holiday. */
+ LinphonePresenceActivityHoliday,
+
+ /** The person is riding in a vehicle, such as a car, but not steering. */
+ LinphonePresenceActivityInTransit,
+
+ /** The person is looking for (paid) work. */
+ LinphonePresenceActivityLookingForWork,
+
+ /** The person is eating his or her midday meal. */
+ LinphonePresenceActivityLunch,
+
+ /** The person is scheduled for a meal, without specifying whether it is breakfast, lunch, or dinner,
+ * or some other meal. */
+ LinphonePresenceActivityMeal,
+
+ /** The person is in an assembly or gathering of people, as for a business, social, or religious purpose.
+ * A meeting is a sub-class of an appointment. */
+ LinphonePresenceActivityMeeting,
+
+ /** The person is talking on the telephone. */
+ LinphonePresenceActivityOnThePhone,
+
+ /** The person is engaged in an activity with no defined representation. A string describing the activity
+ * in plain text SHOULD be provided. */
+ LinphonePresenceActivityOther,
+
+ /** A performance is a sub-class of an appointment and includes musical, theatrical, and cinematic
+ * performances as well as lectures. It is distinguished from a meeting by the fact that the person
+ * may either be lecturing or be in the audience, with a potentially large number of other people,
+ * making interruptions particularly noticeable. */
+ LinphonePresenceActivityPerformance,
+
+ /** The person will not return for the foreseeable future, e.g., because it is no longer working for
+ * the company. */
+ LinphonePresenceActivityPermanentAbsence,
+
+ /** The person is occupying himself or herself in amusement, sport, or other recreation. */
+ LinphonePresenceActivityPlaying,
+
+ /** The person is giving a presentation, lecture, or participating in a formal round-table discussion. */
+ LinphonePresenceActivityPresentation,
+
+ /** The person is visiting stores in search of goods or services. */
+ LinphonePresenceActivityShopping,
+
+ /** The person is sleeping.*/
+ LinphonePresenceActivitySleeping,
+
+ /** The person is observing an event, such as a sports event. */
+ LinphonePresenceActivitySpectator,
+
+ /** The person is controlling a vehicle, watercraft, or plane. */
+ LinphonePresenceActivitySteering,
+
+ /** The person is on a business or personal trip, but not necessarily in-transit. */
+ LinphonePresenceActivityTravel,
+
+ /** The person is watching television. */
+ LinphonePresenceActivityTV,
+
+ /** The activity of the person is unknown. */
+ LinphonePresenceActivityUnknown,
+
+ /** A period of time devoted to pleasure, rest, or relaxation. */
+ LinphonePresenceActivityVacation,
+
+ /** The person is engaged in, typically paid, labor, as part of a profession or job. */
+ LinphonePresenceActivityWorking,
+
+ /** The person is participating in religious rites. */
+ LinphonePresenceActivityWorship
+} LinphonePresenceActivityType;
+
+/**
+ * Structure holding the information about the presence of a person.
+ */
+struct _LinphonePresenceModel;
+
+/**
+ * Presence model type holding information about the presence of a person.
+ */
+typedef struct _LinphonePresenceModel LinphonePresenceModel;
+
+/**
+ * Structure holding the information about a presence service.
+ */
+struct _LinphonePresenceService;
+
+/**
+ * Structure holding the information about a presence person.
+ */
+struct _LinphonePresencePerson;
+
+/**
+ * Presence person holding information about a presence person.
+ */
+typedef struct _LinphonePresencePerson LinphonePresencePerson;
+
+/**
+ * Presence service type holding information about a presence service.
+ */
+typedef struct _LinphonePresenceService LinphonePresenceService;
+
+/**
+ * Structure holding the information about a presence activity.
+ */
+struct _LinphonePresenceActivity;
+
+/**
+ * Presence activity type holding information about a presence activity.
+ */
+typedef struct _LinphonePresenceActivity LinphonePresenceActivity;
+
+/**
+ * Structure holding the information about a presence note.
+ */
+struct _LinphonePresenceNote;
+
+/**
+ * Presence note type holding information about a presence note.
+ */
+typedef struct _LinphonePresenceNote LinphonePresenceNote;
+
+
+
+/*****************************************************************************
+ * HELPER FUNCTIONS TO EASE ACCESS IN MOST SIMPLER CASES *
+ ****************************************************************************/
+
+/**
+ * Creates a presence model specifying an activity.
+ * @param[in] activity The activity to set for the created presence model.
+ * @param[in] description An additional description of the activity (mainly useful for the 'other' activity). Set it to NULL to not add a description.
+ * @return The created presence model, or NULL if an error occured.
+ * @see linphone_presence_model_new
+ * @see linphone_presence_model_new_with_activity_and_note
+ *
+ * The created presence model has the activity specified in the parameters.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new_with_activity(LinphonePresenceActivityType activity, const char *description);
+
+/**
+ * Creates a presence model specifying an activity and adding a note.
+ * @param[in] activity The activity to set for the created presence model.
+ * @param[in] description An additional description of the activity (mainly useful for the 'other' activity). Set it to NULL to not add a description.
+ * @param[in] note An additional note giving additional information about the contact presence.
+ * @param[in] lang The language the note is written in. It can be set to NULL in order to not specify the language of the note.
+ * @return The created presence model, or NULL if an error occured.
+ * @see linphone_presence_model_new_with_activity
+ * @see linphone_presence_model_new_with_activity_and_note
+ *
+ * The created presence model has the activity and the note specified in the parameters.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new_with_activity_and_note(LinphonePresenceActivityType activity, const char *description, const char *note, const char *lang);
+
+/**
+ * Gets the basic status of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the basic status from.
+ * @return The #LinphonePresenceBasicStatus of the #LinphonePresenceModel object given as parameter.
+ */
+LINPHONE_PUBLIC LinphonePresenceBasicStatus linphone_presence_model_get_basic_status(const LinphonePresenceModel *model);
+
+/**
+ * Sets the basic status of a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to set the basic status.
+ * @param[in] basic_status The #LinphonePresenceBasicStatus to set for the #LinphonePresenceModel object.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_set_basic_status(LinphonePresenceModel *model, LinphonePresenceBasicStatus basic_status);
+
+/**
+ * Gets the timestamp of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the timestamp from.
+ * @return The timestamp of the #LinphonePresenceModel object or -1 on error.
+ */
+LINPHONE_PUBLIC time_t linphone_presence_model_get_timestamp(const LinphonePresenceModel *model);
+
+/**
+ * Gets the contact of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the contact from.
+ * @return A pointer to a dynamically allocated string containing the contact, or NULL if no contact is found.
+ *
+ * The returned string is to be freed by calling ms_free().
+ */
+LINPHONE_PUBLIC char * linphone_presence_model_get_contact(const LinphonePresenceModel *model);
+
+/**
+ * Sets the contact of a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to set the contact.
+ * @param[in] contact The contact string to set.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_set_contact(LinphonePresenceModel *model, const char *contact);
+
+/**
+ * Gets the first activity of a presence model (there is usually only one).
+ * @param[in] model The #LinphonePresenceModel object to get the activity from.
+ * @return A #LinphonePresenceActivity object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_model_get_activity(const LinphonePresenceModel *model);
+
+/**
+ * Sets the activity of a presence model (limits to only one activity).
+ * @param[in] model The #LinphonePresenceModel object for which to set the activity.
+ * @param[in] activity The #LinphonePresenceActivityType to set for the model.
+ * @param[in] description An additional description of the activity to set for the model. Can be NULL if no additional description is to be added.
+ * @return 0 if successful, a value < 0 in case of error.
+ *
+ * WARNING: This function will modify the basic status of the model according to the activity being set.
+ * If you don't want the basic status to be modified automatically, you can use the combination of linphone_presence_model_set_basic_status(),
+ * linphone_presence_model_clear_activities() and linphone_presence_model_add_activity().
+ */
+LINPHONE_PUBLIC int linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivityType activity, const char *description);
+
+/**
+ * Gets the number of activities included in the presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the number of activities from.
+ * @return The number of activities included in the #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_model_get_nb_activities(const LinphonePresenceModel *model);
+
+/**
+ * Gets the nth activity of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the activity from.
+ * @param[in] idx The index of the activity to get (the first activity having the index 0).
+ * @return A pointer to a #LinphonePresenceActivity object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_model_get_nth_activity(const LinphonePresenceModel *model, unsigned int idx);
+
+/**
+ * Adds an activity to a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to add an activity.
+ * @param[in] activity The #LinphonePresenceActivity object to add to the model.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_add_activity(LinphonePresenceModel *model, LinphonePresenceActivity *activity);
+
+/**
+ * Clears the activities of a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to clear the activities.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_clear_activities(LinphonePresenceModel *model);
+
+/**
+ * Gets the first note of a presence model (there is usually only one).
+ * @param[in] model The #LinphonePresenceModel object to get the note from.
+ * @param[in] lang The language of the note to get. Can be NULL to get a note that has no language specified or to get the first note whatever language it is written into.
+ * @return A pointer to a #LinphonePresenceNote object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_model_get_note(const LinphonePresenceModel *model, const char *lang);
+
+/**
+ * Adds a note to a presence model.
+ * @param[in] model The #LinphonePresenceModel object to add a note to.
+ * @param[in] note_content The note to be added to the presence model.
+ * @param[in] lang The language of the note to be added. Can be NULL if no language is to be specified for the note.
+ * @return 0 if successful, a value < 0 in case of error.
+ *
+ * Only one note for each language can be set, so e.g. setting a note for the 'fr' language if there is only one will replace the existing one.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_add_note(LinphonePresenceModel *model, const char *note_content, const char *lang);
+
+/**
+ * Clears all the notes of a presence model.
+ * @param[in] model The #LinphonePresenceModel for which to clear notes.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_clear_notes(LinphonePresenceModel *model);
+
+
+/*****************************************************************************
+ * PRESENCE MODEL FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
+ ****************************************************************************/
+
+/**
+ * Creates a default presence model.
+ * @return The created presence model, NULL on error.
+ * @see linphone_presence_model_new_with_activity
+ * @see linphone_presence_model_new_with_activity_and_note
+ *
+ * The created presence model is considered 'offline'.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new(void);
+
+/**
+ * Gets the number of services included in the presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the number of services from.
+ * @return The number of services included in the #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_model_get_nb_services(const LinphonePresenceModel *model);
+
+/**
+ * Gets the nth service of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the service from.
+ * @param[in] idx The index of the service to get (the first service having the index 0).
+ * @return A pointer to a #LinphonePresenceService object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_model_get_nth_service(const LinphonePresenceModel *model, unsigned int idx);
+
+/**
+ * Adds a service to a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to add a service.
+ * @param[in] service The #LinphonePresenceService object to add to the model.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_add_service(LinphonePresenceModel *model, LinphonePresenceService *service);
+
+/**
+ * Clears the services of a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to clear the services.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_clear_services(LinphonePresenceModel *model);
+
+/**
+ * Gets the number of persons included in the presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the number of persons from.
+ * @return The number of persons included in the #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_model_get_nb_persons(const LinphonePresenceModel *model);
+
+/**
+ * Gets the nth person of a presence model.
+ * @param[in] model The #LinphonePresenceModel object to get the person from.
+ * @param[in] idx The index of the person to get (the first person having the index 0).
+ * @return A pointer to a #LinphonePresencePerson object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresencePerson * linphone_presence_model_get_nth_person(const LinphonePresenceModel *model, unsigned int idx);
+
+/**
+ * Adds a person to a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to add a person.
+ * @param[in] person The #LinphonePresencePerson object to add to the model.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_add_person(LinphonePresenceModel *model, LinphonePresencePerson *person);
+
+/**
+ * Clears the persons of a presence model.
+ * @param[in] model The #LinphonePresenceModel object for which to clear the persons.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_model_clear_persons(LinphonePresenceModel *model);
+
+
+/*****************************************************************************
+ * PRESENCE SERVICE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
+ ****************************************************************************/
+
+/**
+ * Creates a presence service.
+ * @param[in] id The id of the presence service to be created. Can be NULL to generate it automatically.
+ * @param[in] basic_status The #LinphonePresenceBasicStatus to set for the #LinphonePresenceService object.
+ * @param[in] contact The contact string to set.
+ * @return The created presence service, NULL on error.
+ *
+ * The created presence service has the basic status 'closed'.
+ */
+LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_service_new(const char *id, LinphonePresenceBasicStatus basic_status, const char *contact);
+
+/**
+ * Gets the id of a presence service.
+ * @param[in] service The #LinphonePresenceService object to get the id from.
+ * @return A pointer to a dynamically allocated string containing the id, or NULL in case of error.
+ *
+ * The returned string is to be freed by calling ms_free().
+ */
+LINPHONE_PUBLIC char * linphone_presence_service_get_id(const LinphonePresenceService *service);
+
+/**
+ * Sets the id of a presence service.
+ * @param[in] service The #LinphonePresenceService object for which to set the id.
+ * @param[in] id The id string to set. Can be NULL to generate it automatically.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_service_set_id(LinphonePresenceService *service, const char *id);
+
+/**
+ * Gets the basic status of a presence service.
+ * @param[in] service The #LinphonePresenceService object to get the basic status from.
+ * @return The #LinphonePresenceBasicStatus of the #LinphonePresenceService object given as parameter.
+ */
+LINPHONE_PUBLIC LinphonePresenceBasicStatus linphone_presence_service_get_basic_status(const LinphonePresenceService *service);
+
+/**
+ * Sets the basic status of a presence service.
+ * @param[in] service The #LinphonePresenceService object for which to set the basic status.
+ * @param[in] basic_status The #LinphonePresenceBasicStatus to set for the #LinphonePresenceService object.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_service_set_basic_status(LinphonePresenceService *service, LinphonePresenceBasicStatus basic_status);
+
+/**
+ * Gets the contact of a presence service.
+ * @param[in] service The #LinphonePresenceService object to get the contact from.
+ * @return A pointer to a dynamically allocated string containing the contact, or NULL if no contact is found.
+ *
+ * The returned string is to be freed by calling ms_free().
+ */
+LINPHONE_PUBLIC char * linphone_presence_service_get_contact(const LinphonePresenceService *service);
+
+/**
+ * Sets the contact of a presence service.
+ * @param[in] service The #LinphonePresenceService object for which to set the contact.
+ * @param[in] contact The contact string to set.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_service_set_contact(LinphonePresenceService *service, const char *contact);
+
+/**
+ * Gets the number of notes included in the presence service.
+ * @param[in] service The #LinphonePresenceService object to get the number of notes from.
+ * @return The number of notes included in the #LinphonePresenceService object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_service_get_nb_notes(const LinphonePresenceService *service);
+
+/**
+ * Gets the nth note of a presence service.
+ * @param[in] service The #LinphonePresenceService object to get the note from.
+ * @param[in] idx The index of the note to get (the first note having the index 0).
+ * @return A pointer to a #LinphonePresenceNote object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_service_get_nth_note(const LinphonePresenceService *service, unsigned int idx);
+
+/**
+ * Adds a note to a presence service.
+ * @param[in] service The #LinphonePresenceService object for which to add a note.
+ * @param[in] note The #LinphonePresenceNote object to add to the service.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_service_add_note(LinphonePresenceService *service, LinphonePresenceNote *note);
+
+/**
+ * Clears the notes of a presence service.
+ * @param[in] service The #LinphonePresenceService object for which to clear the notes.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_service_clear_notes(LinphonePresenceService *service);
+
+
+/*****************************************************************************
+ * PRESENCE PERSON FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
+ ****************************************************************************/
+
+/**
+ * Creates a presence person.
+ * @param[in] id The id of the presence person to be created. Can be NULL to generate it automatically.
+ * @return The created presence person, NULL on error.
+ */
+LINPHONE_PUBLIC LinphonePresencePerson * linphone_presence_person_new(const char *id);
+
+/**
+ * Gets the id of a presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the id from.
+ * @return A pointer to a dynamically allocated string containing the id, or NULL in case of error.
+ *
+ * The returned string is to be freed by calling ms_free().
+ */
+LINPHONE_PUBLIC char * linphone_presence_person_get_id(const LinphonePresencePerson *person);
+
+/**
+ * Sets the id of a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to set the id.
+ * @param[in] id The id string to set. Can be NULL to generate it automatically.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_set_id(LinphonePresencePerson *person, const char *id);
+
+/**
+ * Gets the number of activities included in the presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the number of activities from.
+ * @return The number of activities included in the #LinphonePresencePerson object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_person_get_nb_activities(const LinphonePresencePerson *person);
+
+/**
+ * Gets the nth activity of a presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the activity from.
+ * @param[in] idx The index of the activity to get (the first activity having the index 0).
+ * @return A pointer to a #LinphonePresenceActivity object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_person_get_nth_activity(const LinphonePresencePerson *person, unsigned int idx);
+
+/**
+ * Adds an activity to a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to add an activity.
+ * @param[in] activity The #LinphonePresenceActivity object to add to the person.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_add_activity(LinphonePresencePerson *person, LinphonePresenceActivity *activity);
+
+/**
+ * Clears the activities of a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to clear the activities.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_clear_activities(LinphonePresencePerson *person);
+
+/**
+ * Gets the number of notes included in the presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the number of notes from.
+ * @return The number of notes included in the #LinphonePresencePerson object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_person_get_nb_notes(const LinphonePresencePerson *person);
+
+/**
+ * Gets the nth note of a presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the note from.
+ * @param[in] idx The index of the note to get (the first note having the index 0).
+ * @return A pointer to a #LinphonePresenceNote object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_person_get_nth_note(const LinphonePresencePerson *person, unsigned int idx);
+
+/**
+ * Adds a note to a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to add a note.
+ * @param[in] note The #LinphonePresenceNote object to add to the person.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_add_note(LinphonePresencePerson *person, LinphonePresenceNote *note);
+
+/**
+ * Clears the notes of a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to clear the notes.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_clear_notes(LinphonePresencePerson *person);
+
+/**
+ * Gets the number of activities notes included in the presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the number of activities notes from.
+ * @return The number of activities notes included in the #LinphonePresencePerson object.
+ */
+LINPHONE_PUBLIC unsigned int linphone_presence_person_get_nb_activities_notes(const LinphonePresencePerson *person);
+
+/**
+ * Gets the nth activities note of a presence person.
+ * @param[in] person The #LinphonePresencePerson object to get the activities note from.
+ * @param[in] idx The index of the activities note to get (the first note having the index 0).
+ * @return A pointer to a #LinphonePresenceNote object if successful, NULL otherwise.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_person_get_nth_activities_note(const LinphonePresencePerson *person, unsigned int idx);
+
+/**
+ * Adds an activities note to a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to add an activities note.
+ * @param[in] note The #LinphonePresenceNote object to add to the person.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_add_activities_note(LinphonePresencePerson *person, LinphonePresenceNote *note);
+
+/**
+ * Clears the activities notes of a presence person.
+ * @param[in] person The #LinphonePresencePerson object for which to clear the activities notes.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_person_clear_activities_notes(LinphonePresencePerson *person);
+
+
+/*****************************************************************************
+ * PRESENCE ACTIVITY FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
+ ****************************************************************************/
+
+/**
+ * Creates a presence activity.
+ * @param[in] acttype The #LinphonePresenceActivityType to set for the activity.
+ * @param[in] description An additional description of the activity to set for the activity. Can be NULL if no additional description is to be added.
+ * @return The created presence activity, NULL on error.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_activity_new(LinphonePresenceActivityType acttype, const char *description);
+
+/**
+ * Gets the string representation of a presence activity.
+ * @param[in] activity A pointer to the #LinphonePresenceActivity object for which to get a string representation.
+ * @return A pointer a dynamically allocated string representing the given activity.
+ *
+ * The returned string is to be freed by calling ms_free().
+ */
+LINPHONE_PUBLIC char * linphone_presence_activity_to_string(const LinphonePresenceActivity * activity);
+
+/**
+ * Gets the activity type of a presence activity.
+ * @param[in] activity A pointer to the #LinphonePresenceActivity for which to get the type.
+ * @return The #LinphonePresenceActivityType of the activity.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivityType linphone_presence_activity_get_type(const LinphonePresenceActivity *activity);
+
+/**
+ * Sets the type of activity of a presence activity.
+ * @param[in] activity The #LinphonePresenceActivity for which to set for the activity type.
+ * @param[in] acttype The activity type to set for the activity.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_activity_set_type(LinphonePresenceActivity *activity, LinphonePresenceActivityType acttype);
+
+/**
+ * Gets the description of a presence activity.
+ * @param[in] activity A pointer to the #LinphonePresenceActivity for which to get the description.
+ * @return A pointer to the description string of the presence activity, or NULL if no description is specified.
+ */
+LINPHONE_PUBLIC const char * linphone_presence_activity_get_description(const LinphonePresenceActivity *activity);
+
+/**
+ * Sets the description of a presence activity.
+ * @param[in] activity The #LinphonePresenceActivity object for which to set the description.
+ * @param[in] description An additional description of the activity. Can be NULL if no additional description is to be added.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_activity_set_description(LinphonePresenceActivity *activity, const char *description);
+
+
+/*****************************************************************************
+ * PRESENCE NOTE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
+ ****************************************************************************/
+
+/**
+ * Creates a presence note.
+ * @param[in] content The content of the note to be created.
+ * @param[in] lang The language of the note to be created. Can be NULL if no language is to be specified for the note.
+ * @return The created presence note, NULL on error.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_note_new(const char *content, const char *lang);
+
+/**
+ * Gets the content of a presence note.
+ * @param[in] note A pointer to the #LinphonePresenceNote for which to get the content.
+ * @return A pointer to the content of the presence note.
+ */
+LINPHONE_PUBLIC const char * linphone_presence_note_get_content(const LinphonePresenceNote *note);
+
+/**
+ * Sets the content of a presence note.
+ * @param[in] note The #LinphonePresenceNote object for which to set the content.
+ * @param[in] content The content of the note.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_note_set_content(LinphonePresenceNote *note, const char *content);
+
+/**
+ * Gets the language of a presence note.
+ * @param[in] note A pointer to the #LinphonePresenceNote for which to get the language.
+ * @return A pointer to the language string of the presence note, or NULL if no language is specified.
+ */
+LINPHONE_PUBLIC const char * linphone_presence_note_get_lang(const LinphonePresenceNote *note);
+
+/**
+ * Sets the language of a presence note.
+ * @param[in] note The #LinphonePresenceNote object for which to set the language.
+ * @param[in] lang The language of the note.
+ * @return 0 if successful, a value < 0 in case of error.
+ */
+LINPHONE_PUBLIC int linphone_presence_note_set_lang(LinphonePresenceNote *note, const char *lang);
+
+
+/*****************************************************************************
+ * PRESENCE INTERNAL FUNCTIONS FOR WRAPPERS IN OTHER PROGRAMMING LANGUAGES *
+ ****************************************************************************/
+
+/**
+ * Increase the reference count of the #LinphonePresenceModel object.
+ * @param[in] model The #LinphonePresenceModel object for which the reference count is to be increased.
+ * @return The #LinphonePresenceModel object with the increased reference count.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_ref(LinphonePresenceModel *model);
+
+/**
+ * Decrease the reference count of the #LinphonePresenceModel object and destroy it if it reaches 0.
+ * @param[in] model The #LinphonePresenceModel object for which the reference count is to be decreased.
+ * @return The #LinphonePresenceModel object if the reference count is still positive, NULL if the object has been destroyed.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_unref(LinphonePresenceModel *model);
+
+/**
+ * Sets the user data of a #LinphonePresenceModel object.
+ * @param[in] model The #LinphonePresenceModel object for which to set the user data.
+ * @param[in] user_data A pointer to the user data to set.
+ */
+LINPHONE_PUBLIC void linphone_presence_model_set_user_data(LinphonePresenceModel *model, void *user_data);
+
+/**
+ * Gets the user data of a #LinphonePresenceModel object.
+ * @param[in] model The #LinphonePresenceModel object for which to get the user data.
+ * @return A pointer to the user data.
+ */
+LINPHONE_PUBLIC void * linphone_presence_model_get_user_data(const LinphonePresenceModel *model);
+
+/**
+ * Increase the reference count of the #LinphonePresenceService object.
+ * @param[in] service The #LinphonePresenceService object for which the reference count is to be increased.
+ * @return The #LinphonePresenceService object with the increased reference count.
+ */
+LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_service_ref(LinphonePresenceService *service);
+
+/**
+ * Decrease the reference count of the #LinphonePresenceService object and destroy it if it reaches 0.
+ * @param[in] service The #LinphonePresenceService object for which the reference count is to be decreased.
+ * @return The #LinphonePresenceService object if the reference count is still positive, NULL if the object has been destroyed.
+ */
+LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_service_unref(LinphonePresenceService *service);
+
+/**
+ * Sets the user data of a #LinphonePresenceService object.
+ * @param[in] service The #LinphonePresenceService object for which to set the user data.
+ * @param[in] user_data A pointer to the user data to set.
+ */
+LINPHONE_PUBLIC void linphone_presence_service_set_user_data(LinphonePresenceService *service, void *user_data);
+
+/**
+ * Gets the user data of a #LinphonePresenceService object.
+ * @param[in] service The #LinphonePresenceService object for which to get the user data.
+ * @return A pointer to the user data.
+ */
+LINPHONE_PUBLIC void * linphone_presence_service_get_user_data(const LinphonePresenceService *service);
+
+/**
+ * Increase the reference count of the #LinphonePresencePerson object.
+ * @param[in] person The #LinphonePresencePerson object for which the reference count is to be increased.
+ * @return The #LinphonePresencePerson object with the increased reference count.
+ */
+LINPHONE_PUBLIC LinphonePresencePerson * linphone_presence_person_ref(LinphonePresencePerson *person);
+
+/**
+ * Decrease the reference count of the #LinphonePresencePerson object and destroy it if it reaches 0.
+ * @param[in] person The #LinphonePresencePerson object for which the reference count is to be decreased.
+ * @return The #LinphonePresencePerson object if the reference count is still positive, NULL if the object has been destroyed.
+ */
+LINPHONE_PUBLIC LinphonePresencePerson * linphone_presence_person_unref(LinphonePresencePerson *person);
+
+/**
+ * Sets the user data of a #LinphonePresencePerson object.
+ * @param[in] person The #LinphonePresencePerson object for which to set the user data.
+ * @param[in] user_data A pointer to the user data to set.
+ */
+LINPHONE_PUBLIC void linphone_presence_person_set_user_data(LinphonePresencePerson *person, void *user_data);
+
+/**
+ * Gets the user data of a #LinphonePresencePerson object.
+ * @param[in] person The #LinphonePresencePerson object for which to get the user data.
+ * @return A pointer to the user data.
+ */
+LINPHONE_PUBLIC void * linphone_presence_person_get_user_data(const LinphonePresencePerson *person);
+
+/**
+ * Increase the reference count of the #LinphonePresenceActivity object.
+ * @param[in] activity The #LinphonePresenceActivity object for which the reference count is to be increased.
+ * @return The #LinphonePresenceActivity object with the increased reference count.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_activity_ref(LinphonePresenceActivity *activity);
+
+/**
+ * Decrease the reference count of the #LinphonePresenceActivity object and destroy it if it reaches 0.
+ * @param[in] activity The #LinphonePresenceActivity object for which the reference count is to be decreased.
+ * @return The #LinphonePresenceActivity object if the reference count is still positive, NULL if the object has been destroyed.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_presence_activity_unref(LinphonePresenceActivity *activity);
+
+/**
+ * Sets the user data of a #LinphonePresenceActivity object.
+ * @param[in] activity The #LinphonePresenceActivity object for which to set the user data.
+ * @param[in] user_data A pointer to the user data to set.
+ */
+LINPHONE_PUBLIC void linphone_presence_activity_set_user_data(LinphonePresenceActivity *activity, void *user_data);
+
+/**
+ * Gets the user data of a #LinphonePresenceActivity object.
+ * @param[in] activity The #LinphonePresenceActivity object for which to get the user data.
+ * @return A pointer to the user data.
+ */
+LINPHONE_PUBLIC void * linphone_presence_activity_get_user_data(const LinphonePresenceActivity *activity);
+
+/**
+ * Increase the reference count of the #LinphonePresenceNote object.
+ * @param[in] note The #LinphonePresenceNote object for which the reference count is to be increased.
+ * @return The #LinphonePresenceNote object with the increased reference count.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_note_ref(LinphonePresenceNote *note);
+
+/**
+ * Decrease the reference count of the #LinphonePresenceNote object and destroy it if it reaches 0.
+ * @param[in] note The #LinphonePresenceNote object for which the reference count is to be decreased.
+ * @return The #LinphonePresenceNote object if the reference count is still positive, NULL if the object has been destroyed.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_presence_note_unref(LinphonePresenceNote *note);
+
+/**
+ * Sets the user data of a #LinphonePresenceNote object.
+ * @param[in] note The #LinphonePresenceNote object for which to set the user data.
+ * @param[in] user_data A pointer to the user data to set.
+ */
+LINPHONE_PUBLIC void linphone_presence_note_set_user_data(LinphonePresenceNote *note, void *user_data);
+
+/**
+ * Gets the user data of a #LinphonePresenceNote object.
+ * @param[in] note The #LinphonePresenceNote object for which to get the user data.
+ * @return A pointer to the user data.
+ */
+LINPHONE_PUBLIC void * linphone_presence_note_get_user_data(const LinphonePresenceNote *note);
+
+
+/*****************************************************************************
+ * LINPHONE CORE FUNCTIONS RELATED TO PRESENCE *
+ ****************************************************************************/
+
+/**
+ * Create a LinphonePresenceActivity with the given type and description.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] acttype The #LinphonePresenceActivityType to set for the activity.
+ * @param[in] description An additional description of the activity to set for the activity. Can be NULL if no additional description is to be added.
+ * @return The created #LinphonePresenceActivity object.
+ */
+LINPHONE_PUBLIC LinphonePresenceActivity * linphone_core_create_presence_activity(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description);
+
+/**
+ * Create a default LinphonePresenceModel.
+ * @param[in] lc #LinphoneCore object.
+ * @return The created #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_core_create_presence_model(LinphoneCore *lc);
+
+/**
+ * Create a LinphonePresenceModel with the given activity type and activity description.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] acttype The #LinphonePresenceActivityType to set for the activity of the created model.
+ * @param[in] description An additional description of the activity to set for the activity. Can be NULL if no additional description is to be added.
+ * @return The created #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_core_create_presence_model_with_activity(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description);
+
+/**
+ * Create a LinphonePresenceModel with the given activity type, activity description, note content and note language.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] acttype The #LinphonePresenceActivityType to set for the activity of the created model.
+ * @param[in] description An additional description of the activity to set for the activity. Can be NULL if no additional description is to be added.
+ * @param[in] note The content of the note to be added to the created model.
+ * @param[in] lang The language of the note to be added to the created model.
+ * @return The created #LinphonePresenceModel object.
+ */
+LINPHONE_PUBLIC LinphonePresenceModel * linphone_core_create_presence_model_with_activity_and_note(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description, const char *note, const char *lang);
+
+/**
+ * Create a LinphonePresenceNote with the given content and language.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] content The content of the note to be created.
+ * @param[in] lang The language of the note to be created.
+ * @return The created #LinphonePresenceNote object.
+ */
+LINPHONE_PUBLIC LinphonePresenceNote * linphone_core_create_presence_note(LinphoneCore *lc, const char *content, const char *lang);
+
+/**
+ * Create a LinphonePresencePerson with the given id.
+ * @param[in] lc #LinphoneCore object
+ * @param[in] id The id of the person to be created.
+ * @return The created #LinphonePresencePerson object.
+ */
+LINPHONE_PUBLIC LinphonePresencePerson * linphone_core_create_presence_person(LinphoneCore *lc, const char *id);
+
+/**
+ * Create a LinphonePresenceService with the given id, basic status and contact.
+ * @param[in] lc #LinphoneCore object.
+ * @param[in] id The id of the service to be created.
+ * @param[in] basic_status The basic status of the service to be created.
+ * @param[in] contact A string containing a contact information corresponding to the service to be created.
+ * @return The created #LinphonePresenceService object.
+ */
+LINPHONE_PUBLIC LinphonePresenceService * linphone_core_create_presence_service(LinphoneCore *lc, const char *id, LinphonePresenceBasicStatus basic_status, const char *contact);
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINPHONEPRESENCE_H_ */
diff --git a/coreapi/localplayer.c b/coreapi/localplayer.c
new file mode 100644
index 000000000..eb53eb640
--- /dev/null
+++ b/coreapi/localplayer.c
@@ -0,0 +1,98 @@
+/*
+linphone
+Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.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
+#include
+
+static int _local_player_open(LinphonePlayer *obj, const char *filename);
+static int _local_player_start(LinphonePlayer *obj);
+static int _local_player_pause(LinphonePlayer *obj);
+static int _local_player_seek(LinphonePlayer *obj, int time_ms);
+static MSPlayerState _local_player_get_state(LinphonePlayer *obj);
+static int _local_player_get_duration(LinphonePlayer *obj);
+static int _local_player_get_current_position(LinphonePlayer *obj);
+static void _local_player_close(LinphonePlayer *obj);
+static void _local_player_destroy(LinphonePlayer *obj);
+static void _local_player_eof_callback(void *user_data);
+
+LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id) {
+ LinphonePlayer *obj = ms_new0(LinphonePlayer, 1);
+ if(snd_card == NULL) snd_card = lc->sound_conf.ring_sndcard;
+ if(video_out == NULL) video_out = linphone_core_get_video_display_filter(lc);
+ obj->impl = ms_media_player_new(snd_card, video_out, window_id);
+ obj->open = _local_player_open;
+ obj->start = _local_player_start;
+ obj->pause = _local_player_pause;
+ obj->seek = _local_player_seek;
+ obj->get_state = _local_player_get_state;
+ obj->get_duration = _local_player_get_duration;
+ obj->get_position = _local_player_get_current_position;
+ obj->close = _local_player_close;
+ obj->destroy = _local_player_destroy;
+ ms_media_player_set_eof_callback((MSMediaPlayer *)obj->impl, _local_player_eof_callback, obj);
+ return obj;
+}
+
+bool_t linphone_local_player_matroska_supported(void) {
+ return ms_media_player_matroska_supported();
+}
+
+static int _local_player_open(LinphonePlayer *obj, const char *filename) {
+ return ms_media_player_open((MSMediaPlayer *)obj->impl, filename) ? 0 : -1;
+}
+
+static int _local_player_start(LinphonePlayer *obj) {
+ return ms_media_player_start((MSMediaPlayer *)obj->impl) ? 0 : -1;
+}
+
+static int _local_player_pause(LinphonePlayer *obj) {
+ ms_media_player_pause((MSMediaPlayer *)obj->impl);
+ return 0;
+}
+
+static int _local_player_seek(LinphonePlayer *obj, int time_ms) {
+ return ms_media_player_seek((MSMediaPlayer *)obj->impl, time_ms) ? 0 : -1;
+}
+
+static MSPlayerState _local_player_get_state(LinphonePlayer *obj) {
+ return ms_media_player_get_state((MSMediaPlayer *)obj->impl);
+}
+
+static int _local_player_get_duration(LinphonePlayer *obj) {
+ return ms_media_player_get_duration((MSMediaPlayer *)obj->impl);
+}
+
+static int _local_player_get_current_position(LinphonePlayer *obj) {
+ return ms_media_player_get_current_position((MSMediaPlayer *)obj->impl);
+}
+
+static void _local_player_destroy(LinphonePlayer *obj) {
+ ms_media_player_free((MSMediaPlayer *)obj->impl);
+ _linphone_player_destroy(obj);
+}
+
+static void _local_player_close(LinphonePlayer *obj) {
+ ms_media_player_close((MSMediaPlayer *)obj->impl);
+}
+
+static void _local_player_eof_callback(void *user_data) {
+ LinphonePlayer *obj = (LinphonePlayer *)user_data;
+ obj->cb(obj, obj->user_data);
+}
diff --git a/tools/lpc2xml.c b/coreapi/lpc2xml.c
similarity index 84%
rename from tools/lpc2xml.c
rename to coreapi/lpc2xml.c
index 39fd62b0e..1446a94cc 100644
--- a/tools/lpc2xml.c
+++ b/coreapi/lpc2xml.c
@@ -29,7 +29,7 @@ struct _lpc2xml_context {
const LpConfig *lpc;
lpc2xml_function cbf;
void *ctx;
-
+
xmlDoc *doc;
char errorBuffer[LPC2XML_BZ];
char warningBuffer[LPC2XML_BZ];
@@ -42,7 +42,7 @@ lpc2xml_context* lpc2xml_context_new(lpc2xml_function cbf, void *ctx) {
xmlCtx->lpc = NULL;
xmlCtx->cbf = cbf;
xmlCtx->ctx = ctx;
-
+
xmlCtx->doc = NULL;
xmlCtx->errorBuffer[0]='\0';
xmlCtx->warningBuffer[0]='\0';
@@ -64,8 +64,8 @@ static void lpc2xml_context_clear_logs(lpc2xml_context *ctx) {
}
static void lpc2xml_log(lpc2xml_context *xmlCtx, int level, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
+ va_list args;
+ va_start(args, fmt);
if(xmlCtx->cbf != NULL) {
xmlCtx->cbf((xmlCtx)->ctx, level, fmt, args);
}
@@ -75,8 +75,8 @@ static void lpc2xml_log(lpc2xml_context *xmlCtx, int level, const char *fmt, ...
static void lpc2xml_genericxml_error(void *ctx, const char *fmt, ...) {
lpc2xml_context *xmlCtx = (lpc2xml_context *)ctx;
int sl = strlen(xmlCtx->errorBuffer);
- va_list args;
- va_start(args, fmt);
+ va_list args;
+ va_start(args, fmt);
vsnprintf(xmlCtx->errorBuffer + sl, LPC2XML_BZ-sl, fmt, args);
va_end(args);
}
@@ -85,8 +85,8 @@ static void lpc2xml_genericxml_error(void *ctx, const char *fmt, ...) {
static void lpc2xml_genericxml_warning(void *ctx, const char *fmt, ...) {
lpc2xml_context *xmlCtx = (lpc2xml_context *)ctx;
int sl = strlen(xmlCtx->warningBuffer);
- va_list args;
- va_start(args, fmt);
+ va_list args;
+ va_start(args, fmt);
vsnprintf(xmlCtx->warningBuffer + sl, LPC2XML_BZ-sl, fmt, args);
va_end(args);
}
@@ -94,11 +94,11 @@ static void lpc2xml_genericxml_warning(void *ctx, const char *fmt, ...) {
static int processEntry(const char *section, const char *entry, xmlNode *node, lpc2xml_context *ctx) {
const char *content = lp_config_get_string(ctx->lpc, section, entry, NULL);
- if(content == NULL) {
- lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Issue when reading the lpc");
+ if (content == NULL) {
+ lpc2xml_log(ctx, LPC2XML_ERROR, "Issue when reading the lpc");
return -1;
}
-
+
lpc2xml_log(ctx, LPC2XML_MESSAGE, "Set %s|%s = %s", section, entry, content);
xmlNodeSetContent(node, (const xmlChar *) content);
return 0;
@@ -113,19 +113,28 @@ struct __processSectionCtx {
static void processSection_cb(const char *entry, struct __processSectionCtx *ctx) {
if(ctx->ret == 0) {
- xmlNode *node = xmlNewChild(ctx->node, NULL, (const xmlChar *)"entry", NULL);
+ const char *comment = "#";
+ xmlNode *node;
+ xmlAttr *name_attr;
+ if (strncmp(comment, entry, strlen(comment)) == 0) {
+ lpc2xml_log(ctx->ctx, LPC2XML_WARNING, "Skipped commented entry %s", entry);
+ ctx->ret = 0;
+ return;
+ }
+
+ node = xmlNewChild(ctx->node, NULL, (const xmlChar *)"entry", NULL);
if(node == NULL) {
lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create \"entry\" element");
ctx->ret = -1;
return;
}
- xmlAttr *name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)entry);
+ name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)entry);
if(name_attr == NULL) {
lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create name attribute for \"entry\" element");
ctx->ret = -1;
return;
}
-
+
ctx->ret = processEntry(ctx->section, entry, node, ctx->ctx);
}
}
@@ -147,12 +156,13 @@ struct __processConfigCtx {
static void processConfig_cb(const char *section, struct __processConfigCtx *ctx) {
if(ctx->ret == 0) {
xmlNode *node = xmlNewChild(ctx->node, NULL, (const xmlChar *)"section", NULL);
+ xmlAttr *name_attr;
if(node == NULL) {
lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create \"section\" element");
ctx->ret = -1;
return;
}
- xmlAttr *name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)section);
+ name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)section);
if(name_attr == NULL) {
lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create name attribute for \"section\" element");
ctx->ret = -1;
@@ -170,22 +180,25 @@ static int processConfig(xmlNode *node, lpc2xml_context *ctx) {
static int processDoc(xmlDoc *doc, lpc2xml_context *ctx) {
int ret = 0;
+ xmlNs *xsi_ns;
+ xmlNs *lpc_ns;
+ xmlAttr *schemaLocation;
xmlNode *root_node = xmlNewNode(NULL, (const xmlChar *)"config");
if(root_node == NULL) {
lpc2xml_log(ctx, LPC2XML_ERROR, "Can't create \"config\" element");
return -1;
}
- xmlNs *lpc_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd", NULL);
+ lpc_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd", NULL);
if(lpc_ns == NULL) {
lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create lpc namespace");
} else {
xmlSetNs(root_node, lpc_ns);
}
- xmlNs *xsi_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance", (const xmlChar *)"xsi");
+ xsi_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance", (const xmlChar *)"xsi");
if(lpc_ns == NULL) {
lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create xsi namespace");
}
- xmlAttr *schemaLocation = xmlNewNsProp(root_node, xsi_ns, (const xmlChar *)"schemaLocation", (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd");
+ schemaLocation = xmlNewNsProp(root_node, xsi_ns, (const xmlChar *)"schemaLocation", (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd");
if(schemaLocation == NULL) {
lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create schemaLocation");
}
@@ -196,12 +209,13 @@ static int processDoc(xmlDoc *doc, lpc2xml_context *ctx) {
static int internal_convert_lpc2xml(lpc2xml_context *ctx) {
int ret = 0;
+ xmlDoc *doc;
lpc2xml_log(ctx, LPC2XML_DEBUG, "Generation started");
if(ctx->doc != NULL) {
xmlFreeDoc(ctx->doc);
ctx->doc = NULL;
}
- xmlDoc *doc = xmlNewDoc((const xmlChar *)"1.0");
+ doc = xmlNewDoc((const xmlChar *)"1.0");
ret = processDoc(doc, ctx);
if(ret == 0) {
ctx->doc = doc;
@@ -219,9 +233,10 @@ int lpc2xml_set_lpc(lpc2xml_context* context, const LpConfig *lpc) {
int lpc2xml_convert_file(lpc2xml_context* context, const char *filename) {
int ret = -1;
+ xmlSaveCtxtPtr save_ctx;
lpc2xml_context_clear_logs(context);
xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
- xmlSaveCtxtPtr save_ctx = xmlSaveToFilename(filename, "UTF-8", XML_SAVE_FORMAT);
+ save_ctx = xmlSaveToFilename(filename, "UTF-8", XML_SAVE_FORMAT);
if(save_ctx != NULL) {
ret = internal_convert_lpc2xml(context);
if(ret == 0) {
@@ -241,9 +256,10 @@ int lpc2xml_convert_file(lpc2xml_context* context, const char *filename) {
int lpc2xml_convert_fd(lpc2xml_context* context, int fd) {
int ret = -1;
+ xmlSaveCtxtPtr save_ctx;
lpc2xml_context_clear_logs(context);
xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
- xmlSaveCtxtPtr save_ctx = xmlSaveToFd(fd, "UTF-8", XML_SAVE_FORMAT);
+ save_ctx = xmlSaveToFd(fd, "UTF-8", XML_SAVE_FORMAT);
if(save_ctx != NULL) {
ret = internal_convert_lpc2xml(context);
if(ret == 0) {
@@ -264,9 +280,10 @@ int lpc2xml_convert_fd(lpc2xml_context* context, int fd) {
int lpc2xml_convert_string(lpc2xml_context* context, char **content) {
int ret = -1;
xmlBufferPtr buffer = xmlBufferCreate();
+ xmlSaveCtxtPtr save_ctx;
lpc2xml_context_clear_logs(context);
xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
- xmlSaveCtxtPtr save_ctx = xmlSaveToBuffer(buffer, "UTF-8", XML_SAVE_FORMAT);
+ save_ctx = xmlSaveToBuffer(buffer, "UTF-8", XML_SAVE_FORMAT);
if(save_ctx != NULL) {
ret = internal_convert_lpc2xml(context);
if(ret == 0) {
diff --git a/tools/lpc2xml.h b/coreapi/lpc2xml.h
similarity index 100%
rename from tools/lpc2xml.h
rename to coreapi/lpc2xml.h
diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c
index 5f90855f5..ba15adbe0 100644
--- a/coreapi/lpconfig.c
+++ b/coreapi/lpconfig.c
@@ -39,7 +39,19 @@
#endif
#endif /*_WIN32_WCE*/
+#ifdef _MSC_VER
+#ifdef LINPHONE_WINDOWS_DESKTOP
+#include
+#else
+#include
+#endif
+#else
+#include
+#endif
+#ifdef _WIN32
+#define RENAME_REQUIRES_NONEXISTENT_NEW_PATH 1
+#endif
#define lp_new0(type,n) (type*)calloc(sizeof(type),n)
@@ -49,21 +61,41 @@
typedef struct _LpItem{
char *key;
char *value;
+ int is_comment;
} LpItem;
+typedef struct _LpSectionParam{
+ char *key;
+ char *value;
+} LpSectionParam;
+
typedef struct _LpSection{
char *name;
MSList *items;
+ MSList *params;
} LpSection;
struct _LpConfig{
+ int refcnt;
FILE *file;
char *filename;
+ char *tmpfilename;
MSList *sections;
int modified;
int readonly;
};
+char* lp_realpath(const char* file, char* name) {
+#if defined(_WIN32) || defined(__QNX__) || defined(ANDROID)
+ return ms_strdup(file);
+#else
+ char * output = realpath(file, name);
+ char * msoutput = ms_strdup(output);
+ free(output);
+ return msoutput;
+#endif
+}
+
LpItem * lp_item_new(const char *key, const char *value){
LpItem *item=lp_new0(LpItem,1);
item->key=ortp_strdup(key);
@@ -71,6 +103,29 @@ LpItem * lp_item_new(const char *key, const char *value){
return item;
}
+LpItem * lp_comment_new(const char *comment){
+ LpItem *item=lp_new0(LpItem,1);
+ char* pos = NULL;
+ item->value=ortp_strdup(comment);
+
+ pos=strchr(item->value,'\r');
+ if (pos==NULL)
+ pos=strchr(item->value,'\n');
+
+ if(pos) {
+ *pos='\0'; /*replace the '\n' */
+ }
+ item->is_comment=TRUE;
+ return item;
+}
+
+LpSectionParam *lp_section_param_new(const char *key, const char *value){
+ LpSectionParam *param = lp_new0(LpSectionParam, 1);
+ param->key = ortp_strdup(key);
+ param->value = ortp_strdup(value);
+ return param;
+}
+
LpSection *lp_section_new(const char *name){
LpSection *sec=lp_new0(LpSection,1);
sec->name=ortp_strdup(name);
@@ -79,14 +134,22 @@ LpSection *lp_section_new(const char *name){
void lp_item_destroy(void *pitem){
LpItem *item=(LpItem*)pitem;
- free(item->key);
- free(item->value);
+ if (item->key) ortp_free(item->key);
+ ortp_free(item->value);
free(item);
}
+void lp_section_param_destroy(void *section_param){
+ LpSectionParam *param = (LpSectionParam*)section_param;
+ ortp_free(param->key);
+ ortp_free(param->value);
+ free(param);
+}
+
void lp_section_destroy(LpSection *sec){
- free(sec->name);
+ ortp_free(sec->name);
ms_list_for_each(sec->items,lp_item_destroy);
+ ms_list_for_each(sec->params,lp_section_param_destroy);
ms_list_free(sec->items);
free(sec);
}
@@ -99,6 +162,10 @@ void lp_config_add_section(LpConfig *lpconfig, LpSection *section){
lpconfig->sections=ms_list_append(lpconfig->sections,(void *)section);
}
+void lp_config_add_section_param(LpSection *section, LpSectionParam *param){
+ section->params = ms_list_append(section->params, (void *)param);
+}
+
void lp_config_remove_section(LpConfig *lpconfig, LpSection *section){
lpconfig->sections=ms_list_remove(lpconfig->sections,(void *)section);
lp_section_destroy(section);
@@ -112,6 +179,14 @@ static bool_t is_first_char(const char *start, const char *pos){
return TRUE;
}
+static int is_a_comment(const char *str){
+ while (*str==' '){
+ str++;
+ }
+ if (*str=='#') return 1;
+ return 0;
+}
+
LpSection *lp_config_find_section(const LpConfig *lpconfig, const char *name){
LpSection *sec;
MSList *elem;
@@ -126,13 +201,25 @@ LpSection *lp_config_find_section(const LpConfig *lpconfig, const char *name){
return NULL;
}
+LpSectionParam *lp_section_find_param(const LpSection *sec, const char *key){
+ MSList *elem;
+ LpSectionParam *param;
+ for (elem = sec->params; elem != NULL; elem = ms_list_next(elem)){
+ param = (LpSectionParam*)elem->data;
+ if (strcmp(param->key, key) == 0) {
+ return param;
+ }
+ }
+ return NULL;
+}
+
LpItem *lp_section_find_item(const LpSection *sec, const char *name){
MSList *elem;
LpItem *item;
/*printf("Looking for item %s\n",name);*/
for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){
item=(LpItem*)elem->data;
- if (strcmp(item->key,name)==0) {
+ if (!item->is_comment && strcmp(item->key,name)==0) {
/*printf("Item %s found\n",name);*/
return item;
}
@@ -140,46 +227,71 @@ LpItem *lp_section_find_item(const LpSection *sec, const char *name){
return NULL;
}
-void lp_config_parse(LpConfig *lpconfig, FILE *file){
- char tmp[MAX_LEN]= {'\0'};
- LpSection *cur=NULL;
+static LpSection* lp_config_parse_line(LpConfig* lpconfig, const char* line, LpSection* cur) {
+ LpSectionParam *params = NULL;
char *pos1,*pos2;
int nbs;
- char secname[MAX_LEN];
- char key[MAX_LEN];
+ int size=strlen(line)+1;
+ char *secname=ms_malloc(size);
+ char *key=ms_malloc(size);
+ char *value=ms_malloc(size);
LpItem *item;
- if (file==NULL) return;
+ pos1=strchr(line,'[');
+ if (pos1!=NULL && is_first_char(line,pos1) ){
+ pos2=strchr(pos1,']');
+ if (pos2!=NULL){
+ secname[0]='\0';
+ /* found section */
+ *pos2='\0';
+ nbs = sscanf(pos1+1, "%s", secname);
+ if (nbs >= 1) {
+ if (strlen(secname) > 0) {
+ cur = lp_config_find_section (lpconfig,secname);
+ if (cur == NULL) {
+ cur = lp_section_new(secname);
+ lp_config_add_section(lpconfig, cur);
+ }
- while(fgets(tmp,MAX_LEN,file)!=NULL){
- tmp[sizeof(tmp) -1] = '\0';
- pos1=strchr(tmp,'[');
- if (pos1!=NULL && is_first_char(tmp,pos1) ){
- pos2=strchr(pos1,']');
- if (pos2!=NULL){
- secname[0]='\0';
- /* found section */
- *pos2='\0';
- nbs = sscanf(pos1+1,"%s",secname);
- if (nbs == 1 ){
- if (strlen(secname)>0){
- cur=lp_config_find_section (lpconfig,secname);
- if (cur==NULL){
- cur=lp_section_new(secname);
- lp_config_add_section(lpconfig,cur);
+ if (pos2 > pos1 + 1 + strlen(secname)) {
+ /* found at least one section param */
+ pos2 = pos1 + 1 + strlen(secname) + 1; // Remove the white space after the secname
+ pos1 = strchr(pos2, '=');
+ while (pos1 != NULL) {
+ /* for each section param */
+ key[0] = '\0';
+ value[0] = '\0';
+ *pos1 = ' ';
+ if (sscanf(pos2, "%s %s", key, value) == 2) {
+ params = lp_section_param_new(key, value);
+ lp_config_add_section_param(cur, params);
+
+ pos2 += strlen(key) + strlen(value) + 2; // Remove the = sign + the white space after each param
+ pos1 = strchr(pos2, '=');
+ } else {
+ ms_warning("parse section params error !");
+ pos1 = NULL;
+ }
}
}
- }else{
- ms_warning("parse error!");
}
+ } else {
+ ms_warning("parse error!");
}
- }else {
- pos1=strchr(tmp,'=');
+ }
+ }else {
+ if (is_a_comment(line)){
+ if (cur){
+ LpItem *comment=lp_comment_new(line);
+ lp_section_add_item(cur,comment);
+ }
+ }else{
+ pos1=strchr(line,'=');
if (pos1!=NULL){
key[0]='\0';
*pos1='\0';
- if (sscanf(tmp,"%s",key)>0){
+ if (sscanf(line,"%s",key)>0){
pos1++;
pos2=strchr(pos1,'\r');
@@ -192,17 +304,18 @@ void lp_config_parse(LpConfig *lpconfig, FILE *file){
/* remove ending white spaces */
for (; pos2>pos1 && pos2[-1]==' ';pos2--) pos2[-1]='\0';
- if (pos2-pos1>=0){
+ if (pos2-pos1>0){
/* found a pair key,value */
+
if (cur!=NULL){
item=lp_section_find_item(cur,key);
if (item==NULL){
lp_section_add_item(cur,lp_item_new(key,pos1));
}else{
- ms_free(item->value);
- item->value=strdup(pos1);
+ ortp_free(item->value);
+ item->value=ortp_strdup(pos1);
}
- /*printf("Found %s %s={%s}\n",cur->name,key,pos1);*/
+ /*ms_message("Found %s=%s",key,pos1);*/
}else{
ms_warning("found key,item but no sections");
}
@@ -211,65 +324,164 @@ void lp_config_parse(LpConfig *lpconfig, FILE *file){
}
}
}
+ ms_free(key);
+ ms_free(value);
+ ms_free(secname);
+ return cur;
+}
+
+void lp_config_parse(LpConfig *lpconfig, FILE *file){
+ char tmp[MAX_LEN]= {'\0'};
+ LpSection* current_section = NULL;
+
+ if (file==NULL) return;
+
+ while(fgets(tmp,MAX_LEN,file)!=NULL){
+ tmp[sizeof(tmp) -1] = '\0';
+ current_section = lp_config_parse_line(lpconfig, tmp, current_section);
+ }
}
LpConfig * lp_config_new(const char *filename){
+ return lp_config_new_with_factory(filename, NULL);
+}
+
+LpConfig * lp_config_new_from_buffer(const char *buffer){
+ LpConfig* conf = lp_new0(LpConfig,1);
+ LpSection* current_section = NULL;
+
+ char* ptr = ms_strdup(buffer);
+ char* strtok_storage = NULL;
+ char* line = strtok_r(ptr, "\n", &strtok_storage);
+
+ conf->refcnt=1;
+
+ while( line != NULL ){
+ current_section = lp_config_parse_line(conf,line,current_section);
+ line = strtok_r(NULL, "\n", &strtok_storage);
+ }
+
+ ms_free(ptr);
+
+ return conf;
+}
+
+LpConfig *lp_config_new_with_factory(const char *config_filename, const char *factory_config_filename) {
LpConfig *lpconfig=lp_new0(LpConfig,1);
- struct stat fileStat;
- if (filename!=NULL){
- lpconfig->filename=ortp_strdup(filename);
- lpconfig->file=fopen(filename,"rw");
+ lpconfig->refcnt=1;
+ if (config_filename!=NULL){
+ if(ortp_file_exist(config_filename) == 0) {
+ lpconfig->filename=lp_realpath(config_filename, NULL);
+ if(lpconfig->filename == NULL) {
+ ms_error("Could not find the real path of %s: %s", config_filename, strerror(errno));
+ goto fail;
+ }
+ } else {
+ lpconfig->filename = ms_strdup(config_filename);
+ }
+ lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",lpconfig->filename);
+ ms_message("Using (r/w) config information from %s", lpconfig->filename);
+
+#if !defined(_WIN32)
+ {
+ struct stat fileStat;
+ if ((stat(lpconfig->filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) {
+ /* make existing configuration files non-group/world-accessible */
+ if (chmod(lpconfig->filename, S_IRUSR | S_IWUSR) == -1) {
+ ms_warning("unable to correct permissions on "
+ "configuration file: %s", strerror(errno));
+ }
+ }
+ }
+#endif /*_WIN32*/
+ /*open with r+ to check if we can write on it later*/
+ lpconfig->file=fopen(lpconfig->filename,"r+");
+#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
+ if (lpconfig->file==NULL){
+ lpconfig->file=fopen(lpconfig->tmpfilename,"r+");
+ if (lpconfig->file){
+ ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename);
+ }
+ }
+#endif
if (lpconfig->file!=NULL){
lp_config_parse(lpconfig,lpconfig->file);
fclose(lpconfig->file);
-#if !defined(_WIN32_WCE)
-#ifndef S_ISREG
-#define S_ISREG(mode) ((mode & S_IFMT) == S_IFREG)
-#endif
- if ((stat(filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) {
- /* make existing configuration files non-group/world-accessible */
- if (_chmod(filename, _S_IREAD | _S_IWRITE) == -1) {
- ms_warning("unable to correct permissions on "
- "configuration file: %s", strerror(errno));
- }
- }
-#endif /*_WIN32_WCE*/
+
lpconfig->file=NULL;
lpconfig->modified=0;
}
}
+ if (factory_config_filename != NULL) {
+ lp_config_read_file(lpconfig, factory_config_filename);
+ }
return lpconfig;
+
+fail:
+ ms_free(lpconfig);
+ return NULL;
}
int lp_config_read_file(LpConfig *lpconfig, const char *filename){
- FILE* f=fopen(filename,"r");
+ char* path = lp_realpath(filename, NULL);
+ FILE* f=fopen(path,"r");
if (f!=NULL){
+ ms_message("Reading config information from %s", path);
lp_config_parse(lpconfig,f);
fclose(f);
return 0;
}
- ms_warning("Fail to open file %s",filename);
+ ms_warning("Fail to open file %s",path);
+ ms_free(path);
return -1;
}
void lp_item_set_value(LpItem *item, const char *value){
- free(item->value);
+ char *prev_value=item->value;
item->value=ortp_strdup(value);
+ ortp_free(prev_value);
}
-void lp_config_destroy(LpConfig *lpconfig){
- if (lpconfig->filename!=NULL) free(lpconfig->filename);
+static void _lp_config_destroy(LpConfig *lpconfig){
+ if (lpconfig->filename!=NULL) ortp_free(lpconfig->filename);
+ if (lpconfig->tmpfilename) ortp_free(lpconfig->tmpfilename);
ms_list_for_each(lpconfig->sections,(void (*)(void*))lp_section_destroy);
ms_list_free(lpconfig->sections);
free(lpconfig);
}
+LpConfig *lp_config_ref(LpConfig *lpconfig){
+ lpconfig->refcnt++;
+ return lpconfig;
+}
+
+void lp_config_unref(LpConfig *lpconfig){
+ lpconfig->refcnt--;
+ if (lpconfig->refcnt==0)
+ _lp_config_destroy(lpconfig);
+}
+
+void lp_config_destroy(LpConfig *lpconfig){
+ lp_config_unref(lpconfig);
+}
+
void lp_section_remove_item(LpSection *sec, LpItem *item){
sec->items=ms_list_remove(sec->items,(void *)item);
lp_item_destroy(item);
}
+const char *lp_config_get_section_param_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value){
+ LpSection *sec;
+ LpSectionParam *param;
+ sec = lp_config_find_section(lpconfig, section);
+ if (sec != NULL) {
+ param = lp_section_find_param(sec, key);
+ if (param != NULL) return param->value;
+ }
+ return default_value;
+}
+
const char *lp_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string){
LpSection *sec;
LpItem *item;
@@ -300,13 +512,16 @@ bool_t lp_config_get_range(const LpConfig *lpconfig, const char *section, const
}
}
+
int lp_config_get_int(const LpConfig *lpconfig,const char *section, const char *key, int default_value){
const char *str=lp_config_get_string(lpconfig,section,key,NULL);
if (str!=NULL) {
int ret=0;
+
if (strstr(str,"0x")==str){
sscanf(str,"%x",&ret);
- }else ret=atoi(str);
+ }else
+ sscanf(str,"%i",&ret);
return ret;
}
else return default_value;
@@ -315,7 +530,7 @@ int lp_config_get_int(const LpConfig *lpconfig,const char *section, const char *
int64_t lp_config_get_int64(const LpConfig *lpconfig,const char *section, const char *key, int64_t default_value){
const char *str=lp_config_get_string(lpconfig,section,key,NULL);
if (str!=NULL) {
-#ifdef WIN32
+#ifdef _WIN32
return (int64_t)_atoi64(str);
#else
return atoll(str);
@@ -338,14 +553,14 @@ void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *ke
if (sec!=NULL){
item=lp_section_find_item(sec,key);
if (item!=NULL){
- if (value!=NULL)
+ if (value!=NULL && value[0] != '\0')
lp_item_set_value(item,value);
else lp_section_remove_item(sec,item);
}else{
- if (value!=NULL)
+ if (value!=NULL && value[0] != '\0')
lp_section_add_item(sec,lp_item_new(key,value));
}
- }else if (value!=NULL){
+ }else if (value!=NULL && value[0] != '\0'){
sec=lp_section_new(section);
lp_config_add_section(lpconfig,sec);
lp_section_add_item(sec,lp_item_new(key,value));
@@ -385,24 +600,40 @@ void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key
}
void lp_item_write(LpItem *item, FILE *file){
- fprintf(file,"%s=%s\n",item->key,item->value);
+ if (item->is_comment)
+ fprintf(file,"%s\n",item->value);
+ else if (item->value && item->value[0] != '\0' )
+ fprintf(file,"%s=%s\n",item->key,item->value);
+ else {
+ ms_warning("Not writing item %s to file, it is empty", item->key);
+ }
+}
+
+void lp_section_param_write(LpSectionParam *param, FILE *file){
+ if( param->value && param->value[0] != '\0') {
+ fprintf(file, " %s=%s", param->key, param->value);
+ } else {
+ ms_warning("Not writing param %s to file, it is empty", param->key);
+ }
}
void lp_section_write(LpSection *sec, FILE *file){
- fprintf(file,"[%s]\n",sec->name);
- ms_list_for_each2(sec->items,(void (*)(void*, void*))lp_item_write,(void *)file);
- fprintf(file,"\n");
+ fprintf(file, "[%s",sec->name);
+ ms_list_for_each2(sec->params, (void (*)(void*, void*))lp_section_param_write, (void *)file);
+ fprintf(file, "]\n");
+ ms_list_for_each2(sec->items, (void (*)(void*, void*))lp_item_write, (void *)file);
+ fprintf(file, "\n");
}
int lp_config_sync(LpConfig *lpconfig){
FILE *file;
if (lpconfig->filename==NULL) return -1;
if (lpconfig->readonly) return 0;
-#ifndef WIN32
+#ifndef _WIN32
/* don't create group/world-accessible files */
(void) umask(S_IRWXG | S_IRWXO);
#endif
- file=fopen(lpconfig->filename,"w");
+ file=fopen(lpconfig->tmpfilename,"w");
if (file==NULL){
ms_warning("Could not write %s ! Maybe it is read-only. Configuration will not be saved.",lpconfig->filename);
lpconfig->readonly=1;
@@ -410,6 +641,16 @@ int lp_config_sync(LpConfig *lpconfig){
}
ms_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)file);
fclose(file);
+#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
+ /* On windows, rename() does not accept that the newpath is an existing file, while it is accepted on Unix.
+ * As a result, we are forced to first delete the linphonerc file, and then rename.*/
+ if (remove(lpconfig->filename)!=0){
+ ms_error("Cannot remove %s: %s",lpconfig->filename, strerror(errno));
+ }
+#endif
+ if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){
+ ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno));
+ }
lpconfig->modified=0;
return 0;
}
@@ -435,7 +676,8 @@ void lp_config_for_each_entry(const LpConfig *lpconfig, const char *section, voi
if (sec!=NULL){
for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){
item=(LpItem*)elem->data;
- callback(item->key, ctx);
+ if (!item->is_comment)
+ callback(item->key, ctx);
}
}
}
@@ -451,3 +693,168 @@ void lp_config_clean_section(LpConfig *lpconfig, const char *section){
int lp_config_needs_commit(const LpConfig *lpconfig){
return lpconfig->modified>0;
}
+
+static const char *DEFAULT_VALUES_SUFFIX = "_default_values";
+
+int lp_config_get_default_int(const LpConfig *lpconfig, const char *section, const char *key, int default_value) {
+ char default_section[MAX_LEN];
+ strcpy(default_section, section);
+ strcat(default_section, DEFAULT_VALUES_SUFFIX);
+
+ return lp_config_get_int(lpconfig, default_section, key, default_value);
+}
+
+int64_t lp_config_get_default_int64(const LpConfig *lpconfig, const char *section, const char *key, int64_t default_value) {
+ char default_section[MAX_LEN];
+ strcpy(default_section, section);
+ strcat(default_section, DEFAULT_VALUES_SUFFIX);
+
+ return lp_config_get_int64(lpconfig, default_section, key, default_value);
+}
+
+float lp_config_get_default_float(const LpConfig *lpconfig, const char *section, const char *key, float default_value) {
+ char default_section[MAX_LEN];
+ strcpy(default_section, section);
+ strcat(default_section, DEFAULT_VALUES_SUFFIX);
+
+ return lp_config_get_float(lpconfig, default_section, key, default_value);
+}
+
+const char* lp_config_get_default_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value) {
+ char default_section[MAX_LEN];
+ strcpy(default_section, section);
+ strcat(default_section, DEFAULT_VALUES_SUFFIX);
+
+ return lp_config_get_string(lpconfig, default_section, key, default_value);
+}
+
+/*
+ * WARNING: this function is very dangerous.
+ * Read carefuly the folowing notices:
+ * 1. The 'path' parameter may be modify by
+ * the function. Be care to keep a copy of
+ * the original string.
+ * 2. The return pointer may points on a part of
+ * 'path'. So, be care to not free the string
+ * pointed by 'path' before the last used of
+ * the returned pointer.
+ * 3. Do not feed it after midnight
+ */
+static const char *_lp_config_dirname(char *path) {
+#ifdef _MSC_VER
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+ static char dirname[_MAX_DRIVE + _MAX_DIR];
+ _splitpath(path, drive, dir, fname, ext);
+ snprintf(dirname, sizeof(dirname), "%s%s", drive, dir);
+ return dirname;
+#else
+ return dirname(path);
+#endif
+}
+
+bool_t lp_config_relative_file_exists(const LpConfig *lpconfig, const char *filename) {
+ if (lpconfig->filename == NULL) {
+ return FALSE;
+ } else {
+ char *filename = ms_strdup(lpconfig->filename);
+ const char *dir = _lp_config_dirname(filename);
+ char *filepath = ms_strdup_printf("%s/%s", dir, filename);
+ char *realfilepath = lp_realpath(filepath, NULL);
+ FILE *file;
+
+ ms_free(filename);
+ ms_free(filepath);
+
+ if(realfilepath == NULL) return FALSE;
+
+ file = fopen(realfilepath, "r");
+ ms_free(realfilepath);
+ if (file) {
+ fclose(file);
+ }
+ return file != NULL;
+ }
+}
+
+void lp_config_write_relative_file(const LpConfig *lpconfig, const char *filename, const char *data) {
+ char *dup_config_file = NULL;
+ const char *dir = NULL;
+ char *filepath = NULL;
+ char *realfilepath = NULL;
+ FILE *file;
+
+ if (lpconfig->filename == NULL) return;
+
+ if(strlen(data) == 0) {
+ ms_warning("%s has not been created because there is no data to write", filename);
+ return;
+ }
+
+ dup_config_file = ms_strdup(lpconfig->filename);
+ dir = _lp_config_dirname(dup_config_file);
+ filepath = ms_strdup_printf("%s/%s", dir, filename);
+ realfilepath = lp_realpath(filepath, NULL);
+ if(realfilepath == NULL) {
+ ms_error("Could not resolv %s: %s", filepath, strerror(errno));
+ goto end;
+ }
+
+ file = fopen(realfilepath, "w");
+ if(file == NULL) {
+ ms_error("Could not open %s for write", realfilepath);
+ goto end;
+ }
+
+ fprintf(file, "%s", data);
+ fclose(file);
+
+end:
+ ms_free(dup_config_file);
+ ms_free(filepath);
+ if(realfilepath) ms_free(realfilepath);
+}
+
+int lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename, char *data, size_t max_length) {
+ char *dup_config_file = NULL;
+ const char *dir = NULL;
+ char *filepath = NULL;
+ FILE *file = NULL;
+ char* realfilepath = NULL;
+
+ if (lpconfig->filename == NULL) return -1;
+
+ dup_config_file = ms_strdup(lpconfig->filename);
+ dir = _lp_config_dirname(dup_config_file);
+ filepath = ms_strdup_printf("%s/%s", dir, filename);
+ realfilepath = lp_realpath(filepath, NULL);
+ if(realfilepath == NULL) {
+ ms_error("Could not resolv %s: %s", filepath, strerror(errno));
+ goto err;
+ }
+
+ file = fopen(realfilepath, "r");
+ if(file == NULL) {
+ ms_error("Could not open %s for read. %s", realfilepath, strerror(errno));
+ goto err;
+ }
+
+ if(fread(data, 1, max_length, file)<=0) {
+ ms_error("%s could not be loaded. %s", realfilepath, strerror(errno));
+ goto err;
+ }
+ fclose(file);
+
+ ms_free(dup_config_file);
+ ms_free(filepath);
+ ms_free(realfilepath);
+ return 0;
+
+err:
+ ms_free(filepath);
+ ms_free(filepath);
+ if(realfilepath) ms_free(realfilepath);
+ return -1;
+}
diff --git a/coreapi/lpconfig.h b/coreapi/lpconfig.h
index 310baaff3..b03bfa57f 100644
--- a/coreapi/lpconfig.h
+++ b/coreapi/lpconfig.h
@@ -21,15 +21,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-
+
#ifndef LPCONFIG_H
#define LPCONFIG_H
-
+#include
#include
+#ifndef LINPHONE_PUBLIC
+ #define LINPHONE_PUBLIC MS2_PUBLIC
+#endif
+
/**
* The LpConfig object is used to manipulate a configuration file.
- *
+ *
* @ingroup misc
* The format of the configuration file is a .ini like format:
* - sections are defined in []
@@ -51,30 +55,55 @@ typedef struct _LpConfig LpConfig;
extern "C" {
#endif
+/**
+ * Instantiates a LpConfig object from a user config file.
+ * The caller of this constructor owns a reference. lp_config_unref() must be called when this object is no longer needed.
+ * @ingroup misc
+ * @param filename the filename of the config file to read to fill the instantiated LpConfig
+ * @see lp_config_new_with_factory
+ */
+LINPHONE_PUBLIC LpConfig * lp_config_new(const char *filename);
-#define LP_CONFIG_DEFAULT_STRING(config, name, default) \
- (config) ? (lp_config_get_string(config, "default_values", name, default)) : (default)
+/**
+ * Instantiates a LpConfig object from a user provided buffer.
+ * The caller of this constructor owns a reference. lp_config_unref() must be called when this object is no longer needed.
+ * @ingroup misc
+ * @param buffer the buffer from which the lpconfig will be retrieved. We expect the buffer to be null-terminated.
+ * @see lp_config_new_with_factory
+ * @see lp_config_new
+ */
+LINPHONE_PUBLIC LpConfig * lp_config_new_from_buffer(const char *buffer);
-#define LP_CONFIG_DEFAULT_INT(config, name, default) \
- (config) ? (lp_config_get_int(config, "default_values", name, default)) : (default)
+/**
+ * Instantiates a LpConfig object from a user config file and a factory config file.
+ * The caller of this constructor owns a reference. lp_config_unref() must be called when this object is no longer needed.
+ * @ingroup misc
+ * @param config_filename the filename of the user config file to read to fill the instantiated LpConfig
+ * @param factory_config_filename the filename of the factory config file to read to fill the instantiated LpConfig
+ * @see lp_config_new
+ *
+ * The user config file is read first to fill the LpConfig and then the factory config file is read.
+ * Therefore the configuration parameters defined in the user config file will be overwritten by the parameters
+ * defined in the factory config file.
+ */
+LINPHONE_PUBLIC LpConfig * lp_config_new_with_factory(const char *config_filename, const char *factory_config_filename);
-#define LP_CONFIG_DEFAULT_INT64(config, name, default) \
- (config) ? (lp_config_get_int64(config, "default_values", name, default)) : (default)
+/**
+ * Reads a user config file and fill the LpConfig with the read config values.
+ * @ingroup misc
+ * @param lpconfig The LpConfig object to fill with the content of the file
+ * @param filename The filename of the config file to read to fill the LpConfig
+ */
+LINPHONE_PUBLIC int lp_config_read_file(LpConfig *lpconfig, const char *filename);
-#define LP_CONFIG_DEFAULT_FLOAT(config, name, default) \
- (config) ? (lp_config_get_float(config, "default_values", name, default)) : (default)
-
-
-LpConfig * lp_config_new(const char *filename);
-int lp_config_read_file(LpConfig *lpconfig, const char *filename);
/**
* Retrieves a configuration item as a string, given its section, key, and default value.
- *
+ *
* @ingroup misc
* The default value string is returned if the config item isn't found.
**/
-const char *lp_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string);
-int lp_config_read_file(LpConfig *lpconfig, const char *filename);
+LINPHONE_PUBLIC const char *lp_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string);
+
/**
* Retrieves a configuration item as a range, given its section, key, and default min and max values.
*
@@ -82,95 +111,102 @@ int lp_config_read_file(LpConfig *lpconfig, const char *filename);
* @return TRUE if the value is successfully parsed as a range, FALSE otherwise.
* If FALSE is returned, min and max are filled respectively with default_min and default_max values.
*/
-bool_t lp_config_get_range(const LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max);
+LINPHONE_PUBLIC bool_t lp_config_get_range(const LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max);
+
/**
* Retrieves a configuration item as an integer, given its section, key, and default value.
- *
+ *
* @ingroup misc
* The default integer value is returned if the config item isn't found.
**/
-int lp_config_get_int(const LpConfig *lpconfig,const char *section, const char *key, int default_value);
+LINPHONE_PUBLIC int lp_config_get_int(const LpConfig *lpconfig,const char *section, const char *key, int default_value);
/**
* Retrieves a configuration item as a 64 bit integer, given its section, key, and default value.
- *
+ *
* @ingroup misc
* The default integer value is returned if the config item isn't found.
**/
-int64_t lp_config_get_int64(const LpConfig *lpconfig,const char *section, const char *key, int64_t default_value);
+LINPHONE_PUBLIC int64_t lp_config_get_int64(const LpConfig *lpconfig,const char *section, const char *key, int64_t default_value);
-
-int lp_config_read_file(LpConfig *lpconfig, const char *filename);
/**
* Retrieves a configuration item as a float, given its section, key, and default value.
- *
+ *
* @ingroup misc
* The default float value is returned if the config item isn't found.
**/
-float lp_config_get_float(const LpConfig *lpconfig,const char *section, const char *key, float default_value);
+LINPHONE_PUBLIC float lp_config_get_float(const LpConfig *lpconfig,const char *section, const char *key, float default_value);
+
/**
- * Sets a string config item
+ * Sets a string config item
*
* @ingroup misc
**/
-void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value);
+LINPHONE_PUBLIC void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value);
+
/**
* Sets a range config item
*
* @ingroup misc
*/
-void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value);
+LINPHONE_PUBLIC void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value);
+
/**
* Sets an integer config item
*
* @ingroup misc
**/
-void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value);
+LINPHONE_PUBLIC void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value);
/**
* Sets an integer config item, but store it as hexadecimal
*
* @ingroup misc
**/
-void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value);
+LINPHONE_PUBLIC void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value);
/**
* Sets a 64 bits integer config item
*
* @ingroup misc
**/
-void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value);
+LINPHONE_PUBLIC void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value);
/**
* Sets a float config item
*
* @ingroup misc
**/
-void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value);
+LINPHONE_PUBLIC void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value);
+
/**
* Writes the config file to disk.
- *
+ *
* @ingroup misc
**/
-int lp_config_sync(LpConfig *lpconfig);
+LINPHONE_PUBLIC int lp_config_sync(LpConfig *lpconfig);
+
/**
* Returns 1 if a given section is present in the configuration.
*
* @ingroup misc
**/
-int lp_config_has_section(const LpConfig *lpconfig, const char *section);
+LINPHONE_PUBLIC int lp_config_has_section(const LpConfig *lpconfig, const char *section);
+
/**
* Removes every pair of key,value in a section and remove the section.
*
* @ingroup misc
**/
-void lp_config_clean_section(LpConfig *lpconfig, const char *section);
+LINPHONE_PUBLIC void lp_config_clean_section(LpConfig *lpconfig, const char *section);
+
/**
* Call a function for each section present in the configuration.
*
* @ingroup misc
**/
void lp_config_for_each_section(const LpConfig *lpconfig, void (*callback)(const char *section, void *ctx), void *ctx);
+
/**
* Call a function for each entry present in a section configuration.
*
@@ -180,8 +216,85 @@ void lp_config_for_each_entry(const LpConfig *lpconfig, const char *section, voi
/*tells whether uncommited (with lp_config_sync()) modifications exist*/
int lp_config_needs_commit(const LpConfig *lpconfig);
-void lp_config_destroy(LpConfig *cfg);
-
+
+LINPHONE_PUBLIC void lp_config_destroy(LpConfig *cfg);
+
+/**
+ * Retrieves a default configuration item as an integer, given its section, key, and default value.
+ *
+ * @ingroup misc
+ * The default integer value is returned if the config item isn't found.
+**/
+LINPHONE_PUBLIC int lp_config_get_default_int(const LpConfig *lpconfig, const char *section, const char *key, int default_value);
+
+/**
+ * Retrieves a default configuration item as a 64 bit integer, given its section, key, and default value.
+ *
+ * @ingroup misc
+ * The default integer value is returned if the config item isn't found.
+**/
+LINPHONE_PUBLIC int64_t lp_config_get_default_int64(const LpConfig *lpconfig, const char *section, const char *key, int64_t default_value);
+
+/**
+ * Retrieves a default configuration item as a float, given its section, key, and default value.
+ *
+ * @ingroup misc
+ * The default float value is returned if the config item isn't found.
+**/
+LINPHONE_PUBLIC float lp_config_get_default_float(const LpConfig *lpconfig, const char *section, const char *key, float default_value);
+
+/**
+ * Retrieves a default configuration item as a string, given its section, key, and default value.
+ *
+ * @ingroup misc
+ * The default value string is returned if the config item isn't found.
+**/
+LINPHONE_PUBLIC const char* lp_config_get_default_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value);
+
+/**
+ * Retrieves a section parameter item as a string, given its section and key.
+ *
+ * @ingroup misc
+ * The default value string is returned if the config item isn't found.
+**/
+LINPHONE_PUBLIC const char* lp_config_get_section_param_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value);
+
+
+/**
+ * increment reference count
+ * @ingroup misc
+**/
+LINPHONE_PUBLIC LpConfig *lp_config_ref(LpConfig *lpconfig);
+
+/**
+ * Decrement reference count, which will eventually free the object.
+ * @ingroup misc
+**/
+LINPHONE_PUBLIC void lp_config_unref(LpConfig *lpconfig);
+
+/**
+ * @brief Write a string in a file placed relatively with the Linphone configuration file.
+ * @param lpconfig LpConfig instance used as a reference
+ * @param filename Name of the file where to write data. The name is relative to the place of the config file
+ * @param data String to write
+ */
+LINPHONE_PUBLIC void lp_config_write_relative_file(const LpConfig *lpconfig, const char *filename, const char *data);
+
+/**
+ * @brief Read a string from a file placed beside the Linphone configuration file
+ * @param lpconfig LpConfig instance used as a reference
+ * @param filename Name of the file where data will be read from. The name is relative to the place of the config file
+ * @param data Buffer where read string will be stored
+ * @param max_length Length of the buffer
+ * @return 0 on success, -1 on failure
+ */
+LINPHONE_PUBLIC int lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename, char *data, size_t max_length);
+
+/**
+ * @return TRUE if file exists relative to the to the current location
+**/
+LINPHONE_PUBLIC bool_t lp_config_relative_file_exists(const LpConfig *lpconfig, const char *filename);
+
#ifdef __cplusplus
}
#endif
diff --git a/coreapi/lsd.c b/coreapi/lsd.c
index e47023f4f..6aa803a06 100644
--- a/coreapi/lsd.c
+++ b/coreapi/lsd.c
@@ -194,7 +194,7 @@ int lsd_player_play(LsdPlayer *b, const char *filename ){
ms_warning("Could not play %s",filename);
return -1;
}
- ms_filter_set_notify_callback (b->player,lsd_player_on_eop,b);
+ ms_filter_add_notify_callback (b->player,lsd_player_on_eop,b,FALSE);
lsd_player_configure(b);
ms_filter_call_method_noarg (b->player,MS_PLAYER_START);
return 0;
@@ -249,7 +249,7 @@ LinphoneSoundDaemon * linphone_sound_daemon_new(const char *cardname, int rate,
mp.pin=0;
lsd_player_init(&lsd->branches[0],mp,MS_ITC_SOURCE_ID,lsd);
- ms_filter_set_notify_callback(lsd->branches[0].player,(MSFilterNotifyFunc)lsd_player_configure,&lsd->branches[0]);
+ ms_filter_add_notify_callback(lsd->branches[0].player,(MSFilterNotifyFunc)lsd_player_configure,&lsd->branches[0],FALSE);
for(i=1;ibranches[i],mp,MS_FILE_PLAYER_ID,lsd);
diff --git a/coreapi/message_storage.c b/coreapi/message_storage.c
new file mode 100644
index 000000000..25d37a1b8
--- /dev/null
+++ b/coreapi/message_storage.c
@@ -0,0 +1,721 @@
+/*
+message_storage.c
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "private.h"
+#include "linphonecore.h"
+
+#ifdef MSG_STORAGE_ENABLED
+#ifndef PRIu64
+#define PRIu64 "I64u"
+#endif
+
+#ifndef _WIN32
+#if !defined(ANDROID) && !defined(__QNXNTO__)
+# include
+# include
+# include
+#endif
+#else
+#include
+#endif
+
+#define MAX_PATH_SIZE 1024
+
+#include "sqlite3.h"
+#include
+
+static ORTP_INLINE LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){
+ MSList* transients = cr->transient_messages;
+ LinphoneChatMessage* chat;
+ while( transients ){
+ chat = (LinphoneChatMessage*)transients->data;
+ if(chat->storage_id == storage_id){
+ return linphone_chat_message_ref(chat);
+ }
+ transients = transients->next;
+ }
+ return NULL;
+}
+
+/* DB layout:
+ * | 0 | storage_id
+ * | 1 | type
+ * | 2 | subtype
+ * | 3 | name
+ * | 4 | encoding
+ * | 5 | size
+ * | 6 | data
+ */
+// Callback for sql request when getting linphone content
+static int callback_content(void *data, int argc, char **argv, char **colName) {
+ LinphoneChatMessage *message = (LinphoneChatMessage *)data;
+
+ if (message->file_transfer_information) {
+ linphone_content_unref(message->file_transfer_information);
+ message->file_transfer_information = NULL;
+ }
+ message->file_transfer_information = linphone_content_new();
+ if (argv[1]) linphone_content_set_type(message->file_transfer_information, argv[1]);
+ if (argv[2]) linphone_content_set_subtype(message->file_transfer_information, argv[2]);
+ if (argv[3]) linphone_content_set_name(message->file_transfer_information, argv[3]);
+ if (argv[4]) linphone_content_set_encoding(message->file_transfer_information, argv[4]);
+ linphone_content_set_size(message->file_transfer_information, (size_t)atoi(argv[5]));
+
+ return 0;
+}
+
+static void fetch_content_from_database(sqlite3 *db, LinphoneChatMessage *message, int content_id) {
+ char* errmsg = NULL;
+ int ret;
+ char * buf;
+
+ buf = sqlite3_mprintf("SELECT * FROM content WHERE id = %i", content_id);
+ ret = sqlite3_exec(db, buf, callback_content, message, &errmsg);
+ if (ret != SQLITE_OK) {
+ ms_error("Error in creation: %s.", errmsg);
+ sqlite3_free(errmsg);
+ }
+ sqlite3_free(buf);
+}
+
+
+
+// Called when fetching all conversations from database
+static int callback_all(void *data, int argc, char **argv, char **colName){
+ LinphoneCore* lc = (LinphoneCore*) data;
+ char* address = argv[0];
+ LinphoneAddress *addr = linphone_address_new(address);
+ if (addr){
+ linphone_core_get_chat_room(lc, addr);
+ linphone_address_destroy(addr);
+ }
+ return 0;
+}
+
+/* DB layout:
+ * | 0 | storage_id
+ * | 1 | localContact
+ * | 2 | remoteContact
+ * | 3 | direction flag
+ * | 4 | message
+ * | 5 | time (unused now, used to be string-based timestamp)
+ * | 6 | read flag
+ * | 7 | status
+ * | 8 | external body url
+ * | 9 | utc timestamp
+ * | 10 | app data text
+ * | 11 | linphone content id
+ */
+static int create_chat_message(void *data, int argc, char **argv, char **colName){
+ LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
+ LinphoneAddress *from;
+ LinphoneAddress *to;
+ unsigned int storage_id = atoi(argv[0]);
+
+ // check if the message exists in the transient list, in which case we should return that one.
+ LinphoneChatMessage* new_message = get_transient_message(cr, storage_id);
+ if( new_message == NULL ){
+ new_message = linphone_chat_room_create_message(cr, argv[4]);
+
+ if(atoi(argv[3])==LinphoneChatMessageIncoming){
+ new_message->dir=LinphoneChatMessageIncoming;
+ from=linphone_address_new(argv[2]);
+ to=linphone_address_new(argv[1]);
+ } else {
+ new_message->dir=LinphoneChatMessageOutgoing;
+ from=linphone_address_new(argv[1]);
+ to=linphone_address_new(argv[2]);
+ }
+ linphone_chat_message_set_from(new_message,from);
+ linphone_address_destroy(from);
+ if (to){
+ linphone_chat_message_set_to(new_message,to);
+ linphone_address_destroy(to);
+ }
+
+ new_message->time = (time_t)atol(argv[9]);
+ new_message->is_read=atoi(argv[6]);
+ new_message->state=atoi(argv[7]);
+ new_message->storage_id=storage_id;
+ new_message->external_body_url= ms_strdup(argv[8]);
+ new_message->appdata = ms_strdup(argv[10]);
+
+ if (argv[11] != NULL) {
+ int id = atoi(argv[11]);
+ if (id >= 0) {
+ fetch_content_from_database(cr->lc->db, new_message, id);
+ }
+ }
+ }
+ cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message);
+
+ return 0;
+}
+
+void linphone_sql_request_message(sqlite3 *db,const char *stmt,LinphoneChatRoom *cr){
+ char* errmsg=NULL;
+ int ret;
+ ret=sqlite3_exec(db,stmt,create_chat_message,cr,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_error("Error in creation: %s.", errmsg);
+ sqlite3_free(errmsg);
+ }
+}
+
+int linphone_sql_request(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;
+}
+
+// Process the request to fetch all chat contacts
+void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){
+ char* errmsg=NULL;
+ int ret;
+ ret=sqlite3_exec(db,stmt,callback_all,lc,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_error("linphone_sql_request_all: error sqlite3_exec(): %s.", errmsg);
+ sqlite3_free(errmsg);
+ }
+}
+
+static int linphone_chat_message_store_content(LinphoneChatMessage *msg) {
+ LinphoneCore *lc = linphone_chat_room_get_lc(msg->chat_room);
+ int id = -1;
+ if (lc->db) {
+ LinphoneContent *content = msg->file_transfer_information;
+ char *buf = sqlite3_mprintf("INSERT INTO content VALUES(NULL,%Q,%Q,%Q,%Q,%i,%Q);",
+ linphone_content_get_type(content),
+ linphone_content_get_subtype(content),
+ linphone_content_get_name(content),
+ linphone_content_get_encoding(content),
+ linphone_content_get_size(content),
+ NULL
+ );
+ linphone_sql_request(lc->db, buf);
+ sqlite3_free(buf);
+ id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
+ }
+ return id;
+}
+
+unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
+ LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room);
+ int id = 0;
+
+ if (lc->db){
+ int content_id = -1;
+ char *peer;
+ char *local_contact;
+ char *buf;
+ if (msg->file_transfer_information) {
+ content_id = linphone_chat_message_store_content(msg);
+ }
+
+ peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
+ local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
+ buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i);",
+ local_contact,
+ peer,
+ msg->dir,
+ msg->message,
+ "-1", /* use UTC field now */
+ msg->is_read,
+ msg->state,
+ msg->external_body_url,
+ (int64_t)msg->time,
+ msg->appdata,
+ content_id
+ );
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+ ms_free(local_contact);
+ ms_free(peer);
+ id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
+ }
+ return id;
+}
+
+void linphone_chat_message_store_state(LinphoneChatMessage *msg){
+ LinphoneCore *lc=msg->chat_room->lc;
+ if (lc->db){
+ char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (id = %i);",
+ msg->state,msg->storage_id);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+ }
+}
+
+void linphone_chat_message_store_appdata(LinphoneChatMessage* msg){
+ LinphoneCore *lc=msg->chat_room->lc;
+ if (lc->db){
+ char *buf=sqlite3_mprintf("UPDATE history SET appdata=%Q WHERE id=%i;",
+ msg->appdata,msg->storage_id);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+ }
+}
+
+void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
+ LinphoneCore *lc=linphone_chat_room_get_lc(cr);
+ int read=1;
+ char *peer;
+ char *buf;
+
+ if (lc->db==NULL) return ;
+
+ // optimization: do not modify the database if no message is marked as unread
+ if(linphone_chat_room_get_unread_messages_count(cr) == 0) return;
+
+ peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
+ buf=sqlite3_mprintf("UPDATE history SET read=%i WHERE remoteContact = %Q;",
+ read,peer);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+ ms_free(peer);
+
+ cr->unread_count = 0;
+}
+
+void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+ LinphoneCore *lc=linphone_chat_room_get_lc(cr);
+ char *buf;
+
+ if (lc->db==NULL) return ;
+
+ buf=sqlite3_mprintf("UPDATE history SET url=%Q WHERE id=%i;",msg->external_body_url,msg->storage_id);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+}
+
+static int linphone_chat_room_get_messages_count(LinphoneChatRoom *cr, bool_t unread_only){
+ LinphoneCore *lc=linphone_chat_room_get_lc(cr);
+ int numrows=0;
+ char *peer;
+ char *buf;
+ sqlite3_stmt *selectStatement;
+ int returnValue;
+
+ if (lc->db==NULL) return 0;
+
+ // optimization: do not read database if the count is already available in memory
+ if(unread_only && cr->unread_count >= 0) return cr->unread_count;
+
+ peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
+ buf=sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q %s;",peer,unread_only?"AND read = 0":"");
+ returnValue = sqlite3_prepare_v2(lc->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);
+ ms_free(peer);
+
+ /* no need to test the sign of cr->unread_count here
+ * because it has been tested above */
+ if(unread_only) cr->unread_count = numrows;
+
+ return numrows;
+}
+
+int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
+ return linphone_chat_room_get_messages_count(cr, TRUE);
+}
+
+int linphone_chat_room_get_history_size(LinphoneChatRoom *cr){
+ return linphone_chat_room_get_messages_count(cr, FALSE);
+}
+
+void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+ LinphoneCore *lc=cr->lc;
+ char *buf;
+
+ if (lc->db==NULL) return ;
+
+ buf=sqlite3_mprintf("DELETE FROM history WHERE id = %i;", msg->storage_id);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+
+ if(cr->unread_count >= 0 && !msg->is_read) {
+ assert(cr->unread_count > 0);
+ cr->unread_count--;
+ }
+}
+
+void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
+ LinphoneCore *lc=cr->lc;
+ char *peer;
+ char *buf;
+
+ if (lc->db==NULL) return ;
+
+ peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
+ buf=sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;",peer);
+ linphone_sql_request(lc->db,buf);
+ sqlite3_free(buf);
+ ms_free(peer);
+
+ if(cr->unread_count > 0) cr->unread_count = 0;
+}
+
+MSList *linphone_chat_room_get_history_range(LinphoneChatRoom *cr, int startm, int endm){
+ LinphoneCore *lc=linphone_chat_room_get_lc(cr);
+ MSList *ret;
+ char *buf,*buf2;
+ char *peer;
+ uint64_t begin,end;
+ int buf_max_size = 512;
+
+ if (lc->db==NULL) return NULL;
+ peer = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
+
+ cr->messages_hist = NULL;
+
+ /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
+ buf=ms_malloc(buf_max_size);
+ buf=sqlite3_snprintf(buf_max_size-1,buf,"SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC",peer);
+
+
+ if (startm<0) startm=0;
+
+ if ((endm>0&&endm>=startm) || (startm == 0 && endm == 0) ){
+ buf2=ms_strdup_printf("%s LIMIT %i ",buf,endm+1-startm);
+ ms_free(buf);
+ buf = buf2;
+ }else if(startm>0){
+ ms_message("%s(): end is lower than start (%d < %d). Assuming no end limit.",__FUNCTION__,endm,startm);
+ buf2=ms_strdup_printf("%s LIMIT -1",buf);
+ ms_free(buf);
+ buf = buf2;
+ }
+
+ if (startm>0){
+ buf2=ms_strdup_printf("%s OFFSET %i ",buf,startm);
+ ms_free(buf);
+ buf = buf2;
+ }
+
+ begin=ortp_get_cur_time_ms();
+ linphone_sql_request_message(lc->db,buf,cr);
+ end=ortp_get_cur_time_ms();
+ ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
+ ms_free(buf);
+ ret=cr->messages_hist;
+ cr->messages_hist=NULL;
+ ms_free(peer);
+ return ret;
+}
+
+MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
+ return linphone_chat_room_get_history_range(cr, 0, nb_message-1);
+}
+
+
+void linphone_close_storage(sqlite3* db){
+ sqlite3_close(db);
+}
+
+void linphone_create_table(sqlite3* db){
+ char* errmsg=NULL;
+ int ret;
+ ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS history ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "localContact TEXT NOT NULL,"
+ "remoteContact TEXT NOT NULL,"
+ "direction INTEGER,"
+ "message TEXT,"
+ "time TEXT NOT NULL,"
+ "read INTEGER,"
+ "status INTEGER"
+ ");",
+ 0,0,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_error("Error in creation: %s.\n", errmsg);
+ sqlite3_free(errmsg);
+ }
+}
+
+
+static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+static time_t parse_time_from_db( const char* time ){
+ /* messages used to be stored in the DB by using string-based time */
+ struct tm ret={0};
+ char tmp1[80]={0};
+ char tmp2[80]={0};
+ int i,j;
+ time_t parsed = 0;
+
+ if( sscanf(time,"%3c %3c%d%d:%d:%d %d",tmp1,tmp2,&ret.tm_mday,
+ &ret.tm_hour,&ret.tm_min,&ret.tm_sec,&ret.tm_year) == 7 ){
+ ret.tm_year-=1900;
+ for(i=0;i<7;i++) {
+ if(strcmp(tmp1,days[i])==0) ret.tm_wday=i;
+ }
+ for(j=0;j<12;j++) {
+ if(strcmp(tmp2,months[j])==0) ret.tm_mon=j;
+ }
+ ret.tm_isdst=-1;
+ parsed = mktime(&ret);
+ }
+ return parsed;
+}
+
+
+static int migrate_messages_timestamp(void* data,int argc, char** argv, char** column_names) {
+ time_t new_time = parse_time_from_db(argv[1]);
+ if( new_time ){
+ /* replace 'time' by -1 and set 'utc' to the timestamp */
+ char *buf = sqlite3_mprintf("UPDATE history SET utc=%lld,time='-1' WHERE id=%i;", (int64_t)new_time, atoi(argv[0]));
+ if( buf) {
+ linphone_sql_request((sqlite3*)data, buf);
+ sqlite3_free(buf);
+ }
+ } else {
+ ms_warning("Cannot parse time %s from id %s", argv[1], argv[0]);
+ }
+ return 0;
+}
+
+static void linphone_migrate_timestamps(sqlite3* db){
+ int ret;
+ char* errmsg = NULL;
+ uint64_t begin=ortp_get_cur_time_ms();
+
+ linphone_sql_request(db,"BEGIN TRANSACTION");
+
+ ret = sqlite3_exec(db,"SELECT id,time,direction FROM history WHERE time != '-1';", migrate_messages_timestamp, db, &errmsg);
+ if( ret != SQLITE_OK ){
+ ms_warning("Error migrating outgoing messages: %s.\n", errmsg);
+ sqlite3_free(errmsg);
+ linphone_sql_request(db, "ROLLBACK");
+ } else {
+ uint64_t end;
+ linphone_sql_request(db, "COMMIT");
+ end=ortp_get_cur_time_ms();
+ ms_message("Migrated message timestamps to UTC in %lu ms",(unsigned long)(end-begin));
+ }
+}
+
+void linphone_update_table(sqlite3* db) {
+ char* errmsg=NULL;
+ int ret;
+
+ // for image url storage
+ ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN url TEXT;",NULL,NULL,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_message("Table already up to date: %s.", errmsg);
+ sqlite3_free(errmsg);
+ } else {
+ ms_debug("Table history updated successfully for URL.");
+ }
+
+ // for UTC timestamp storage
+ ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN utc INTEGER;", NULL,NULL,&errmsg);
+ if( ret != SQLITE_OK ){
+ ms_message("Table already up to date: %s.", errmsg);
+ sqlite3_free(errmsg);
+ } else {
+ ms_debug("Table history updated successfully for UTC.");
+ // migrate from old text-based timestamps to unix time-based timestamps
+ linphone_migrate_timestamps(db);
+ }
+
+ // new field for app-specific storage
+ ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN appdata TEXT;",NULL,NULL,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_message("Table already up to date: %s.", errmsg);
+ sqlite3_free(errmsg);
+ } else {
+ ms_debug("Table history updated successfully for app-specific data.");
+ }
+
+ // new field for linphone content storage
+ ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN content INTEGER;",NULL,NULL,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_message("Table already up to date: %s.", errmsg);
+ sqlite3_free(errmsg);
+ } else {
+ ms_debug("Table history updated successfully for content data.");
+ ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS content ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "type TEXT,"
+ "subtype TEXT,"
+ "name TEXT,"
+ "encoding TEXT,"
+ "size INTEGER,"
+ "data BLOB"
+ ");",
+ 0,0,&errmsg);
+ if(ret != SQLITE_OK) {
+ ms_error("Error in creation: %s.\n", errmsg);
+ sqlite3_free(errmsg);
+ } else {
+ ms_debug("Table content successfully created.");
+ }
+ }
+}
+
+void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
+ char *buf;
+
+ if (lc->db==NULL) return;
+ buf=sqlite3_mprintf("SELECT remoteContact FROM history GROUP BY remoteContact;");
+ linphone_sql_request_all(lc->db,buf,lc);
+ sqlite3_free(buf);
+}
+
+static void _linphone_message_storage_profile(void*data,const char*statement, sqlite3_uint64 duration){
+ ms_warning("SQL statement '%s' took %" PRIu64 " microseconds", statement, (uint64_t)(duration / 1000LL) );
+}
+
+static void linphone_message_storage_activate_debug(sqlite3* db, bool_t debug){
+ if( debug ){
+ sqlite3_profile(db, _linphone_message_storage_profile, NULL );
+ } else {
+ sqlite3_profile(db, NULL, NULL );
+ }
+}
+
+void linphone_core_message_storage_set_debug(LinphoneCore *lc, bool_t debug){
+
+ lc->debug_storage = debug;
+
+ if( lc->db ){
+ linphone_message_storage_activate_debug(lc->db, debug);
+ }
+}
+
+static int _linphone_sqlite3_open(const char *db_file, sqlite3 **db) {
+#if defined(ANDROID) || defined(__QNXNTO__)
+ return sqlite3_open(db_file, db);
+#elif defined(_WIN32)
+ int ret;
+ wchar_t db_file_utf16[MAX_PATH_SIZE];
+ ret = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, db_file, -1, db_file_utf16, MAX_PATH_SIZE);
+ if(ret == 0) db_file_utf16[0] = '\0';
+ return sqlite3_open16(db_file_utf16, db);
+#else
+ char db_file_locale[MAX_PATH_SIZE] = {'\0'};
+ char db_file_utf8[MAX_PATH_SIZE] = "";
+ char *inbuf=db_file_locale, *outbuf=db_file_utf8;
+ size_t inbyteleft = MAX_PATH_SIZE, outbyteleft = MAX_PATH_SIZE;
+ iconv_t cb;
+
+ strncpy(db_file_locale, db_file, MAX_PATH_SIZE-1);
+ cb = iconv_open("UTF-8", nl_langinfo(CODESET));
+ if(cb != (iconv_t)-1) {
+ int ret;
+ ret = iconv(cb, &inbuf, &inbyteleft, &outbuf, &outbyteleft);
+ if(ret == -1) db_file_utf8[0] = '\0';
+ iconv_close(cb);
+ }
+ return sqlite3_open(db_file_utf8, db);
+#endif
+}
+
+void linphone_core_message_storage_init(LinphoneCore *lc){
+ int ret;
+ const char *errmsg;
+ sqlite3 *db;
+
+ linphone_core_message_storage_close(lc);
+
+ ret=_linphone_sqlite3_open(lc->chat_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_message_storage_activate_debug(db, lc->debug_storage);
+
+ linphone_create_table(db);
+ linphone_update_table(db);
+ lc->db=db;
+
+ // Create a chatroom for each contact in the chat history
+ linphone_message_storage_init_chat_rooms(lc);
+}
+
+void linphone_core_message_storage_close(LinphoneCore *lc){
+ if (lc->db){
+ sqlite3_close(lc->db);
+ lc->db=NULL;
+ }
+}
+
+#else
+
+unsigned int linphone_chat_message_store(LinphoneChatMessage *cr){
+ return 0;
+}
+
+void linphone_chat_message_store_state(LinphoneChatMessage *cr){
+}
+
+void linphone_chat_message_store_appdata(LinphoneChatMessage *msg){
+}
+
+void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
+}
+
+MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
+ return NULL;
+}
+
+LINPHONE_PUBLIC MSList *linphone_chat_room_get_history_range(LinphoneChatRoom *cr, int begin, int end){
+ return NULL;
+}
+
+void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+}
+
+void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
+}
+
+void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
+}
+
+void linphone_core_message_storage_init(LinphoneCore *lc){
+}
+
+void linphone_core_message_storage_close(LinphoneCore *lc){
+}
+
+void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+}
+
+int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
+ return 0;
+}
+
+int linphone_chat_room_get_history_size(LinphoneChatRoom *cr){
+ return 0;
+}
+
+#endif
diff --git a/coreapi/misc.c b/coreapi/misc.c
index c2e1217e0..60465887d 100644
--- a/coreapi/misc.c
+++ b/coreapi/misc.c
@@ -1,1164 +1,1836 @@
-
-/*
-linphone
-Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.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/mediastream.h"
-#include
-#include
-#ifdef HAVE_SIGHANDLER_T
-#include
-#endif /*HAVE_SIGHANDLER_T*/
-
-#include
-#if !defined(_WIN32_WCE)
-#include
-#include
-#include
-#if _MSC_VER
-#include
-#else
-#include
-#endif
-#include
-#endif /*_WIN32_WCE*/
-
-#undef snprintf
-#include
-
-#ifdef HAVE_GETIFADDRS
-#include
-#include
-#endif
-#include
-#if _MSC_VER
-#define snprintf _snprintf
-#define popen _popen
-#define pclose _pclose
-#endif
-
-#if !defined(WIN32)
-
-static char lock_name[80];
-static char lock_set=0;
-/* put a lock file in /tmp. this is called when linphone runs as a daemon*/
-int set_lock_file()
-{
- FILE *lockfile;
-
- snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
- lockfile=fopen(lock_name,"w");
- if (lockfile==NULL)
- {
- printf("Failed to create lock file.\n");
- return(-1);
- }
- fprintf(lockfile,"%i",getpid());
- fclose(lockfile);
- lock_set=1;
- return(0);
-}
-
-/* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
-int get_lock_file()
-{
- int pid;
- FILE *lockfile;
-
- snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
- lockfile=fopen(lock_name,"r");
- if (lockfile==NULL)
- return(-1);
- if (fscanf(lockfile,"%i",&pid)!=1){
- ms_warning("Could not read pid in lock file.");
- fclose(lockfile);
- return -1;
- }
- fclose(lockfile);
- return pid;
-}
-
-/* remove the lock file if it was set*/
-int remove_lock_file()
-{
- int err=0;
- if (lock_set)
- {
- err=unlink(lock_name);
- lock_set=0;
- }
- return(err);
-}
-
-#endif
-
-char *int2str(int number)
-{
- char *numstr=ms_malloc(10);
- snprintf(numstr,10,"%i",number);
- return numstr;
-}
-
-void check_sound_device(LinphoneCore *lc)
-{
-#ifdef _linux
- int fd=0;
- int len;
- int a;
- char *file=NULL;
- char *i810_audio=NULL;
- char *snd_pcm_oss=NULL;
- char *snd_mixer_oss=NULL;
- char *snd_pcm=NULL;
- fd=open("/proc/modules",O_RDONLY);
-
- if (fd>0){
- /* read the entire /proc/modules file and check if sound conf seems correct */
- /*a=fstat(fd,&statbuf);
- if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
- len=statbuf.st_size;
- if (len==0) ms_warning("/proc/modules has zero size!");
- */
- /***** fstat does not work on /proc/modules for unknown reason *****/
- len=6000;
- file=ms_malloc(len+1);
- a=read(fd,file,len);
- if (avtable.display_warning(lc,_("You are currently using the i810_audio driver.\nThis driver is buggy and so does not work with Linphone.\nWe suggest that you replace it by its equivalent ALSA driver,\neither with packages from your distribution, or by downloading\nALSA drivers at http://www.alsa-project.org."));*/
- goto end;
- }
- snd_pcm=strstr(file,"snd-pcm");
- if (snd_pcm!=NULL){
- snd_pcm_oss=strstr(file,"snd-pcm-oss");
- snd_mixer_oss=strstr(file,"snd-mixer-oss");
- if (snd_pcm_oss==NULL){
- lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the pcm oss emulation module\nis missing and linphone needs it. Please execute\n'modprobe snd-pcm-oss' as root to load it."));
- }
- if (snd_mixer_oss==NULL){
- lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the mixer oss emulation module\nis missing and linphone needs it. Please execute\n 'modprobe snd-mixer-oss' as root to load it."));
- }
- }
- }else {
-
- ms_warning("Could not open /proc/modules.");
- }
- /* now check general volume. Some user forget to rise it and then complain that linphone is
- not working */
- /* but some other users complain that linphone should not change levels...
- if (lc->sound_conf.sndcard!=NULL){
- a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
- if (a<50){
- ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
- snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
- }
- }
- */
- end:
- if (file!=NULL) ms_free(file);
- if (fd>0) close(fd);
-#endif
-}
-
-#define UDP_HDR_SZ 8
-#define RTP_HDR_SZ 12
-#define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
-
-static void payload_type_set_enable(PayloadType *pt,int value)
-{
- if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
- else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
-}
-
-static bool_t payload_type_enabled(const PayloadType *pt) {
- return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
-}
-
-bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){
- if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
- return payload_type_enabled(pt);
- }
- ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
- return FALSE;
-}
-
-int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){
- if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
- payload_type_set_enable(pt,enabled);
- _linphone_core_codec_config_write(lc);
- return 0;
- }
- ms_error("Enabling codec not in audio or video list of PayloadType !");
- return -1;
-}
-
-int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
- return payload_type_get_number(pt);
-}
-
-const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
- if (ms_filter_codec_supported(pt->mime_type)){
- MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
-#ifdef ENABLE_NLS
- return dgettext("mediastreamer",desc->text);
-#else
- return desc->text;
-#endif
- }
- return NULL;
-}
-
-
-/*this function makes a special case for speex/8000.
-This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
-is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
-static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
- int upload_bw=linphone_core_get_upload_bandwidth(lc);
- if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
- if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
- return 15000;
- }
- }
- return pt->normal_bitrate;
-}
-
-static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
- double npacket=50;
- double packet_size;
- int bitrate;
- bitrate=get_codec_bitrate(lc,pt);
- packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
- return packet_size*8.0*npacket;
-}
-
-void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
- call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
- ms_message("Audio bandwidth for this call is %i",call->audio_bw);
-}
-
-void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
- const MSList *elem;
- PayloadType *max=NULL;
- for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
- PayloadType *pt=(PayloadType*)elem->data;
- if (payload_type_enabled(pt)){
- int pt_bitrate=get_codec_bitrate(lc,pt);
- if (max==NULL) max=pt;
- else if (max->normal_bitrateaudio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
- }
-}
-
-bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
-{
- double codec_band;
- bool_t ret=FALSE;
-
- switch (pt->type){
- case PAYLOAD_AUDIO_CONTINUOUS:
- case PAYLOAD_AUDIO_PACKETIZED:
- codec_band=get_audio_payload_bandwidth(lc,pt);
- ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
- /*hack to avoid using uwb codecs when having low bitrate and video*/
- if (bandwidth_is_greater(199,bandwidth_limit)){
- if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
- ret=FALSE;
- }
- }
- //ms_message("Payload %s: %g",pt->mime_type,codec_band);
- break;
- case PAYLOAD_VIDEO:
- if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
- ret=TRUE;
- }
- else ret=FALSE;
- break;
- }
- return ret;
-}
-
-/* return TRUE if codec can be used with bandwidth, FALSE else*/
-bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
-{
- double codec_band;
- int allowed_bw,video_bw;
- bool_t ret=FALSE;
-
- linphone_core_update_allocated_audio_bandwidth(lc);
- allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
- linphone_core_get_upload_bandwidth(lc));
- if (allowed_bw==0) {
- allowed_bw=-1;
- video_bw=1500; /*around 1.5 Mbit/s*/
- }else
- video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
-
- switch (pt->type){
- case PAYLOAD_AUDIO_CONTINUOUS:
- case PAYLOAD_AUDIO_PACKETIZED:
- codec_band=get_audio_payload_bandwidth(lc,pt);
- ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
- /*hack to avoid using uwb codecs when having low bitrate and video*/
- if (bandwidth_is_greater(199,allowed_bw)){
- if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
- ret=FALSE;
- }
- }
- //ms_message("Payload %s: %g",pt->mime_type,codec_band);
- break;
- case PAYLOAD_VIDEO:
- if (video_bw>0){
- pt->normal_bitrate=video_bw*1000;
- ret=TRUE;
- }
- else ret=FALSE;
- break;
- }
- return ret;
-}
-
-bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
-#if !defined(_WIN32_WCE)
- FILE *f=popen(command,"r");
- if (f!=NULL){
- int err;
- *result=ms_malloc(4096);
- err=fread(*result,1,4096-1,f);
- if (err<0){
- ms_warning("Error reading command output:%s",strerror(errno));
- ms_free(result);
- return FALSE;
- }
- (*result)[err]=0;
- err=pclose(f);
- if (command_ret!=NULL) *command_ret=err;
- return TRUE;
- }
-#endif /*_WIN32_WCE*/
- return FALSE;
-}
-
-static ortp_socket_t create_socket(int local_port){
- struct sockaddr_in laddr;
- ortp_socket_t sock;
- int optval;
- sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
- if (sock<0) {
- ms_error("Fail to create socket");
- return -1;
- }
- memset (&laddr,0,sizeof(laddr));
- laddr.sin_family=AF_INET;
- laddr.sin_addr.s_addr=INADDR_ANY;
- laddr.sin_port=htons(local_port);
- if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
- ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
- close_socket(sock);
- return -1;
- }
- optval=1;
- if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
- (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
- ms_warning("Fail to set SO_REUSEADDR");
- }
- set_non_blocking_socket(sock);
- return sock;
-}
-
-static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
- char buf[STUN_MAX_MESSAGE_SIZE];
- int len = STUN_MAX_MESSAGE_SIZE;
- StunAtrString username;
- StunAtrString password;
- StunMessage req;
- int err;
- memset(&req, 0, sizeof(StunMessage));
- memset(&username,0,sizeof(username));
- memset(&password,0,sizeof(password));
- stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
- len = stunEncodeMessage( &req, buf, len, &password);
- if (len<=0){
- ms_error("Fail to encode stun message.");
- return -1;
- }
- err=sendto(sock,buf,len,0,server,addrlen);
- if (err<0){
- ms_error("sendto failed: %s",strerror(errno));
- return -1;
- }
- return 0;
-}
-
-int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
- struct addrinfo hints,*res=NULL;
- int family = PF_INET;
- int port_int = 3478;
- int ret;
- char port[6];
- char host[NI_MAXHOST];
- char *p1, *p2;
- if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
- family = PF_INET6;
- } else {
- p1 = strchr(server, ':');
- p2 = strrchr(server, ':');
- if (p1 && p2 && (p1 != p2)) {
- family = PF_INET6;
- host[NI_MAXHOST-1]='\0';
- strncpy(host, server, sizeof(host) - 1);
- } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
- host[NI_MAXHOST-1]='\0';
- strncpy(host, server, sizeof(host) - 1);
- }
- }
- snprintf(port, sizeof(port), "%d", port_int);
- memset(&hints,0,sizeof(hints));
- hints.ai_family=family;
- hints.ai_socktype=SOCK_DGRAM;
- hints.ai_protocol=IPPROTO_UDP;
- ret=getaddrinfo(host,port,&hints,&res);
- if (ret!=0){
- ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
- return -1;
- }
- if (!res) return -1;
- memcpy(ss,res->ai_addr,res->ai_addrlen);
- *socklen=res->ai_addrlen;
- freeaddrinfo(res);
- return 0;
-}
-
-static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
- char buf[STUN_MAX_MESSAGE_SIZE];
- int len = STUN_MAX_MESSAGE_SIZE;
- StunMessage resp;
- len=recv(sock,buf,len,0);
- if (len>0){
- struct in_addr ia;
- stunParseMessage(buf,len, &resp );
- *id=resp.msgHdr.tr_id.octet[0];
- if (resp.hasXorMappedAddress){
- *port = resp.xorMappedAddress.ipv4.port;
- ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
- }else if (resp.hasMappedAddress){
- *port = resp.mappedAddress.ipv4.port;
- ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
- }else return -1;
- strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
- }
- return len;
-}
-
-/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
-int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
- const char *server=linphone_core_get_stun_server(lc);
- StunCandidate *ac=&call->ac;
- StunCandidate *vc=&call->vc;
-
- if (lc->sip_conf.ipv6_enabled){
- ms_warning("stun support is not implemented for ipv6");
- return -1;
- }
- if (server!=NULL){
- struct sockaddr_storage ss;
- socklen_t ss_len;
- ortp_socket_t sock1=-1, sock2=-1;
- int loops=0;
- bool_t video_enabled=linphone_core_video_enabled(lc);
- bool_t got_audio,got_video;
- bool_t cone_audio=FALSE,cone_video=FALSE;
- struct timeval init,cur;
- double elapsed;
- int ret=0;
-
- if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
- ms_error("Fail to parser stun server address: %s",server);
- return -1;
- }
- if (lc->vtable.display_status!=NULL)
- lc->vtable.display_status(lc,_("Stun lookup in progress..."));
-
- /*create the two audio and video RTP sockets, and send STUN message to our stun server */
- sock1=create_socket(call->audio_port);
- if (sock1==-1) return -1;
- if (video_enabled){
- sock2=create_socket(call->video_port);
- if (sock2==-1) return -1;
- }
- got_audio=FALSE;
- got_video=FALSE;
- gettimeofday(&init,NULL);
- do{
-
- int id;
- if (loops%20==0){
- ms_message("Sending stun requests...");
- sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
- sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
- if (sock2!=-1){
- sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
- sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
- }
- }
-#ifdef WIN32
- Sleep(10);
-#else
- usleep(10000);
-#endif
-
- if (recvStunResponse(sock1,ac->addr,
- &ac->port,&id)>0){
- ms_message("STUN test result: local audio port maps to %s:%i",
- ac->addr,
- ac->port);
- if (id==11)
- cone_audio=TRUE;
- got_audio=TRUE;
- }
- if (recvStunResponse(sock2,vc->addr,
- &vc->port,&id)>0){
- ms_message("STUN test result: local video port maps to %s:%i",
- vc->addr,
- vc->port);
- if (id==22)
- cone_video=TRUE;
- got_video=TRUE;
- }
- gettimeofday(&cur,NULL);
- elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
- if (elapsed>2000) {
- ms_message("Stun responses timeout, going ahead.");
- ret=-1;
- break;
- }
- loops++;
- }while(!(got_audio && (got_video||sock2==-1) ) );
- if (ret==0) ret=(int)elapsed;
- if (!got_audio){
- ms_error("No stun server response for audio port.");
- }else{
- if (!cone_audio) {
- ms_message("NAT is symmetric for audio port");
- }
- }
- if (sock2!=-1){
- if (!got_video){
- ms_error("No stun server response for video port.");
- }else{
- if (!cone_video) {
- ms_message("NAT is symmetric for video port.");
- }
- }
- }
- close_socket(sock1);
- if (sock2!=-1) close_socket(sock2);
- return ret;
- }
- return -1;
-}
-
-int linphone_core_get_edge_bw(LinphoneCore *lc){
- int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
- return edge_bw;
-}
-
-int linphone_core_get_edge_ptime(LinphoneCore *lc){
- int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
- return edge_ptime;
-}
-
-void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
- int threshold;
- if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
- ms_message("Stun server ping time is %i ms",ping_time_ms);
- threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
-
- if (ping_time_ms>threshold){
- /* we might be in a 2G network*/
- params->low_bandwidth=TRUE;
- }/*else use default settings */
- }
- if (params->low_bandwidth){
- params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
- params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
- params->has_video=FALSE;
- }
-}
-
-
-
-int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
-{
- char local_addr[64];
- struct sockaddr_storage ss;
- socklen_t ss_len;
- IceCheckList *audio_check_list;
- IceCheckList *video_check_list;
- const char *server = linphone_core_get_stun_server(lc);
-
- if ((server == NULL) || (call->ice_session == NULL)) return -1;
- audio_check_list = ice_session_check_list(call->ice_session, 0);
- video_check_list = ice_session_check_list(call->ice_session, 1);
- if (audio_check_list == NULL) return -1;
-
- if (lc->sip_conf.ipv6_enabled){
- ms_warning("stun support is not implemented for ipv6");
- return -1;
- }
-
- if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
- ms_error("Fail to parser stun server address: %s", server);
- return -1;
- }
- if (lc->vtable.display_status != NULL)
- lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
-
- /* Gather local host candidates. */
- if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
- ms_error("Fail to get local ip");
- return -1;
- }
- if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
- ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
- ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
- }
- if (call->params.has_video && (video_check_list != NULL)
- && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
- ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
- ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
- }
-
- ms_message("ICE: gathering candidate from [%s]",server);
- /* Gather local srflx candidates. */
- ice_session_gather_candidates(call->ice_session, ss, ss_len);
- return 0;
-}
-
-void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
-{
- IceCheckList *audio_check_list;
- IceCheckList *video_check_list;
- IceSessionState session_state;
-
- if (call->ice_session == NULL) return;
- audio_check_list = ice_session_check_list(call->ice_session, 0);
- video_check_list = ice_session_check_list(call->ice_session, 1);
- if (audio_check_list == NULL) return;
-
- session_state = ice_session_state(call->ice_session);
- if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
- if (ice_check_list_state(audio_check_list) == ICL_Completed) {
- switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
- case ICT_HostCandidate:
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
- break;
- case ICT_ServerReflexiveCandidate:
- case ICT_PeerReflexiveCandidate:
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
- break;
- case ICT_RelayedCandidate:
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
- break;
- }
- } else {
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
- }
- if (call->params.has_video && (video_check_list != NULL)) {
- if (ice_check_list_state(video_check_list) == ICL_Completed) {
- switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
- case ICT_HostCandidate:
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
- break;
- case ICT_ServerReflexiveCandidate:
- case ICT_PeerReflexiveCandidate:
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
- break;
- case ICT_RelayedCandidate:
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
- break;
- }
- } else {
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
- }
- }
- } else if (session_state == IS_Running) {
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
- if (call->params.has_video && (video_check_list != NULL)) {
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
- }
- } else {
- call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
- if (call->params.has_video && (video_check_list != NULL)) {
- call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
- }
- }
-}
-
-void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
-{
- const char *rtp_addr, *rtcp_addr;
- IceSessionState session_state = ice_session_state(session);
- int nb_candidates;
- int i, j;
- bool_t result;
-
- if (session_state == IS_Completed) {
- desc->ice_completed = TRUE;
- result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
- if (result == TRUE) {
- strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
- } else {
- ms_warning("If ICE has completed successfully, rtp_addr should be set!");
- }
- }
- else {
- desc->ice_completed = FALSE;
- }
- strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
- strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
- for (i = 0; i < desc->n_active_streams; i++) {
- SalStreamDescription *stream = &desc->streams[i];
- IceCheckList *cl = ice_session_check_list(session, i);
- nb_candidates = 0;
- if (cl == NULL) continue;
- if (ice_check_list_state(cl) == ICL_Completed) {
- stream->ice_completed = TRUE;
- result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
- } else {
- stream->ice_completed = FALSE;
- result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
- }
- if (result == TRUE) {
- strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
- strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
- } else {
- memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
- memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
- }
- if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
- strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
- else
- memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
- if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
- strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
- else
- memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
- stream->ice_mismatch = ice_check_list_is_mismatch(cl);
- if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
- memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
- for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
- SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
- IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
- const char *default_addr = NULL;
- int default_port = 0;
- if (ice_candidate->componentID == 1) {
- default_addr = stream->rtp_addr;
- default_port = stream->rtp_port;
- } else if (ice_candidate->componentID == 2) {
- default_addr = stream->rtcp_addr;
- default_port = stream->rtcp_port;
- } else continue;
- if (default_addr[0] == '\0') default_addr = desc->addr;
- /* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */
- if ((ice_check_list_state(cl) == ICL_Completed)
- && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
- continue;
- strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
- sal_candidate->componentID = ice_candidate->componentID;
- sal_candidate->priority = ice_candidate->priority;
- strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
- strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
- sal_candidate->port = ice_candidate->taddr.port;
- if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
- strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
- sal_candidate->rport = ice_candidate->base->taddr.port;
- }
- nb_candidates++;
- }
- }
- if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
- int rtp_port, rtcp_port;
- memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
- if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
- strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
- stream->ice_remote_candidates[0].port = rtp_port;
- strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
- stream->ice_remote_candidates[1].port = rtcp_port;
- } else {
- ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
- }
- } else {
- for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
- stream->ice_remote_candidates[j].addr[0] = '\0';
- stream->ice_remote_candidates[j].port = 0;
- }
- }
- }
-}
-
-static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
-{
- if (componentID == 1) {
- *addr = stream->rtp_addr;
- *port = stream->rtp_port;
- } else if (componentID == 2) {
- *addr = stream->rtcp_addr;
- *port = stream->rtcp_port;
- } else return;
- if ((*addr)[0] == '\0') *addr = md->addr;
-}
-
-void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
-{
- bool_t ice_restarted = FALSE;
-
- if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
- int i, j;
-
- /* Check for ICE restart and set remote credentials. */
- if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
- ice_session_restart(call->ice_session);
- ice_restarted = TRUE;
- } else {
- for (i = 0; i < md->n_total_streams; i++) {
- const SalStreamDescription *stream = &md->streams[i];
- IceCheckList *cl = ice_session_check_list(call->ice_session, i);
- if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
- ice_session_restart(call->ice_session);
- ice_restarted = TRUE;
- break;
- }
- }
- }
- if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
- ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
- } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
- if (ice_restarted == FALSE) {
- ice_session_restart(call->ice_session);
- ice_restarted = TRUE;
- }
- ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
- }
- for (i = 0; i < md->n_total_streams; i++) {
- const SalStreamDescription *stream = &md->streams[i];
- IceCheckList *cl = ice_session_check_list(call->ice_session, i);
- if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
- if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
- if (ice_restarted == FALSE) {
- ice_session_restart(call->ice_session);
- ice_restarted = TRUE;
- }
- ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
- break;
- }
- }
- }
-
- /* Create ICE check lists if needed and parse ICE attributes. */
- for (i = 0; i < md->n_total_streams; i++) {
- const SalStreamDescription *stream = &md->streams[i];
- IceCheckList *cl = ice_session_check_list(call->ice_session, i);
- if (cl == NULL) {
- cl = ice_check_list_new();
- ice_session_add_check_list(call->ice_session, cl);
- switch (stream->type) {
- case SalAudio:
- if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
- break;
- case SalVideo:
- if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
- break;
- default:
- break;
- }
- }
- if (stream->ice_mismatch == TRUE) {
- ice_check_list_set_state(cl, ICL_Failed);
- } else if (stream->rtp_port == 0) {
- ice_session_remove_check_list(call->ice_session, cl);
- } else {
- if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
- ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
- for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
- const SalIceCandidate *candidate = &stream->ice_candidates[j];
- bool_t default_candidate = FALSE;
- const char *addr = NULL;
- int port = 0;
- if (candidate->addr[0] == '\0') break;
- if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
- get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
- if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
- default_candidate = TRUE;
- ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
- candidate->priority, candidate->foundation, default_candidate);
- }
- if (ice_restarted == FALSE) {
- bool_t losing_pairs_added = FALSE;
- for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
- const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
- const char *addr = NULL;
- int port = 0;
- int componentID = j + 1;
- if (candidate->addr[0] == '\0') break;
- get_default_addr_and_port(componentID, md, stream, &addr, &port);
- if (j == 0) {
- /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
- ice_check_list_unselect_valid_pairs(cl);
- }
- ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
- losing_pairs_added = TRUE;
- }
- if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
- }
- }
- }
- for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
- ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
- }
- ice_session_check_mismatch(call->ice_session);
- } else {
- /* Response from remote does not contain mandatory ICE attributes, delete the session. */
- linphone_call_delete_ice_session(call);
- return;
- }
- if (ice_session_nb_check_lists(call->ice_session) == 0) {
- linphone_call_delete_ice_session(call);
- }
-}
-
-bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
-{
- int i;
-
- for (i = 0; i < md->n_active_streams; i++) {
- if (md->streams[i].type == SalVideo)
- return TRUE;
- }
- return FALSE;
-}
-
-LinphoneCall * is_a_linphone_call(void *user_pointer){
- LinphoneCall *call=(LinphoneCall*)user_pointer;
- if (call==NULL) return NULL;
- return call->magic==linphone_call_magic ? call : NULL;
-}
-
-LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
- LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
- if (cfg==NULL) return NULL;
- return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
-}
-
-unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
- unsigned int ret=0;
- const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
- if (features){
- char tmp[256]={0};
- char name[256];
- char *p,*n;
- strncpy(tmp,features,sizeof(tmp)-1);
- for(p=tmp;*p!='\0';p++){
- if (*p==' ') continue;
- n=strchr(p,'|');
- if (n) *n='\0';
- sscanf(p,"%s",name);
- ms_message("Found audio feature %s",name);
- if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
- else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
- else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
- else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
- else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
- else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
- else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
- else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
- else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
- else if (strcasecmp(name,"NONE")==0) ret=0;
- else ms_error("Unsupported audio feature %s requested in config file.",name);
- if (!n) break;
- p=n;
- }
- }else ret=AUDIO_STREAM_FEATURE_ALL;
-
- if (ret==AUDIO_STREAM_FEATURE_ALL){
- /*since call recording is specified before creation of the stream in linphonecore,
- * it will be requested on demand. It is not necessary to include it all the time*/
- ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
- }
- return ret;
-}
-
-
-#ifdef HAVE_GETIFADDRS
-
-#include
-static int get_local_ip_with_getifaddrs(int type, char *address, int size)
-{
- struct ifaddrs *ifp;
- struct ifaddrs *ifpstart;
- int ret = 0;
-
- if (getifaddrs(&ifpstart) < 0) {
- return -1;
- }
-#ifndef __linux
- #define UP_FLAG IFF_UP /* interface is up */
-#else
- #define UP_FLAG IFF_RUNNING /* resources allocated */
-#endif
-
- for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
- if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
- && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
- {
- if(getnameinfo(ifp->ifa_addr,
- (type == AF_INET6) ?
- sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
- address, size, NULL, 0, NI_NUMERICHOST) == 0) {
- if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
- /*ms_message("getifaddrs() found %s",address);*/
- ret++;
- break;
- }
- }
- }
- }
- freeifaddrs(ifpstart);
- return ret;
-}
-#endif
-
-
-static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
- int err,tmp;
- struct addrinfo hints;
- struct addrinfo *res=NULL;
- struct sockaddr_storage addr;
- struct sockaddr *p_addr=(struct sockaddr*)&addr;
- ortp_socket_t sock;
- socklen_t s;
-
- memset(&hints,0,sizeof(hints));
- hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
- hints.ai_socktype=SOCK_DGRAM;
- /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
- err=getaddrinfo(dest,"5060",&hints,&res);
- if (err!=0){
- ms_error("getaddrinfo() error: %s",gai_strerror(err));
- return -1;
- }
- if (res==NULL){
- ms_error("bug: getaddrinfo returned nothing.");
- return -1;
- }
- sock=socket(res->ai_family,SOCK_DGRAM,0);
- tmp=1;
- err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
- if (err<0){
- ms_warning("Error in setsockopt: %s",strerror(errno));
- }
- err=connect(sock,res->ai_addr,res->ai_addrlen);
- if (err<0) {
- ms_error("Error in connect: %s",strerror(errno));
- freeaddrinfo(res);
- close_socket(sock);
- return -1;
- }
- freeaddrinfo(res);
- res=NULL;
- s=sizeof(addr);
- err=getsockname(sock,(struct sockaddr*)&addr,&s);
- if (err!=0) {
- ms_error("Error in getsockname: %s",strerror(errno));
- close_socket(sock);
- return -1;
- }
- if (p_addr->sa_family==AF_INET){
- struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
- if (p_sin->sin_addr.s_addr==0){
- close_socket(sock);
- return -1;
- }
- }
- err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
- if (err!=0){
- ms_error("getnameinfo error: %s",strerror(errno));
- }
- close_socket(sock);
- ms_message("Local interface to reach %s is %s.",dest,result);
- return 0;
-}
-
-int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
- strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
-#ifdef HAVE_GETIFADDRS
- if (dest==NULL) {
- /*we use getifaddrs for lookup of default interface */
- int found_ifs;
-
- found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
- if (found_ifs==1){
- return 0;
- }else if (found_ifs<=0){
- /*absolutely no network on this machine */
- return -1;
- }
- }
-#endif
- /*else use connect to find the best local ip address */
- if (type==AF_INET)
- dest="87.98.157.38"; /*a public IP address*/
- else dest="2a00:1450:8002::68";
- return get_local_ip_for_with_connect(type,dest,result);
-}
-
-#ifndef WIN32
-#include
-
-
-
-void _linphone_core_configure_resolver(){
-/*bionic declares _res but does not define nor export it !!*/
-#ifdef ANDROID
- /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
- setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
-#else
- res_init();
- _res.retrans=2; /*retransmit every two seconds*/
- _res.retry=2; /*only two times per DNS server*/
-#endif
-}
-
-#else
-
-void _linphone_core_configure_resolver(){
-}
-
-#endif
+
+/*
+linphone
+Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.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/mediastream.h"
+#include
+#include
+#ifdef HAVE_SIGHANDLER_T
+#include
+#endif /*HAVE_SIGHANDLER_T*/
+
+#include
+#if !defined(_WIN32_WCE)
+#include