Merge remote-tracking branch 'linphone/master'

Conflicts:
	.gitignore
	Makefile.am
	mediastreamer2
	oRTP
This commit is contained in:
Guillaume BIENKOWSKI 2013-12-10 15:58:24 +01:00
commit 354b234f9b
252 changed files with 33813 additions and 7150 deletions

View file

@ -22,7 +22,7 @@
<folderInfo id="0.2079208171." name="/" resourcePath="">
<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.2084203071" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
<targetPlatform binaryParser="org.eclipse.cdt.core.MachO64;org.eclipse.cdt.core.ELF" id="org.eclipse.cdt.build.core.prefbase.toolchain.2084203071.81924294" name=""/>
<builder arguments="CFLAGS=&quot;-g -Werror -Wall&quot; CXXFLAGS=&quot;-g&quot;" command="make" id="org.eclipse.cdt.build.core.settings.default.builder.731584538" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
<builder arguments="-j4 CFLAGS=&quot;-g -Wall -Qunused-arguments&quot; CXXFLAGS=&quot;-g&quot;" command="make" id="org.eclipse.cdt.build.core.settings.default.builder.731584538" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
<tool id="org.eclipse.cdt.build.core.settings.holder.libs.1252970003" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
<tool id="org.eclipse.cdt.build.core.settings.holder.1371414073" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.306286573" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
@ -35,9 +35,12 @@
</tool>
</toolChain>
</folderInfo>
<fileInfo id="0.2079208171.129489708" name="mediastreamer-config.h" rcbsApplicability="disable" resourcePath="mediastreamer2/mediastreamer-config.h" toolsToInvoke="org.eclipse.cdt.build.core.settings.holder.1371414073.1809017452">
<tool id="org.eclipse.cdt.build.core.settings.holder.1371414073.1809017452" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder.1371414073"/>
</fileInfo>
<fileInfo id="0.2079208171.2090246372" name="ringback.wav" rcbsApplicability="disable" resourcePath="tester/sounds/ringback.wav" toolsToInvoke=""/>
<sourceEntries>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="coreapi"/>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="gtk"/>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="tester"/>
</sourceEntries>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
@ -48,8 +51,10 @@
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="linphone.null.1149313048" name="linphone"/>
</storageModule>
<storageModule moduleId="refreshScope" versionNumber="1">
<resource resourceType="PROJECT" workspacePath="/linphone"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Default">
<resource resourceType="PROJECT" workspacePath="/linphone"/>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
<buildTargets>
@ -198,4 +203,6 @@
</profile>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cproject>

16
.gitignore vendored
View file

@ -52,14 +52,24 @@ coreapi/help/registration
coreapi/test_ecc
coreapi/test_lsd
gtk/version_date.h
daemon/linphone-daemon
daemon/linphone-daemon-pipetest
tools/lpc2xml_test
tools/xml2lpc_test
*.la
*.lo
*.deps
*.libs
share/certdata.txt
coreapi/test_numbers
specs.c
*.orig
*.rej
*.kdev4
*.swp
.deps
.libs
coreapi/help/notify
share/fresh-rootca.pem
tester/liblinphone_tester
tools/lp-gen-wrappers
tools/lpc2xml_test
tools/xml2lpc_test

View file

@ -23,7 +23,7 @@
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value>CFLAGS=&quot;-g -Werror -Wall&quot; CXXFLAGS=&quot;-g&quot;</value>
<value>CFLAGS=&quot;-g -Werror -Wall&quot; CXXFLAGS=&quot;-g&quot; V=1</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>

12
COPYING
View file

@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest

View file

@ -4,8 +4,7 @@
ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS)
SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \
coreapi console gtk share scripts tools daemon
coreapi console gtk share scripts tools daemon tester include
ACLOCAL_FLAGS=-I$(top_srcdir)/m4
@ -37,7 +36,7 @@ SDK_EXCLUDED= \
GTK_PREFIX=/
GTK_THEME=Outcrop
GTK_FILELIST=gtk+-2.22.1.filelist
GTK_FILELIST=gtk+-2.24.8.filelist
GTK_FILELIST_PATH=$(abs_top_srcdir)/$(GTK_FILELIST)
LINPHONEDEPS_FILELIST=linphone-deps.filelist
WINBINDIST_FILES=`cat $(abs_top_srcdir)/$(LINPHONEDEPS_FILELIST)`
@ -101,31 +100,33 @@ endif
other-cherrypick:
cd $(GTK_PREFIX) && \
for file in $(WINBINDIST_FILES) ; do \
if test -d $$file; then \
if test -d $(prefix)/$$file; then \
$(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\
else \
cp $$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\
cp $(prefix)/$$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\
fi \
done
cp /mingw/bin/libgcc_s*.dll \
/mingw/bin/libstdc++-6.dll \
/mingw/bin/libintl-8.dll \
/mingw/bin/libiconv-2.dll \
/mingw/bin/pthreadGC2.dll \
$(INSTALLDIR_WITH_PREFIX)/bin/.
if test -d /mingw/bin ; then \
cp /mingw/bin/libgcc_s*.dll \
/mingw/bin/libstdc++-6.dll \
/mingw/bin/libintl-8.dll \
/mingw/bin/libiconv-2.dll \
/mingw/bin/pthreadGC2.dll \
$(INSTALLDIR_WITH_PREFIX)/bin/. ;\
fi
gtk-cherrypick:
cd $(GTK_PREFIX) && \
for file in `cat $(GTK_FILELIST_PATH)` ; do \
if test -d $$file; then \
if test -d $(prefix)/$$file; then \
$(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\
else \
cp $$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\
cp $(prefix)/$$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\
fi \
done && \
$(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/share/themes && \
cp -rf share/themes/$(GTK_THEME) $(INSTALLDIR_WITH_PREFIX)/share/themes/.
cp -rf $(prefix)/share/themes/$(GTK_THEME) $(INSTALLDIR_WITH_PREFIX)/share/themes/.
zip:
rm -f $(ZIPFILE)
@ -230,6 +231,6 @@ bundle: $(LIBICONV_HACK)
clean-local:
rm -rf $(BUNDLEDIR)
discovery:
touch specs.cpp
touch specs.c
$(CC) --include $(top_builddir)/config.h \
$(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) -E -P -v -dD specs.cpp
$(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(CUNIT_CFLAGS) -E -P -v -dD specs.c

14
NEWS
View file

@ -1,3 +1,17 @@
linphone-3.7...??
Liblinphone level improvements thanks to belle-sip new SIP stack:
* multiple SIP transports simualtaneously now allowed
* IP dual stack: can use IPv6 and IPv4 simultaneously
* fully asynchronous behavior: no more lengthly DNS or connections
* +sip.instance parameter (RFC5626)
* alias parameter (RFC5923)
* better management of network disconnections
* SIP/TLS handled through lightweighted polarssl library (instead of openssl)
* SIP transaction state machines improved (RFC6026)
* Privacy API (RFC3323, RFC3325)
* Full support of rich presence in (RFC4480)
linphone-3.6.1 -- June 17, 2013
* fix memory leak with some video cameras on windows.

10
README
View file

@ -8,9 +8,9 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol.
- intltool
- you need at least:
- libosip2>=3.5.0
- libeXosip2>=3.5.0
- belle-sip>=1.0.0
- speex>=1.2.0 (including libspeexdsp part)
- libxml2
+ if you want the gtk/glade interface:
- libgtk >=2.16.0
@ -33,11 +33,11 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol.
Here is the command line to get these dependencies installed for Ubuntu && Debian
$ sudo apt-get install libtool intltool libgtk2.0-dev libosip2-dev libexosip2-dev libspeexdsp-dev libavcodec-dev libswscale-dev libx11-dev libvxl1-dev libgl1-mesa-dev libglew1.6-dev libv4l-dev
$ sudo apt-get install libtool intltool libgtk2.0-dev libosip2-dev libexosip2-dev libspeexdsp-dev libavcodec-dev libswscale-dev libx11-dev libxv-dev libgl1-mesa-dev libglew1.6-dev libv4l-dev libxml2-dev
+ for optional library
$ sudo apt-get install libreadline-dev libgsm1-dev libtheora-dev libsoup2.4-dev libsqlite3-dev libupnp4-dev
$ sudo apt-get install libreadline-dev libgsm1-dev libtheora-dev libsoup2.4-dev libsqlite3-dev libupnp4-dev
+ Install srtp (optional) for call encryption :
$ git clone git://git.linphone.org/srtp.git
$ cd srtp && autoconf && ./configure && make

View file

@ -7,17 +7,17 @@ You need:
- Macports: http://www.macports.org/
Download and install macports using its user friendly installer.
- In order to enable generation of bundle for multiple macos version it is recommended to edit /opt/local/etc/macports/macports.conf to add the
following line:
macosx_deployment_target 10.6
- In order to enable generation of bundle for multiple macos version and 32 bit processors, it is recommended to:
1) edit /opt/local/etc/macports/macports.conf to add the following line:
macosx_deployment_target 10.6
2) edit /opt/local/etc/macports/variants.conf to add the following line:
+universal
- Install build time dependencies
$ sudo port install automake autoconf libtool intltool
- Install some linphone dependencies with macports
$ sudo port install speex
$ sudo port install libosip2 # WARNING: currently outdated in macport
$ sudo port install libeXosip2 #WARNING: currently outdated in macport
$ sudo port install ffmpeg-devel -gpl2
$ sudo port install libvpx
$ sudo port install readline
@ -36,6 +36,22 @@ You need:
The softwares below need to be compiled manually. To ensure compatibility with multiple mac os version it is recommended to do:
$ export MACOSX_DEPLOYMENT_TARGET=10.6
$ export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5"
$ export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5"
$ export CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5"
$ export LDFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5"
- Install polarssl (encryption library used by belle-sip)
$ git clone git://git.linphone.org/polarssl.git -b linphone
$ cd polarssl
$ ./autogen.sh && ./configure --prefix=/opt/local && make
$ sudo make install
- Install belle-sip (sip stack)
$ git clone git://git.linphone.org/belle-sip.git
$ cd belle-sip
$ ./autogen.sh && ./configure --prefix=/opt/local && make
$ sudo make install
- Install srtp (optional) for call encryption
$ sudo port install srtp
@ -72,7 +88,7 @@ The softwares below need to be compiled manually. To ensure compatibility with m
Then or otherwise, do:
$ ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp && make
$ ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make
Install to /opt/local

View file

@ -26,7 +26,7 @@ Download lastest linphone-deps-win32 zip from
http://download.savannah.gnu.org/releases-noredirect/linphone/misc
using your browser.
Download lastest gtk+2 win32 bundle from http://www.gtk.org
Download lastest gtk+-2.24.10 win32 _bundle_ from http://www.gtk.org
Install all these three package in /:
@ -65,23 +65,19 @@ WARNING: During the build, windows might slow down suddenly. Using ctl+alt+del t
you might see a process 'LVpSRV.exe' or something like this that eats 90% of cpu.
Kill it. Don't know what it is, but once killed, windows runs normally.
#Compile and install tunnel (optional, available under proprietary licensing)
cd tunnel && ./autogen.sh && ./configure --prefix=/usr --enable-shared --disable-static && make && make install
#Build linphone itself:
#run autogen.sh after a git checkout or update
./autogen.sh
./configure --prefix=/opt/linphone --enable-shared --disable-static
./configure --prefix=/usr --enable-shared --disable-static
#note: in order to use the tunnel, append --enable-tunnel to the configure line above.
#compile:
make
#now install to /opt/linphone, required for compilation of plugins.
#now install to /usr, required for compilation of plugins.
make install
@ -100,7 +96,7 @@ make setup.exe
#build plugins
cd mediastreamer2/plugins/msx264
./autogen.sh
PKG_CONFIG_PATH=/opt/linphone/lib/pkgconfig ./configure --prefix=/opt/linphone --enable-shared --disable-static
PKG_CONFIG_PATH=/usr/lib/pkgconfig ./configure --prefix=/usr --enable-shared --disable-static
#make a binary zip of this plugin
make zip
#or make an installer
@ -109,7 +105,7 @@ make setup.exe
#the buddylookup plugin enables lookup of buddies in a remote database using xml-rpc over http/https.
cd coreapi/plugins/buddylookup
./autogen.sh
PKG_CONFIG_PATH=/opt/linphone/lib/pkgconfig ./configure --prefix=/opt/linphone --enable-shared --disable-static
PKG_CONFIG_PATH=/usr/lib/pkgconfig ./configure --prefix=/usr --enable-shared --disable-static
make
#make a binary zip of this plugin
make zip
@ -125,13 +121,15 @@ These notes are useful if you want to upgrade part of the software that is inclu
linphone-deps packages.
List of software included in linphone-deps:
libosip2 (compiled)
libeXosip2 (compiled)
antlr3c (compiled)
polarssl (compiled
belle-sip (compiled)
libsrtp (compiled)
libavcodec, libavutil, libavformat, libavdevice, libswscale (compiled, all these from ffmpeg)
libtheora (from the web)
libx264 (compiled from the version distributed from linphone's web site)
libogg (from the web)
libspeex, libspeexdsp (compiled, statically to workaround a dll-related crash)
libspeex, libspeexdsp (compiled)
libgnutls (from the web)
libgsm (from the web)
libxml2 (compiled)
@ -142,6 +140,45 @@ Remarks:
For every package compiled that goes into linphone-deps, .la files (libtool files) must be removed to avoid libtool errors.
When running "make install DESTDIR=<somepath>", somepath must be absolute and should not contain any ~ or space.
- building antlr3c
* download the sources with:
$ git clone -b linphone git://git.linphone.org/antlr3.git
* compile and install
$ cd runtime/C
$ ./autogen.sh
$ ./configure --prefix=/usr --enable-shared --disable-static
$ make
$ make install
$ make install DESTDIR=/home/<myuser>/antlr3c-install
$ cp
- building polarssl
* download the sources with:
$ git clone -b linphone git://git.linphone.org/polarssl.git
* compile and install:
$ cd polarssl
$ make lib SHARED=1 WINDOWS=1
$ make install DESTDIR=/usr
$ make install DESTDIR=/home/<myuser>/polarssl-install
- building belle-sip
* download the sources with:
$ git clone git://git.linphone.org/belle-sip.git
* compile and install, assuming you have already compiled polarssl and antlr3c and installed in /.
$ ./autogen.sh
$ ./configure --prefix=/usr --enable-shared --disable-static
$ make && make install && make install DESTDIR=/home/<myuser>/belle-sip-install
- building libsrtp
* download the sources with
$ git clone git://git.linphone.org/srtp.git
* compile with
$ autoconf
$ ./configure --prefix=/usr
$ make libsrtp.a
$ make install
$ make install DESTDIR=/home/<myuser>/libsrtp-install
- building sqlite3
* download the sources on the following website:
http://www.sqlite.org/download.html (choose the sqlite-autoconf-3XXX.tar.gz)

View file

@ -1,50 +0,0 @@
##
## Android.mk -Android build script-
##
##
## Copyright (C) 2010 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.
##
LOCAL_PATH:= $(call my-dir)/../../coreapi
include $(CLEAR_VARS)
include $(linphone-root-dir)/submodules/linphone/build/android/common.mk
ifeq ($(LINPHONE_VIDEO),1)
LOCAL_SHARED_LIBRARIES += \
libavcodecnoneon \
libswscale \
libavcore \
libavutil
endif
LOCAL_MODULE := liblinphonenoneon
ifeq ($(TARGET_ARCH_ABI),armeabi)
LOCAL_MODULE_FILENAME := liblinphonearmv5noneon
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_MODULE_FILENAME := liblinphonearmv7noneon
endif
ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_MODULE_FILENAME := liblinphonex86
endif
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/cpufeatures)

View file

@ -23,20 +23,215 @@ LOCAL_PATH:= $(call my-dir)/../../coreapi
include $(CLEAR_VARS)
include $(linphone-root-dir)/submodules/linphone/build/android/common.mk
LOCAL_CPP_EXTENSION := .cc
ifeq ($(LINPHONE_VIDEO),1)
LOCAL_SRC_FILES := \
linphonecore.c \
misc.c \
enum.c \
presence.c \
proxy.c \
friend.c \
authentication.c \
lpconfig.c \
chat.c \
sipsetup.c \
siplogin.c \
address.c \
linphonecore_jni.cc \
bellesip_sal/sal_address_impl.c \
bellesip_sal/sal_impl.c \
bellesip_sal/sal_op_call.c \
bellesip_sal/sal_op_call_transfer.c \
bellesip_sal/sal_op_impl.c \
bellesip_sal/sal_op_message.c \
bellesip_sal/sal_op_presence.c \
bellesip_sal/sal_op_registration.c \
bellesip_sal/sal_op_publish.c \
bellesip_sal/sal_op_info.c \
bellesip_sal/sal_op_events.c \
bellesip_sal/sal_sdp.c \
sal.c \
offeranswer.c \
callbacks.c \
linphonecall.c \
conference.c \
ec-calibrator.c \
linphone_tunnel_config.c \
message_storage.c \
info.c \
event.c
ifndef LINPHONE_VERSION
LINPHONE_VERSION = "Devel"
endif
LOCAL_CFLAGS += \
-D_BYTE_ORDER=_LITTLE_ENDIAN \
-DORTP_INET6 \
-DINET6 \
-DENABLE_TRACE \
-DHAVE_CONFIG_H \
-DLINPHONE_VERSION=\"$(LINPHONE_VERSION)\" \
-DLINPHONE_PLUGINS_DIR=\"\\tmp\" \
-DUSE_BELLESIP
LOCAL_CFLAGS += -DIN_LINPHONE
ifeq ($(_BUILD_VIDEO),1)
LOCAL_CFLAGS += -DVIDEO_ENABLED
ifeq ($(BUILD_X264),1)
LOCAL_CFLAGS += -DHAVE_X264
endif
endif
ifeq ($(BUILD_CONTACT_HEADER),1)
LOCAL_CFLAGS += -DSAL_OP_CALL_FORCE_CONTACT_IN_RINGING
endif
ifeq ($(USE_JAVAH),1)
LOCAL_CFLAGS += -DUSE_JAVAH
endif
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../build/android \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include \
$(LOCAL_PATH)/../../belle-sip/include \
$(LOCAL_PATH)/../../../gen \
$(LOCAL_PATH)/../../externals/libxml2/include \
$(LOCAL_PATH)/../../externals/build/libxml2
LOCAL_LDLIBS += -llog -ldl
LOCAL_STATIC_LIBRARIES := \
cpufeatures \
libmediastreamer2 \
libortp \
libbellesip \
libgsm \
liblpxml2
ifeq ($(BUILD_TUNNEL),1)
LOCAL_CFLAGS +=-DTUNNEL_ENABLED
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include $(LOCAL_PATH)/../../tunnel/src
LOCAL_SRC_FILES += linphone_tunnel.cc TunnelManager.cc
LOCAL_STATIC_LIBRARIES += libtunnelclient
else
LOCAL_SRC_FILES += linphone_tunnel_stubs.c
endif
_BUILD_AMR=0
ifneq ($(BUILD_AMRNB), 0)
_BUILD_AMR=1
endif
ifneq ($(BUILD_AMRWB), 0)
_BUILD_AMR=1
endif
ifneq ($(_BUILD_AMR), 0)
LOCAL_CFLAGS += -DHAVE_AMR
LOCAL_STATIC_LIBRARIES += \
libmsamr \
libopencoreamr
endif
ifneq ($(BUILD_AMRWB), 0)
LOCAL_STATIC_LIBRARIES += \
libvoamrwbenc
endif
ifeq ($(BUILD_SILK),1)
LOCAL_CFLAGS += -DHAVE_SILK
LOCAL_STATIC_LIBRARIES += libmssilk
endif
ifeq ($(BUILD_WEBRTC_ISAC),1)
LOCAL_CFLAGS += -DHAVE_ISAC
LOCAL_STATIC_LIBRARIES += libwebrtc_isacfix_neon
LOCAL_STATIC_LIBRARIES += libwebrtc_spl libwebrtc_isacfix libmsisac
endif
ifeq ($(BUILD_G729),1)
LOCAL_CFLAGS += -DHAVE_G729
LOCAL_STATIC_LIBRARIES += libbcg729 libmsbcg729
endif
ifeq ($(_BUILD_VIDEO),1)
LOCAL_LDLIBS += -lGLESv2
LOCAL_STATIC_LIBRARIES += libvpx
ifeq ($(BUILD_X264),1)
LOCAL_STATIC_LIBRARIES += \
libmsx264 \
libx264
endif
endif
ifeq ($(BUILD_UPNP),1)
LOCAL_CFLAGS += -DBUILD_UPNP
LOCAL_SRC_FILES += upnp.c
endif
LOCAL_STATIC_LIBRARIES += libspeex
ifeq ($(BUILD_SRTP), 1)
LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE)
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS += -DHAVE_ILBC=1
LOCAL_STATIC_LIBRARIES += libmsilbc
endif
LOCAL_C_INCLUDES += $(LIBLINPHONE_EXTENDED_C_INCLUDES)
LOCAL_WHOLE_STATIC_LIBRARIES += $(LIBLINPHONE_EXTENDED_STATIC_LIBS)
LOCAL_SRC_FILES += $(LIBLINPHONE_EXTENDED_SRC_FILES)
ifeq ($(BUILD_GPLV3_ZRTP),1)
LOCAL_SHARED_LIBRARIES += libssl-linphone libcrypto-linphone
LOCAL_SHARED_LIBRARIES += libzrtpcpp
endif
ifeq ($(BUILD_SRTP),1)
LOCAL_SHARED_LIBRARIES += libsrtp
endif
ifeq ($(BUILD_REMOTE_PROVISIONING),1)
LOCAL_SRC_FILES += ../tools/xml2lpc.c \
../tools/xml2lpc_jni.cc \
../tools/lpc2xml.c \
../tools/lpc2xml_jni.cc
endif
ifeq ($(BUILD_SQLITE),1)
LOCAL_CFLAGS += -DMSG_STORAGE_ENABLED
LOCAL_STATIC_LIBRARIES += liblinsqlite
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../../externals/sqlite3/
endif
ifeq ($(BUILD_OPUS),1)
LOCAL_STATIC_LIBRARIES += libopus
endif
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_EXPORT_CFLAGS := $(LOCAL_CFLAGS)
ifeq ($(_BUILD_VIDEO),1)
LOCAL_SHARED_LIBRARIES += \
libavcodec \
libswscale \
libavcore \
libavutil
libavcodec-linphone \
libswscale-linphone \
libavutil-linphone
endif
LOCAL_MODULE := liblinphone
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_MODULE_FILENAME := liblinphonearmv7
endif
LOCAL_MODULE_FILENAME := liblinphone-$(TARGET_ARCH_ABI)
include $(BUILD_SHARED_LIBRARY)

View file

@ -1,205 +0,0 @@
##
## Android.mk -Android build script-
##
##
## Copyright (C) 2010 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.
##
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
linphonecore.c \
misc.c \
enum.c \
presence.c \
proxy.c \
friend.c \
authentication.c \
lpconfig.c \
chat.c \
sipsetup.c \
siplogin.c \
address.c \
linphonecore_jni.cc \
sal.c \
sal_eXosip2.c \
sal_eXosip2_presence.c \
sal_eXosip2_sdp.c \
offeranswer.c \
callbacks.c \
linphonecall.c \
conference.c \
ec-calibrator.c \
linphone_tunnel_config.c \
message_storage.c
ifndef LINPHONE_VERSION
LINPHONE_VERSION = "Devel"
endif
LOCAL_CFLAGS += \
-D_BYTE_ORDER=_LITTLE_ENDIAN \
-DORTP_INET6 \
-DINET6 \
-DOSIP_MT \
-DENABLE_TRACE \
-DHAVE_CONFIG_H \
-DLINPHONE_VERSION=\"$(LINPHONE_VERSION)\" \
-DLINPHONE_PLUGINS_DIR=\"\\tmp\" \
LOCAL_CFLAGS += -DIN_LINPHONE
ifeq ($(LINPHONE_VIDEO),1)
LOCAL_CFLAGS += -DVIDEO_ENABLED
ifeq ($(BUILD_X264),1)
LOCAL_CFLAGS += -DHAVE_X264
endif
endif
ifeq ($(USE_JAVAH),1)
LOCAL_CFLAGS += -DUSE_JAVAH
endif
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/../build/android \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include \
$(LOCAL_PATH)/../../externals/exosip/include \
$(LOCAL_PATH)/../../externals/osip/include \
$(LOCAL_PATH)/../../../gen
LOCAL_LDLIBS += -llog -ldl
LOCAL_STATIC_LIBRARIES := \
cpufeatures \
libmediastreamer2 \
libortp \
libeXosip2 \
libosip2 \
libgsm
ifeq ($(BUILD_REMOTE_PROVISIONING),1)
LOCAL_STATIC_LIBRARIES += \
libxml2lpc \
liblpc2xml \
liblpxml2
endif
ifeq ($(BUILD_TUNNEL),1)
LOCAL_CFLAGS +=-DTUNNEL_ENABLED
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include $(LOCAL_PATH)/../../tunnel/src
LOCAL_SRC_FILES += linphone_tunnel.cc TunnelManager.cc
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SHARED_LIBRARIES += libtunnelclient
else
LOCAL_STATIC_LIBRARIES += libtunnelclient
endif
else
LOCAL_SRC_FILES += linphone_tunnel_stubs.c
endif
_BUILD_AMR=0
ifneq ($(BUILD_AMRNB), 0)
_BUILD_AMR=1
endif
ifneq ($(BUILD_AMRWB), 0)
_BUILD_AMR=1
endif
ifneq ($(_BUILD_AMR), 0)
LOCAL_CFLAGS += -DHAVE_AMR
LOCAL_STATIC_LIBRARIES += \
libmsamr \
libopencoreamr
endif
ifneq ($(BUILD_AMRWB), 0)
LOCAL_STATIC_LIBRARIES += \
libvoamrwbenc
endif
ifeq ($(BUILD_SILK),1)
LOCAL_CFLAGS += -DHAVE_SILK
LOCAL_STATIC_LIBRARIES += libmssilk
endif
ifeq ($(BUILD_G729),1)
LOCAL_CFLAGS += -DHAVE_G729
LOCAL_SHARED_LIBRARIES += libbcg729
LOCAL_STATIC_LIBRARIES += libmsbcg729
endif
ifeq ($(LINPHONE_VIDEO),1)
LOCAL_LDLIBS += -lGLESv2
LOCAL_STATIC_LIBRARIES += libvpx
ifeq ($(BUILD_X264),1)
LOCAL_STATIC_LIBRARIES += \
libmsx264 \
libx264
endif
endif
ifeq ($(BUILD_UPNP),1)
LOCAL_CFLAGS += -DBUILD_UPNP
LOCAL_SRC_FILES += upnp.c
endif
LOCAL_STATIC_LIBRARIES += libspeex
ifeq ($(BUILD_SRTP), 1)
LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE)
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS += -DHAVE_ILBC=1
LOCAL_STATIC_LIBRARIES += libmsilbc
endif
LOCAL_C_INCLUDES += $(LIBLINPHONE_EXTENDED_C_INCLUDES)
LOCAL_WHOLE_STATIC_LIBRARIES += $(LIBLINPHONE_EXTENDED_STATIC_LIBS)
LOCAL_SRC_FILES += $(LIBLINPHONE_EXTENDED_SRC_FILES)
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SHARED_LIBRARIES += liblinssl liblincrypto
ifeq ($(BUILD_GPLV3_ZRTP),1)
LOCAL_SHARED_LIBRARIES += libzrtpcpp
endif
ifeq ($(BUILD_SRTP),1)
LOCAL_SHARED_LIBRARIES += libsrtp
endif
else
LOCAL_LDLIBS += -lz
#LOCAL_STATIC_LIBRARIES += libz libdl
LOCAL_STATIC_LIBRARIES += \
libssl-static libcrypto-static
ifeq ($(BUILD_GPLV3_ZRTP),1)
LOCAL_STATIC_LIBRARIES += libzrtpcpp-static
endif
ifeq ($(BUILD_SRTP),1)
LOCAL_STATIC_LIBRARIES += libsrtp-static
endif
endif

View file

@ -38,19 +38,19 @@
/* #def HAVE_EXOSIP_DSCP */
/* Defined when eXosip_get_version is available */
#define HAVE_EXOSIP_GET_VERSION
/* #undef HAVE_EXOSIP_GET_VERSION */
/* Defined when eXosip_reset_transports is available */
#define HAVE_EXOSIP_RESET_TRANSPORTS
/* #undef HAVE_EXOSIP_RESET_TRANSPORTS */
/* Defined when eXosip_tls_verify_certificate is available */
#define HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE
/* #undef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE */
/* Defined when eXosip_tls_verify_certificate is available */
/* #undef HAVE_EXOSIP_TLS_VERIFY_CN */
/* Defined when eXosip_get_socket is available */
#define HAVE_EXOSIP_TRYLOCK
/* #undef HAVE_EXOSIP_TRYLOCK */
/* If present, the getenv function allows fim to read environment variables.
*/

View file

@ -0,0 +1,33 @@
LOCAL_PATH := $(call my-dir)/../../tester
common_SRC_FILES := \
call_tester.c \
liblinphone_tester.c \
message_tester.c \
presence_tester.c \
register_tester.c \
setup_tester.c \
upnp_tester.c \
eventapi_tester.c
common_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../coreapi \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include
include $(CLEAR_VARS)
LOCAL_MODULE := liblinphone_tester
LOCAL_MODULE_FILENAME := liblinphone_tester-$(TARGET_ARCH_ABI)
LOCAL_SRC_FILES += $(common_SRC_FILES)
LOCAL_C_INCLUDES = $(common_C_INCLUDES)
LOCAL_CFLAGS = -DIN_LINPHONE
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := cunit liblinphone
include $(BUILD_SHARED_LIBRARY)
#end

View file

@ -1,47 +0,0 @@
##
## Android.mk -Android build script-
##
##
## Copyright (C) 2013 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.
##
LOCAL_PATH:= $(call my-dir)/../../tools
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
lpc2xml.c \
lpc2xml_jni.cc \
LOCAL_CFLAGS += -DIN_LINPHONE
LOCAL_C_INCLUDES = \
$(LOCAL_PATH)/../coreapi \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include \
$(LOCAL_PATH)/../../externals/libxml2/include \
$(LOCAL_PATH)/../../externals/build/libxml2 \
LOCAL_SHARED_LIBRARIES = \
# liblinphonenoneon \
# liblinphone \
LOCAL_MODULE := liblpc2xml
include $(BUILD_STATIC_LIBRARY)

View file

@ -1,47 +0,0 @@
##
## Android.mk -Android build script-
##
##
## Copyright (C) 2013 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.
##
LOCAL_PATH:= $(call my-dir)/../../tools
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := \
xml2lpc.c \
xml2lpc_jni.cc \
LOCAL_CFLAGS += -DIN_LINPHONE
LOCAL_C_INCLUDES = \
$(LOCAL_PATH)/../coreapi \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include \
$(LOCAL_PATH)/../../externals/libxml2/include \
$(LOCAL_PATH)/../../externals/build/libxml2 \
LOCAL_SHARED_LIBRARIES = \
# liblinphonenoneon \
# liblinphone \
LOCAL_MODULE := libxml2lpc
include $(BUILD_STATIC_LIBRARY)

View file

@ -15,7 +15,8 @@
<prefix name="default">/opt/local</prefix>
<prefix name="linphone">${env:LINPHONE_INSTALL_PREFIX}</prefix>
<prefix name="ms2plugins">${env:MS2_PLUGINS_INSTALL_PREFIX}</prefix>
<prefix name="macports">/opt/local</prefix>
<!-- This prefix definition is necessary if some dependencies are to be taken from /usr/local/lib -->
<prefix name="local">/usr/local</prefix>
<!-- The project directory is the default location of the created
app. If you leave out the path, the current directory is
used. Note the usage of an environment variable here again.
@ -55,11 +56,11 @@
<main-binary>${prefix:linphone}/bin/linphone</main-binary>
<binary >
${prefix:ms2plugins}/lib/mediastreamer/plugins/*.so
${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so
</binary>
<binary >
${prefix:linphone}/lib/*.dylib
${prefix:linphone}/lib/*.*.dylib
</binary>
<!-- Copy in GTK+ modules. Note the ${gtkdir} macro, which expands

View file

@ -0,0 +1,274 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{08dd0d38-d9b5-4626-b60d-b4d76b571142}</ProjectGuid>
<RootNamespace>LibLinphone</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="";UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>belle-sip_dll.lib;mediastreamer2_dll.lib;ws2_32.lib;ortp_dll.lib;gsm_dll.lib;speex_dll.lib;speexdsp_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
</Link>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="";UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
<AdditionalDependencies>belle-sip_dll.lib;mediastreamer2_dll.lib;ws2_32.lib;ortp_dll.lib;gsm_dll.lib;speex_dll.lib;speexdsp_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
</Link>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="";UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<PreprocessToFile>false</PreprocessToFile>
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
<ExceptionHandling>SyncCThrow</ExceptionHandling>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
<IgnoreSpecificDefaultLibraries>ole32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<WindowsMetadataFile>
</WindowsMetadataFile>
</Link>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR="";UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
<AdditionalDependencies>belle-sip_dll.lib;mediastreamer2_dll.lib;ws2_32.lib;ortp_dll.lib;gsm_dll.lib;speex_dll.lib;speexdsp_dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
</Link>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\coreapi\address.c" />
<ClCompile Include="..\..\..\coreapi\authentication.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_address_impl.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_impl.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_call.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_call_transfer.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_events.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_impl.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_info.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_message.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_presence.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_publish.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_op_registration.c" />
<ClCompile Include="..\..\..\coreapi\bellesip_sal\sal_sdp.c" />
<ClCompile Include="..\..\..\coreapi\callbacks.c" />
<ClCompile Include="..\..\..\coreapi\chat.c" />
<ClCompile Include="..\..\..\coreapi\conference.c" />
<ClCompile Include="..\..\..\coreapi\ec-calibrator.c" />
<ClCompile Include="..\..\..\coreapi\enum.c" />
<ClCompile Include="..\..\..\coreapi\event.c" />
<ClCompile Include="..\..\..\coreapi\friend.c" />
<ClCompile Include="..\..\..\coreapi\info.c" />
<ClCompile Include="..\..\..\coreapi\linphonecall.c" />
<ClCompile Include="..\..\..\coreapi\linphonecore.c" />
<ClCompile Include="..\..\..\coreapi\linphone_tunnel.cc" />
<ClCompile Include="..\..\..\coreapi\linphone_tunnel_config.c" />
<ClCompile Include="..\..\..\coreapi\lpconfig.c" />
<ClCompile Include="..\..\..\coreapi\lsd.c" />
<ClCompile Include="..\..\..\coreapi\message_storage.c" />
<ClCompile Include="..\..\..\coreapi\misc.c" />
<ClCompile Include="..\..\..\coreapi\offeranswer.c" />
<ClCompile Include="..\..\..\coreapi\presence.c" />
<ClCompile Include="..\..\..\coreapi\proxy.c" />
<ClCompile Include="..\..\..\coreapi\sal.c" />
<ClCompile Include="..\..\..\coreapi\siplogin.c" />
<ClCompile Include="..\..\..\coreapi\sipsetup.c" />
<ClCompile Include="..\..\..\coreapi\TunnelManager.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\coreapi\bellesip_sal\sal_impl.h" />
<ClInclude Include="..\..\..\coreapi\enum.h" />
<ClInclude Include="..\..\..\coreapi\event.h" />
<ClInclude Include="..\..\..\coreapi\linphonecore.h" />
<ClInclude Include="..\..\..\coreapi\linphonecore_utils.h" />
<ClInclude Include="..\..\..\coreapi\linphonefriend.h" />
<ClInclude Include="..\..\..\coreapi\linphone_tunnel.h" />
<ClInclude Include="..\..\..\coreapi\lpconfig.h" />
<ClInclude Include="..\..\..\coreapi\offeranswer.h" />
<ClInclude Include="..\..\..\coreapi\private.h" />
<ClInclude Include="..\..\..\coreapi\sipsetup.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\belle-sip\build\windows\belle-sip\belle-sip.vcxproj">
<Project>{4c225a82-800b-427b-ba7b-61686a9b347f}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\mediastreamer2\build\vsx\mediastreamer2\mediastreamer2\mediastreamer2.vcxproj">
<Project>{027bad0e-9179-48c1-9733-7aa7e2c2ec70}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\msamr\build\windows\msamr\msamr\msamr.vcxproj">
<Project>{9924ac72-f96c-4e56-94d9-2b025da43c6b}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\msilbc\build\windows\msilbc\msilbc\msilbc.vcxproj">
<Project>{072fad20-7007-4da2-b2e7-16ce2b219f67}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\mssilk\build\windows\mssilk\mssilk\mssilk.vcxproj">
<Project>{36b528f9-fb79-4078-a16b-0a7442581bb7}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\mswasapi\mswasapi\mswasapi\mswasapi.vcxproj">
<Project>{d22bd217-d0f8-4274-9b3a-f3f35f46482c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\oRTP\build\vsx\oRTP\oRTP\oRTP.vcxproj">
<Project>{ffc7b532-0502-4d88-ac98-9e89071cbc97}</Project>
<Private>false</Private>
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
<ProjectReference Include="..\..\..\..\tunnel\build\windows\tunnel\tunnel\tunnel.vcxproj">
<Project>{59500dd1-b192-4ddf-a402-8a8e3739e032}</Project>
</ProjectReference>
<ProjectReference Include="..\libxml2\libxml2\libxml2.vcxproj">
<Project>{5dfa07b4-0be9-46a9-ba32-fdf5a55c580b}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='ARM'">
<Reference Include="Windows">
<IsWinMDFile>true</IsWinMDFile>
</Reference>
<Reference Include="platform.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\coreapi\TunnelManager.hh" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsPhone\v$(TargetPlatformVersion)\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets" Condition="'$(Platform)'=='ARM'" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,522 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Phone
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibLinphoneTester-wp8", "LibLinphoneTester-wp8\LibLinphoneTester-wp8.csproj", "{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}"
ProjectSection(ProjectDependencies) = postProject
{5E94A00B-B14A-4E42-8284-8CB0EF099534} = {5E94A00B-B14A-4E42-8284-8CB0EF099534}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibLinphone", "..\LibLinphone\LibLinphone.vcxproj", "{08DD0D38-D9B5-4626-B60D-B4D76B571142}"
ProjectSection(ProjectDependencies) = postProject
{D22BD217-D0F8-4274-9B3A-F3F35F46482C} = {D22BD217-D0F8-4274-9B3A-F3F35F46482C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibLinphoneTester", "..\LibLinphoneTester\LibLinphoneTester.vcxproj", "{5E94A00B-B14A-4E42-8284-8CB0EF099534}"
ProjectSection(ProjectDependencies) = postProject
{902DAF1D-EBF1-4D03-B598-143500A50AB4} = {902DAF1D-EBF1-4D03-B598-143500A50AB4}
{08DD0D38-D9B5-4626-B60D-B4D76B571142} = {08DD0D38-D9B5-4626-B60D-B4D76B571142}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "..\..\..\..\belle-sip\build\windows\belle-sip\belle-sip.vcxproj", "{4C225A82-800B-427B-BA7B-61686A9B347F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediastreamer2", "..\..\..\..\mediastreamer2\build\vsx\mediastreamer2\mediastreamer2\mediastreamer2.vcxproj", "{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "oRTP", "..\..\..\..\oRTP\build\vsx\oRTP\oRTP\oRTP.vcxproj", "{FFC7B532-0502-4D88-AC98-9E89071CBC97}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libantlr3c", "..\..\..\..\antlr3\runtime\C\build\vsx\libantlr3c\libantlr3c.vcxproj", "{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsm", "..\..\..\..\gsm\build\windows\gsm\gsm\gsm.vcxproj", "{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speex", "..\..\..\..\speex\build\windows\speex\speex\speex.vcxproj", "{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speexdsp", "..\..\..\..\speex\build\windows\speex\speexdsp\speexdsp.vcxproj", "{6BD78980-9C71-4341-8775-AD19E9EC7305}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cunit", "..\..\..\..\cunit\build\windows\cunit\cunit.vcxproj", "{902DAF1D-EBF1-4D03-B598-143500A50AB4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswasapi", "..\..\..\..\mswasapi\mswasapi\mswasapi\mswasapi.vcxproj", "{D22BD217-D0F8-4274-9B3A-F3F35F46482C}"
ProjectSection(ProjectDependencies) = postProject
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}
{FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "webrtcaecm", "..\..\..\..\webrtc\build\windows\webrtcaecm\webrtcaecm\webrtcaecm.vcxproj", "{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libilbc-rfc3951", "..\..\..\..\libilbc-rfc3951\build\windows\libilbc-rfc3951\libilbc-rfc3951\libilbc-rfc3951.vcxproj", "{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsilbc", "..\..\..\..\msilbc\build\windows\msilbc\msilbc\msilbc.vcxproj", "{072FAD20-7007-4DA2-B2E7-16CE2B219F67}"
ProjectSection(ProjectDependencies) = postProject
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}
{FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmssilk", "..\..\..\..\mssilk\build\windows\mssilk\mssilk\mssilk.vcxproj", "{36B528F9-FB79-4078-A16B-0A7442581BB7}"
ProjectSection(ProjectDependencies) = postProject
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}
{FFC7B532-0502-4D88-AC98-9E89071CBC97} = {FFC7B532-0502-4D88-AC98-9E89071CBC97}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsamr", "..\..\..\..\msamr\build\windows\msamr\msamr\msamr.vcxproj", "{9924AC72-F96C-4E56-94D9-2B025DA43C6B}"
ProjectSection(ProjectDependencies) = postProject
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70} = {027BAD0E-9179-48C1-9733-7AA7E2C2EC70}
{018A4428-535C-4566-9AE0-E93AFF0D3ED2} = {018A4428-535C-4566-9AE0-E93AFF0D3ED2}
{7AC65D2A-6981-4D17-856D-C37A522739D8} = {7AC65D2A-6981-4D17-856D-C37A522739D8}
{88191E75-2993-48D7-AA76-652F274EF0FE} = {88191E75-2993-48D7-AA76-652F274EF0FE}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vo-amrwbenc", "..\..\..\..\msamr\build\windows\msamr\vo-amrwbenc\vo-amrwbenc.vcxproj", "{018A4428-535C-4566-9AE0-E93AFF0D3ED2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrnb", "..\..\..\..\msamr\build\windows\msamr\opencore_amrnb\opencore_amrnb.vcxproj", "{88191E75-2993-48D7-AA76-652F274EF0FE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrwb", "..\..\..\..\msamr\build\windows\msamr\opencore_amrwb\opencore_amrwb.vcxproj", "{7AC65D2A-6981-4D17-856D-C37A522739D8}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\windows\polarssl\polarssl\polarssl.vcxproj", "{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tunnel", "..\..\..\..\tunnel\build\windows\tunnel\tunnel\tunnel.vcxproj", "{59500DD1-B192-4DDF-A402-8A8E3739E032}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxml2", "..\libxml2\libxml2\libxml2.vcxproj", "{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.ActiveCfg = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.Build.0 = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|ARM.Deploy.0 = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Mixed Platforms.Deploy.0 = Debug|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Win32.ActiveCfg = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Win32.Build.0 = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|Win32.Deploy.0 = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.ActiveCfg = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.Build.0 = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Debug|x86.Deploy.0 = Debug|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Any CPU.Build.0 = Release|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Any CPU.Deploy.0 = Release|Any CPU
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.ActiveCfg = Release|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.Build.0 = Release|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|ARM.Deploy.0 = Release|ARM
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Mixed Platforms.ActiveCfg = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Mixed Platforms.Build.0 = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Mixed Platforms.Deploy.0 = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Win32.ActiveCfg = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Win32.Build.0 = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|Win32.Deploy.0 = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.ActiveCfg = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.Build.0 = Release|x86
{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}.Release|x86.Deploy.0 = Release|x86
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|Any CPU.ActiveCfg = Debug|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|ARM.ActiveCfg = Debug|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|ARM.Build.0 = Debug|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|Win32.ActiveCfg = Debug|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|Win32.Build.0 = Debug|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|x86.ActiveCfg = Debug|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Debug|x86.Build.0 = Debug|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|Any CPU.ActiveCfg = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|ARM.ActiveCfg = Release|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|ARM.Build.0 = Release|ARM
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|Mixed Platforms.Build.0 = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|Win32.ActiveCfg = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|Win32.Build.0 = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|x86.ActiveCfg = Release|Win32
{08DD0D38-D9B5-4626-B60D-B4D76B571142}.Release|x86.Build.0 = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|Any CPU.ActiveCfg = Debug|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|ARM.ActiveCfg = Debug|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|ARM.Build.0 = Debug|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|Win32.ActiveCfg = Debug|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|Win32.Build.0 = Debug|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|x86.ActiveCfg = Debug|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Debug|x86.Build.0 = Debug|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|Any CPU.ActiveCfg = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|ARM.ActiveCfg = Release|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|ARM.Build.0 = Release|ARM
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|Mixed Platforms.Build.0 = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|Win32.ActiveCfg = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|Win32.Build.0 = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|x86.ActiveCfg = Release|Win32
{5E94A00B-B14A-4E42-8284-8CB0EF099534}.Release|x86.Build.0 = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Any CPU.ActiveCfg = Debug|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.ActiveCfg = Debug|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.Build.0 = Debug|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Win32.ActiveCfg = Debug|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Win32.Build.0 = Debug|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.ActiveCfg = Debug|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.Build.0 = Debug|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Any CPU.ActiveCfg = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.ActiveCfg = Release|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.Build.0 = Release|ARM
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Mixed Platforms.Build.0 = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Win32.ActiveCfg = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Win32.Build.0 = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.ActiveCfg = Release|Win32
{4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.Build.0 = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|Any CPU.ActiveCfg = Debug|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|ARM.ActiveCfg = Debug|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|ARM.Build.0 = Debug|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|Win32.ActiveCfg = Debug|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|Win32.Build.0 = Debug|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|x86.ActiveCfg = Debug|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Debug|x86.Build.0 = Debug|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|Any CPU.ActiveCfg = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|ARM.ActiveCfg = Release|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|ARM.Build.0 = Release|ARM
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|Mixed Platforms.Build.0 = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|Win32.ActiveCfg = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|Win32.Build.0 = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|x86.ActiveCfg = Release|Win32
{027BAD0E-9179-48C1-9733-7AA7E2C2EC70}.Release|x86.Build.0 = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Any CPU.ActiveCfg = Debug|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.ActiveCfg = Debug|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.Build.0 = Debug|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Win32.ActiveCfg = Debug|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Win32.Build.0 = Debug|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|x86.ActiveCfg = Debug|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|x86.Build.0 = Debug|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Any CPU.ActiveCfg = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.ActiveCfg = Release|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.Build.0 = Release|ARM
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Mixed Platforms.Build.0 = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Win32.ActiveCfg = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Win32.Build.0 = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|x86.ActiveCfg = Release|Win32
{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|x86.Build.0 = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.ActiveCfg = Debug|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.Build.0 = Debug|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Win32.ActiveCfg = Debug|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Win32.Build.0 = Debug|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.ActiveCfg = Debug|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.Build.0 = Debug|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Any CPU.ActiveCfg = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.ActiveCfg = Release|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.Build.0 = Release|ARM
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Mixed Platforms.Build.0 = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Win32.ActiveCfg = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Win32.Build.0 = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.ActiveCfg = Release|Win32
{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.Build.0 = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|Any CPU.ActiveCfg = Debug|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|ARM.ActiveCfg = Debug|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|ARM.Build.0 = Debug|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|Win32.ActiveCfg = Debug|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|Win32.Build.0 = Debug|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|x86.ActiveCfg = Debug|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Debug|x86.Build.0 = Debug|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|Any CPU.ActiveCfg = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|ARM.ActiveCfg = Release|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|ARM.Build.0 = Release|ARM
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|Mixed Platforms.Build.0 = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|Win32.ActiveCfg = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|Win32.Build.0 = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|x86.ActiveCfg = Release|Win32
{746EA080-5BA9-42C5-9E52-EA421C3F3AFD}.Release|x86.Build.0 = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|Any CPU.ActiveCfg = Debug|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|ARM.ActiveCfg = Debug|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|ARM.Build.0 = Debug|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|Win32.ActiveCfg = Debug|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|Win32.Build.0 = Debug|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|x86.ActiveCfg = Debug|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Debug|x86.Build.0 = Debug|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|Any CPU.ActiveCfg = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|ARM.ActiveCfg = Release|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|ARM.Build.0 = Release|ARM
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|Mixed Platforms.Build.0 = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|Win32.ActiveCfg = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|Win32.Build.0 = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|x86.ActiveCfg = Release|Win32
{D5EC8C11-C1D9-47E3-BB82-A93C300FD902}.Release|x86.Build.0 = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|Any CPU.ActiveCfg = Debug|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|ARM.ActiveCfg = Debug|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|ARM.Build.0 = Debug|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|Win32.ActiveCfg = Debug|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|Win32.Build.0 = Debug|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|x86.ActiveCfg = Debug|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Debug|x86.Build.0 = Debug|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|Any CPU.ActiveCfg = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|ARM.ActiveCfg = Release|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|ARM.Build.0 = Release|ARM
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|Mixed Platforms.Build.0 = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|Win32.ActiveCfg = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|Win32.Build.0 = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|x86.ActiveCfg = Release|Win32
{6BD78980-9C71-4341-8775-AD19E9EC7305}.Release|x86.Build.0 = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|Any CPU.ActiveCfg = Debug|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.ActiveCfg = Debug|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.Build.0 = Debug|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|Mixed Platforms.ActiveCfg = Debug|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|Mixed Platforms.Build.0 = Debug|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|Win32.ActiveCfg = Debug|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|Win32.Build.0 = Debug|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.ActiveCfg = Debug|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.Build.0 = Debug|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|Any CPU.ActiveCfg = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.ActiveCfg = Release|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.Build.0 = Release|ARM
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|Mixed Platforms.Build.0 = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|Win32.ActiveCfg = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|Win32.Build.0 = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.ActiveCfg = Release|Win32
{902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.Build.0 = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|Any CPU.ActiveCfg = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|ARM.ActiveCfg = Debug|ARM
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|ARM.Build.0 = Debug|ARM
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|Win32.ActiveCfg = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|Win32.Build.0 = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|x86.ActiveCfg = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Debug|x86.Build.0 = Debug|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|Any CPU.ActiveCfg = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|ARM.ActiveCfg = Release|ARM
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|ARM.Build.0 = Release|ARM
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|Mixed Platforms.Build.0 = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|Win32.ActiveCfg = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|Win32.Build.0 = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|x86.ActiveCfg = Release|Win32
{D22BD217-D0F8-4274-9B3A-F3F35F46482C}.Release|x86.Build.0 = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|Any CPU.ActiveCfg = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|ARM.ActiveCfg = Debug|ARM
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|ARM.Build.0 = Debug|ARM
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|Win32.ActiveCfg = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|Win32.Build.0 = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|x86.ActiveCfg = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Debug|x86.Build.0 = Debug|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|Any CPU.ActiveCfg = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|ARM.ActiveCfg = Release|ARM
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|ARM.Build.0 = Release|ARM
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|Mixed Platforms.Build.0 = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|Win32.ActiveCfg = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|Win32.Build.0 = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|x86.ActiveCfg = Release|Win32
{1C4E6DA0-B8C7-4A05-A58E-54A6ED07C8DF}.Release|x86.Build.0 = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|ARM.ActiveCfg = Debug|ARM
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|ARM.Build.0 = Debug|ARM
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|Win32.ActiveCfg = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|Win32.Build.0 = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|x86.ActiveCfg = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Debug|x86.Build.0 = Debug|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|Any CPU.ActiveCfg = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|ARM.ActiveCfg = Release|ARM
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|ARM.Build.0 = Release|ARM
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|Mixed Platforms.Build.0 = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|Win32.ActiveCfg = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|Win32.Build.0 = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|x86.ActiveCfg = Release|Win32
{8E216BF3-2DD8-4794-8E97-B1AED301ED4D}.Release|x86.Build.0 = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|Any CPU.ActiveCfg = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|ARM.ActiveCfg = Debug|ARM
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|ARM.Build.0 = Debug|ARM
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|Win32.ActiveCfg = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|Win32.Build.0 = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|x86.ActiveCfg = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Debug|x86.Build.0 = Debug|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|Any CPU.ActiveCfg = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|ARM.ActiveCfg = Release|ARM
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|ARM.Build.0 = Release|ARM
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|Mixed Platforms.Build.0 = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|Win32.ActiveCfg = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|Win32.Build.0 = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|x86.ActiveCfg = Release|Win32
{072FAD20-7007-4DA2-B2E7-16CE2B219F67}.Release|x86.Build.0 = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|Any CPU.ActiveCfg = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|ARM.ActiveCfg = Debug|ARM
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|ARM.Build.0 = Debug|ARM
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|Win32.ActiveCfg = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|Win32.Build.0 = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|x86.ActiveCfg = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Debug|x86.Build.0 = Debug|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|Any CPU.ActiveCfg = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|ARM.ActiveCfg = Release|ARM
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|ARM.Build.0 = Release|ARM
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|Mixed Platforms.Build.0 = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|Win32.ActiveCfg = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|Win32.Build.0 = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|x86.ActiveCfg = Release|Win32
{36B528F9-FB79-4078-A16B-0A7442581BB7}.Release|x86.Build.0 = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|ARM.ActiveCfg = Debug|ARM
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|ARM.Build.0 = Debug|ARM
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|Win32.ActiveCfg = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|Win32.Build.0 = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|x86.ActiveCfg = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Debug|x86.Build.0 = Debug|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|Any CPU.ActiveCfg = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|ARM.ActiveCfg = Release|ARM
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|ARM.Build.0 = Release|ARM
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|Mixed Platforms.Build.0 = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|Win32.ActiveCfg = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|Win32.Build.0 = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|x86.ActiveCfg = Release|Win32
{9924AC72-F96C-4E56-94D9-2B025DA43C6B}.Release|x86.Build.0 = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|Any CPU.ActiveCfg = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|ARM.ActiveCfg = Debug|ARM
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|ARM.Build.0 = Debug|ARM
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|Win32.ActiveCfg = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|Win32.Build.0 = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|x86.ActiveCfg = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Debug|x86.Build.0 = Debug|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|Any CPU.ActiveCfg = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|ARM.ActiveCfg = Release|ARM
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|ARM.Build.0 = Release|ARM
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|Mixed Platforms.Build.0 = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|Win32.ActiveCfg = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|Win32.Build.0 = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|x86.ActiveCfg = Release|Win32
{018A4428-535C-4566-9AE0-E93AFF0D3ED2}.Release|x86.Build.0 = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|Any CPU.ActiveCfg = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|ARM.ActiveCfg = Debug|ARM
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|ARM.Build.0 = Debug|ARM
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|Win32.ActiveCfg = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|Win32.Build.0 = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|x86.ActiveCfg = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Debug|x86.Build.0 = Debug|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|Any CPU.ActiveCfg = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|ARM.ActiveCfg = Release|ARM
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|ARM.Build.0 = Release|ARM
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|Mixed Platforms.Build.0 = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|Win32.ActiveCfg = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|Win32.Build.0 = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|x86.ActiveCfg = Release|Win32
{88191E75-2993-48D7-AA76-652F274EF0FE}.Release|x86.Build.0 = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|Any CPU.ActiveCfg = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|ARM.ActiveCfg = Debug|ARM
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|ARM.Build.0 = Debug|ARM
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|Win32.ActiveCfg = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|Win32.Build.0 = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|x86.ActiveCfg = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Debug|x86.Build.0 = Debug|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|Any CPU.ActiveCfg = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|ARM.ActiveCfg = Release|ARM
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|ARM.Build.0 = Release|ARM
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|Mixed Platforms.Build.0 = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|Win32.ActiveCfg = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|Win32.Build.0 = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|x86.ActiveCfg = Release|Win32
{7AC65D2A-6981-4D17-856D-C37A522739D8}.Release|x86.Build.0 = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Any CPU.ActiveCfg = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.ActiveCfg = Debug|ARM
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.Build.0 = Debug|ARM
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Win32.ActiveCfg = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Win32.Build.0 = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.ActiveCfg = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.Build.0 = Debug|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Any CPU.ActiveCfg = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.ActiveCfg = Release|ARM
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.Build.0 = Release|ARM
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Mixed Platforms.Build.0 = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Win32.ActiveCfg = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Win32.Build.0 = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.ActiveCfg = Release|Win32
{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.Build.0 = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Any CPU.ActiveCfg = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.ActiveCfg = Debug|ARM
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.Build.0 = Debug|ARM
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Win32.ActiveCfg = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Win32.Build.0 = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.ActiveCfg = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.Build.0 = Debug|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Any CPU.ActiveCfg = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.ActiveCfg = Release|ARM
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.Build.0 = Release|ARM
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Mixed Platforms.Build.0 = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Win32.ActiveCfg = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Win32.Build.0 = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.ActiveCfg = Release|Win32
{59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.Build.0 = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{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|Mixed Platforms.ActiveCfg = Debug|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{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}.Debug|x86.ActiveCfg = Debug|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Debug|x86.Build.0 = Debug|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Any CPU.ActiveCfg = Release|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|Mixed Platforms.ActiveCfg = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Mixed Platforms.Build.0 = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.ActiveCfg = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|Win32.Build.0 = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|x86.ActiveCfg = Release|Win32
{5DFA07B4-0BE9-46A9-BA32-FDF5A55C580B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,20 @@
<Application
x:Class="LibLinphoneTester_wp8.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">
<!--Application Resources-->
<Application.Resources>
<local:LocalizedStrings xmlns:local="clr-namespace:LibLinphoneTester_wp8" x:Key="LocalizedStrings"/>
</Application.Resources>
<Application.ApplicationLifetimeObjects>
<!--Required object that handles lifetime events for the application-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
</Application.ApplicationLifetimeObjects>
</Application>

View file

@ -0,0 +1,234 @@
using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using LibLinphoneTester_wp8.Resources;
using linphone_tester_native;
namespace LibLinphoneTester_wp8
{
public partial class App : Application
{
/// <summary>
/// Provides easy access to the root frame of the Phone Application.
/// </summary>
/// <returns>The root frame of the Phone Application.</returns>
public static PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Constructor for the Application object.
/// </summary>
public App()
{
// Global handler for uncaught exceptions.
UnhandledException += Application_UnhandledException;
// Standard XAML initialization
InitializeComponent();
// Phone-specific initialization
InitializePhoneApplication();
// Language display initialization
InitializeLanguage();
// Show graphics profiling information while debugging.
if (Debugger.IsAttached)
{
// Display the current frame rate counters.
Application.Current.Host.Settings.EnableFrameRateCounter = true;
// Show the areas of the app that are being redrawn in each frame.
//Application.Current.Host.Settings.EnableRedrawRegions = true;
// Enable non-production analysis visualization mode,
// which shows areas of a page that are handed off to GPU with a colored overlay.
//Application.Current.Host.Settings.EnableCacheVisualization = true;
// Prevent the screen from turning off while under the debugger by disabling
// the application's idle detection.
// Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
// and consume battery power when the user is not using the phone.
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}
tester = new LinphoneTesterNative();
suite = null;
}
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
// Code to execute if a navigation fails
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
if (Debugger.IsAttached)
{
// A navigation has failed; break into the debugger
Debugger.Break();
}
}
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
Debugger.Break();
}
}
#region Phone application initialization
// Avoid double-initialization
private bool phoneApplicationInitialized = false;
// Do not add any additional code to this method
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack
RootFrame.Navigated += CheckForResetNavigation;
// Ensure we don't initialize again
phoneApplicationInitialized = true;
}
// Do not add any additional code to this method
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
}
private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again
RootFrame.Navigated -= ClearBackStackAfterReset;
// Only clear the stack for 'new' (forward) and 'refresh' navigations
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;
// For UI consistency, clear the entire page stack
while (RootFrame.RemoveBackEntry() != null)
{
; // do nothing
}
}
#endregion
// Initialize the app's font and flow direction as defined in its localized resource strings.
//
// To ensure that the font of your application is aligned with its supported languages and that the
// FlowDirection for each of those languages follows its traditional direction, ResourceLanguage
// and ResourceFlowDirection should be initialized in each resx file to match these values with that
// file's culture. For example:
//
// AppResources.es-ES.resx
// ResourceLanguage's value should be "es-ES"
// ResourceFlowDirection's value should be "LeftToRight"
//
// AppResources.ar-SA.resx
// ResourceLanguage's value should be "ar-SA"
// ResourceFlowDirection's value should be "RightToLeft"
//
// For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072.
//
private void InitializeLanguage()
{
try
{
// Set the font to match the display language defined by the
// ResourceLanguage resource string for each supported language.
//
// Fall back to the font of the neutral language if the Display
// language of the phone is not supported.
//
// If a compiler error is hit then ResourceLanguage is missing from
// the resource file.
RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);
// Set the FlowDirection of all elements under the root frame based
// on the ResourceFlowDirection resource string for each
// supported language.
//
// If a compiler error is hit then ResourceFlowDirection is missing from
// the resource file.
FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
RootFrame.FlowDirection = flow;
}
catch
{
// If an exception is caught here it is most likely due to either
// ResourceLangauge not being correctly set to a supported language
// code or ResourceFlowDirection is set to a value other than LeftToRight
// or RightToLeft.
if (Debugger.IsAttached)
{
Debugger.Break();
}
throw;
}
}
public bool suiteRunning()
{
return (suite != null) && (suite.running);
}
public LinphoneTesterNative tester { get; set; }
public UnitTestSuite suite { get; set; }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,6 @@
[net]
mtu=1300
[sip]
ping_with_options=0
sip_random_port=1

View file

@ -0,0 +1,41 @@
[sip]
sip_port=5092
sip_tcp_port=5092
sip_tls_port=5093
default_proxy=0
ping_with_options=0
register_only_when_network_is_up=0
[auth_info_0]
username=laure
userid=laure
passwd=secret
realm="sip.example.org"
[proxy_0]
reg_proxy=sip.example.org
reg_identity=sip:laure@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
[rtp]
audio_rtp_port=9010
video_rtp_port=9012
[video]
display=0
capture=0
show_local=0
size=vga
enabled=0
self_view=0
automatically_initiate=0
automatically_accept=0
device=StaticImage: Static picture
[sound]
echocancellation=0 #to not overload cpu in case of VG

View file

@ -0,0 +1,45 @@
[sip]
sip_port=5082
sip_tcp_port=5082
sip_tls_port=5083
default_proxy=0
ping_with_options=0
register_only_when_network_is_up=0
incoming_calls_early_media=1
[auth_info_0]
username=marie
userid=marie
passwd=secret
realm="sip.example.org"
[proxy_0]
reg_proxy=sip.example.org;transport=tcp
reg_route=sip.example.org;transport=tcp;lr
reg_identity=sip:marie@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
[friend_0]
url="Paupoche" <sip:pauline@sip.example.org>
pol=accept
subscribe=0
[rtp]
audio_rtp_port=8070
video_rtp_port=8072
[video]
display=0
capture=0
show_local=0
size=vga
enabled=0
self_view=0
automatically_initiate=0
automatically_accept=0
device=StaticImage: Static picture

View file

@ -0,0 +1,47 @@
[sip]
sip_port=5082
sip_tcp_port=5082
sip_tls_port=5083
default_proxy=0
ping_with_options=0
register_only_when_network_is_up=0
[auth_info_0]
username=marie
userid=marie
passwd=secret
realm="sip.example.org"
[proxy_0]
reg_proxy=sip.example.org;transport=tcp
reg_route=sip.example.org;transport=tcp;lr
reg_identity=sip:marie@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
[friend_0]
url="Paupoche" <sip:pauline@sip.example.org>
pol=accept
subscribe=0
[rtp]
audio_rtp_port=8070
video_rtp_port=8072
[video]
display=0
capture=0
show_local=0
size=vga
enabled=0
self_view=0
automatically_initiate=0
automatically_accept=0
device=StaticImage: Static picture
[sound]
echocancellation=0 #to not overload cpu in case of VG

View file

@ -0,0 +1,55 @@
[sip]
sip_port=5072
sip_tcp_port=5072
sip_tls_port=5073
default_proxy=0
[auth_info_0]
username=liblinphone_tester
userid=liblinphone_tester
passwd=secret
realm="auth.example.org"
[auth_info_1]
username=pauline
userid=pauline
passwd=secret
realm="sip.example.org"
[auth_info_2]
username=liblinphone_tester
userid=liblinphone_tester
passwd=secret
realm="auth1.example.org"
[auth_info_3]
username=marie
userid=marie
passwd=secret
realm="sip.example.org"
[proxy_0]
reg_proxy=sip2.linphone.org;transport=tls
reg_identity=sip:pauline@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
[proxy_1]
reg_proxy=sip.example.org;transport=tcp
reg_identity=sip:marie@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
[proxy_2]
reg_proxy=auth1.example.org
reg_identity=sip:liblinphone_tester@auth1.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0

View file

@ -0,0 +1,46 @@
[sip]
sip_port=5072
sip_tcp_port=5072
sip_tls_port=5073
default_proxy=0
ping_with_options=0
register_only_when_network_is_up=0
[auth_info_0]
username=pauline
userid=pauline
passwd=secret
realm="sip.example.org"
[proxy_0]
reg_proxy=sip2.linphone.org;transport=tls
reg_route=sip2.linphone.org;transport=tls
reg_identity=sip:pauline@sip.example.org
reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
#[friend_0]
#url="Mariette" <sip:marie@sip.example.org>
#pol=accept
#subscribe=0
[rtp]
audio_rtp_port=8090
video_rtp_port=8092
[video]
display=0
capture=0
show_local=0
size=vga
enabled=0
self_view=0
automatically_initiate=0
automatically_accept=0
device=StaticImage: Static picture
[sound]
echocancellation=0 #to not overload cpu in case of VG

View file

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.20506</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{34D6878F-6CAB-4AE3-9CCC-25E8D6734C90}</ProjectGuid>
<ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LibLinphoneTester_wp8</RootNamespace>
<AssemblyName>LibLinphoneTester_wp8</AssemblyName>
<TargetFrameworkIdentifier>WindowsPhone</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
<SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
<SilverlightApplication>true</SilverlightApplication>
<SupportedCultures>
</SupportedCultures>
<XapOutputs>true</XapOutputs>
<GenerateSilverlightManifest>true</GenerateSilverlightManifest>
<XapFilename>LibLinphoneTester_wp8_$(Configuration)_$(Platform).xap</XapFilename>
<SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
<SilverlightAppEntry>LibLinphoneTester_wp8.App</SilverlightAppEntry>
<ValidateXaml>true</ValidateXaml>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<ThrowErrorsInValidation>true</ThrowErrorsInValidation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>Bin\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>Bin\Release</OutputPath>
<DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>Bin\x86\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>Bin\x86\Release</OutputPath>
<DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>Bin\ARM\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>Bin\ARM\Release</OutputPath>
<DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="LocalizedStrings.cs" />
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\AppResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.resx</DependentUpon>
</Compile>
<Compile Include="TestCasePage.xaml.cs">
<DependentUpon>TestCasePage.xaml</DependentUpon>
</Compile>
<Compile Include="TestResultPage.xaml.cs">
<DependentUpon>TestResultPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</ApplicationDefinition>
<Page Include="MainPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="TestCasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="TestResultPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\empty_rc" />
<Content Include="Assets\laure_rc" />
<Content Include="Assets\marie_rc" />
<Content Include="Assets\oldphone.wav" />
<Content Include="Assets\pauline_rc" />
<Content Include="Assets\multi_account_lrc" />
<Content Include="log.html" />
<Content Include="Assets\marie_early_rc" />
<None Include="Properties\AppManifest.xml" />
<None Include="Properties\WMAppManifest.xml">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\AlignmentGrid.png" />
<Content Include="Assets\ApplicationIcon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\ringback.wav" />
<Content Include="Assets\Tiles\FlipCycleTileLarge.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Tiles\FlipCycleTileMedium.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Tiles\FlipCycleTileSmall.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Tiles\IconicTileMediumLarge.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Tiles\IconicTileSmall.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\AppResources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\LibLinphoneTester\LibLinphoneTester.vcxproj">
<Project>{5E94A00B-B14A-4E42-8284-8CB0EF099534}</Project>
<Name>LibLinphoneTester</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ProjectExtensions />
<PropertyGroup>
<PreBuildEvent>Xcopy /I /Y $(ProjectDir)..\..\..\..\tester\*rc $(ProjectDir)Assets\</PreBuildEvent>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,14 @@
using LibLinphoneTester_wp8.Resources;
namespace LibLinphoneTester_wp8
{
/// <summary>
/// Provides access to string resources.
/// </summary>
public class LocalizedStrings
{
private static AppResources _localizedResources = new AppResources();
public AppResources LocalizedResources { get { return _localizedResources; } }
}
}

View file

@ -0,0 +1,42 @@
<phone:PhoneApplicationPage
x:Class="LibLinphoneTester_wp8.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="UnitTestItemTemplate">
<TextBlock Style="{StaticResource PhoneTextTitle2Style}" Text="{Binding Name}"/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="LINPHONE TESTER" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="Test Suite" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<CheckBox x:Name="Verbose" Content="Verbose" HorizontalAlignment="Right" Margin="0,0,0,537" IsChecked="True"/>
<phone:LongListSelector x:Name="Tests" HorizontalAlignment="Left" Height="510" VerticalAlignment="Top" Width="456" ItemTemplate="{StaticResource UnitTestItemTemplate}" LayoutMode="List" Margin="0,70,0,-10" Tap="Tests_Tap"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
namespace LibLinphoneTester_wp8
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
var tester = (Application.Current as App).tester;
List<UnitTestSuiteName> source = new List<UnitTestSuiteName>();
source.Add(new UnitTestSuiteName("ALL"));
for (int i = 0; i < tester.nbTestSuites(); i++)
{
source.Add(new UnitTestSuiteName(tester.testSuiteName(i)));
}
Tests.ItemsSource = source;
}
private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
UnitTestSuiteName test = (sender as LongListSelector).SelectedItem as UnitTestSuiteName;
if (test == null) return;
if (test.Name == "ALL")
{
NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative));
}
else
{
NavigationService.Navigate(new Uri("/TestCasePage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative));
}
}
}
public class UnitTestSuiteName
{
public string Name
{
get;
set;
}
public UnitTestSuiteName(string name)
{
this.Name = name;
}
}
}

View file

@ -0,0 +1,6 @@
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Deployment.Parts>
</Deployment.Parts>
</Deployment>

View file

@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Resources;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("LibLinphoneTester_wp8")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LibLinphoneTester_wp8")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f1aad7a9-2083-4726-ab28-f57b1dd5891e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
<DefaultLanguage xmlns="" code="en-US" />
<App xmlns="" ProductID="{34d6878f-6cab-4ae3-9ccc-25e8d6734c90}" Title="LibLinphoneTester_wp8" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="Belledonne Communications" Description="Sample description" Publisher="Belledonne Communications" PublisherID="{975afaec-3620-4b48-9a95-98c6d06539a6}">
<IconPath IsRelative="true" IsResource="false">Assets\ApplicationIcon.png</IconPath>
<Capabilities>
<Capability Name="ID_CAP_NETWORKING" />
<Capability Name="ID_CAP_MEDIALIB_AUDIO" />
<Capability Name="ID_CAP_MEDIALIB_PLAYBACK" />
<Capability Name="ID_CAP_SENSORS" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
</Capabilities>
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
</Tasks>
<Tokens>
<PrimaryToken TokenID="LibLinphoneTester_wp8Token" TaskName="_default">
<TemplateFlip>
<SmallImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileSmall.png</SmallImageURI>
<Count>0</Count>
<BackgroundImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileMedium.png</BackgroundImageURI>
<Title>LibLinphoneTester_wp8</Title>
<BackContent>
</BackContent>
<BackBackgroundImageURI>
</BackBackgroundImageURI>
<BackTitle>
</BackTitle>
<LargeBackgroundImageURI>
</LargeBackgroundImageURI>
<LargeBackContent>
</LargeBackContent>
<LargeBackBackgroundImageURI>
</LargeBackBackgroundImageURI>
<DeviceLockImageURI>
</DeviceLockImageURI>
<HasLarge>
</HasLarge>
</TemplateFlip>
</PrimaryToken>
</Tokens>
<ScreenResolutions>
<ScreenResolution Name="ID_RESOLUTION_WVGA" />
<ScreenResolution Name="ID_RESOLUTION_WXGA" />
<ScreenResolution Name="ID_RESOLUTION_HD720P" />
</ScreenResolutions>
</App>
</Deployment>

View file

@ -0,0 +1,127 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17626
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LibLinphoneTester_wp8.Resources
{
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class AppResources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal AppResources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager
{
get
{
if (object.ReferenceEquals(resourceMan, null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LibLinphoneTester_wp8.Resources.AppResources", typeof(AppResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to LeftToRight.
/// </summary>
public static string ResourceFlowDirection
{
get
{
return ResourceManager.GetString("ResourceFlowDirection", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to us-EN.
/// </summary>
public static string ResourceLanguage
{
get
{
return ResourceManager.GetString("ResourceLanguage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to MY APPLICATION.
/// </summary>
public static string ApplicationTitle
{
get
{
return ResourceManager.GetString("ApplicationTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to button.
/// </summary>
public static string AppBarButtonText
{
get
{
return ResourceManager.GetString("AppBarButtonText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to menu item.
/// </summary>
public static string AppBarMenuItemText
{
get
{
return ResourceManager.GetString("AppBarMenuItemText", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ResourceFlowDirection" xml:space="preserve">
<value>LeftToRight</value>
<comment>Controls the FlowDirection for all elements in the RootFrame. Set to the traditional direction of this resource file's language</comment>
</data>
<data name="ResourceLanguage" xml:space="preserve">
<value>en-US</value>
<comment>Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language.</comment>
</data>
<data name="ApplicationTitle" xml:space="preserve">
<value>MY APPLICATION</value>
</data>
<data name="AppBarButtonText" xml:space="preserve">
<value>add</value>
</data>
<data name="AppBarMenuItemText" xml:space="preserve">
<value>Menu Item</value>
</data>
</root>

View file

@ -0,0 +1,40 @@
<phone:PhoneApplicationPage
x:Class="LibLinphoneTester_wp8.TestCasePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="UnitTestItemTemplate">
<TextBlock Style="{StaticResource PhoneTextTitle2Style}" Text="{Binding Name}"/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="LINPHONETESTER" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Test Case" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="Tests" HorizontalAlignment="Left" Height="510" VerticalAlignment="Top" Width="456" ItemTemplate="{StaticResource UnitTestItemTemplate}" LayoutMode="List" Margin="0,70,0,-10" Tap="Tests_Tap" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
namespace LibLinphoneTester_wp8
{
public partial class TestCasePage : PhoneApplicationPage
{
public TestCasePage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
suiteName = NavigationContext.QueryString["SuiteName"];
verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]);
var tester = (Application.Current as App).tester;
List<UnitTestCaseName> source = new List<UnitTestCaseName>();
source.Add(new UnitTestCaseName("ALL"));
for (int i = 0; i < tester.nbTests(suiteName); i++)
{
source.Add(new UnitTestCaseName(tester.testName(suiteName, i)));
}
Tests.ItemsSource = source;
}
private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
UnitTestCaseName test = (sender as LongListSelector).SelectedItem as UnitTestCaseName;
if (test == null) return;
if (!(Application.Current as App).suiteRunning())
{
NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + suiteName + "&CaseName=" + test.Name + "&Verbose=" + verbose, UriKind.Relative));
}
}
private string suiteName;
private bool verbose;
}
public class UnitTestCaseName
{
public string Name
{
get;
set;
}
public UnitTestCaseName(string name)
{
this.Name = name;
}
}
}

View file

@ -0,0 +1,39 @@
<phone:PhoneApplicationPage
x:Class="LibLinphoneTester_wp8.TestResultPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Style="{StaticResource PhoneTextNormalStyle}">
<Run Text="LINPHONE TESTER"/>
<LineBreak/>
<Run/>
</TextBlock>
<TextBlock Text="Test Results" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="2" Margin="12,0,12,0">
<phone:WebBrowser x:Name="Browser" IsScriptEnabled="True" LoadCompleted="Browser_LoadCompleted" Background="{StaticResource PhoneBackgroundBrush}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>

View file

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Threading.Tasks;
using linphone_tester_native;
namespace LibLinphoneTester_wp8
{
public delegate void OutputDisplayDelegate(int level, String msg);
public partial class TestResultPage : PhoneApplicationPage
{
public TestResultPage()
{
InitializeComponent();
Browser.Navigate(new Uri("log.html", UriKind.Relative));
}
private void Browser_LoadCompleted(object sender, NavigationEventArgs e)
{
string suiteName = NavigationContext.QueryString["SuiteName"];
string caseName;
if (NavigationContext.QueryString.ContainsKey("CaseName"))
{
caseName = NavigationContext.QueryString["CaseName"];
}
else
{
caseName = "ALL";
}
bool verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]);
var app = (Application.Current as App);
app.suite = new UnitTestSuite(suiteName, caseName, verbose, new OutputDisplayDelegate(OutputDisplay));
app.suite.run();
}
public void OutputDisplay(int level, String msg)
{
this.Dispatcher.BeginInvoke(() =>
{
msg = msg.Replace("\r\n", "\n");
string[] lines = msg.Split('\n');
bool insertNewLine = false;
foreach (string line in lines)
{
if (line.Length == 0)
{
insertNewLine = false;
Browser.InvokeScript("append_nl");
}
else
{
if (insertNewLine == true)
{
Browser.InvokeScript("append_nl");
}
if (level == 0)
{
Browser.InvokeScript("append_trace", line, "debug");
}
else if (level == 1)
{
Browser.InvokeScript("append_trace", line, "message");
}
else if (level == 2)
{
Browser.InvokeScript("append_trace", line, "warning");
}
else if (level == 3)
{
Browser.InvokeScript("append_trace", line, "error");
}
else
{
Browser.InvokeScript("append_text", line);
}
insertNewLine = true;
}
}
});
}
}
public class UnitTestSuite : OutputTraceListener
{
public UnitTestSuite(string SuiteName, string CaseName, bool Verbose, OutputDisplayDelegate OutputDisplay)
{
this.SuiteName = SuiteName;
this.CaseName = CaseName;
this.Verbose = Verbose;
this.Running = false;
this.OutputDisplay = OutputDisplay;
}
async public void run()
{
Running = true;
var tup = new Tuple<string, string, bool>(SuiteName, CaseName, Verbose);
var t = Task.Factory.StartNew((object parameters) =>
{
var tester = (Application.Current as App).tester;
tester.setOutputTraceListener(this);
var p = parameters as Tuple<string, string, bool>;
tester.run(p.Item1, p.Item2, p.Item3);
}, tup);
await t;
Running = false;
}
public void outputTrace(int level, String msg)
{
if (OutputDisplay != null)
{
OutputDisplay(level, msg);
}
System.Diagnostics.Debug.WriteLine(msg);
}
public bool running {
get { return Running; }
protected set { Running = value; }
}
private string SuiteName;
private string CaseName;
private bool Verbose;
private bool Running;
private OutputDisplayDelegate OutputDisplay;
}
}

View file

@ -0,0 +1,34 @@
<html>
<head>
<style>
html { background-color: black; font-family:'Courier New'; font-size: 16pt; color: white; }
.debug { color: light-grey; }
.message { color: white; }
.warning { color: orange; }
.error { color: red; }
</style>
<script>
function append_text(msg) {
var content = document.getElementById("content");
var new_text = document.createTextNode(msg);
content.appendChild(new_text);
}
function append_trace(msg, level) {
var content = document.getElementById("content");
var span_elem = document.createElement('span');
span_elem.className = level;
var new_text = document.createTextNode(msg);
span_elem.appendChild(new_text);
content.appendChild(span_elem);
}
function append_nl() {
var content = document.getElementById("content");
var element = document.createElement('br');
content.appendChild(element);
}
</script>
</head>
<body>
<p><pre id="content"></pre></p>
</body>
</html>

View file

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5e94a00b-b14a-4e42-8284-8cb0ef099534}</ProjectGuid>
<RootNamespace>linphone_tester_native</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<WinMDAssembly>true</WinMDAssembly>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>_USRDLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;HAVE_CU_GET_SUITE;IN_LINPHONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tester;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\cunit\build\windows\cunit\$(Platform)\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
<ExceptionHandling>Async</ExceptionHandling>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>ole32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
<AdditionalDependencies>WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<PreprocessorDefinitions>_USRDLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tester;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\cunit\build\windows\cunit\$(Platform)\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Reference Include="platform.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\tester\call_tester.c" />
<ClCompile Include="..\..\..\tester\eventapi_tester.c" />
<ClCompile Include="..\..\..\tester\liblinphone_tester.c" />
<ClCompile Include="..\..\..\tester\message_tester.c" />
<ClCompile Include="..\..\..\tester\presence_tester.c" />
<ClCompile Include="..\..\..\tester\register_tester.c" />
<ClCompile Include="..\..\..\tester\setup_tester.c" />
<ClCompile Include="linphone-tester-native.cpp">
<CompileAsWinRT Condition="'$(Platform)'=='ARM'">true</CompileAsWinRT>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\tester\liblinphone_tester.h" />
<ClInclude Include="linphone-tester-native.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\cunit\build\windows\cunit\cunit.vcxproj">
<Project>{902daf1d-ebf1-4d03-b598-143500a50ab4}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\mediastreamer2\build\vsx\mediastreamer2\mediastreamer2\mediastreamer2.vcxproj">
<Project>{027bad0e-9179-48c1-9733-7aa7e2c2ec70}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\oRTP\build\vsx\oRTP\oRTP\oRTP.vcxproj">
<Project>{ffc7b532-0502-4d88-ac98-9e89071cbc97}</Project>
</ProjectReference>
<ProjectReference Include="..\LibLinphone\LibLinphone.vcxproj">
<Project>{08dd0d38-d9b5-4626-b60d-b4d76b571142}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsPhone\v$(TargetPlatformVersion)\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,125 @@
#include <string>
#include "linphone-tester-native.h"
#include "ortp/logging.h"
#include "cunit/Util.h"
using namespace linphone_tester_native;
using namespace Platform;
#define MAX_TRACE_SIZE 512
#define MAX_SUITE_NAME_SIZE 128
static OutputTraceListener^ sTraceListener;
static void nativeOutputTraceHandler(OutputTraceLevel lev, const char *fmt, va_list args)
{
if (sTraceListener) {
wchar_t wstr[MAX_TRACE_SIZE];
std::string str;
str.resize(MAX_TRACE_SIZE);
vsnprintf((char *)str.c_str(), MAX_TRACE_SIZE, fmt, args);
mbstowcs(wstr, str.c_str(), sizeof(wstr));
String^ msg = ref new String(wstr);
sTraceListener->outputTrace(lev, msg);
}
}
static void LinphoneNativeGenericOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args)
{
OutputTraceLevel level = Message;
char fmt2[MAX_TRACE_SIZE];
snprintf(fmt2, MAX_TRACE_SIZE, "%s\n", fmt);
if (lev == ORTP_DEBUG) level = Debug;
else if (lev == ORTP_MESSAGE) level = Message;
else if (lev == ORTP_TRACE) level = Message;
else if (lev == ORTP_WARNING) level = Warning;
else if (lev == ORTP_ERROR) level = Error;
else if (lev == ORTP_FATAL) level = Error;
nativeOutputTraceHandler(level, fmt2, args);
}
static void LinphoneNativeOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args)
{
if (lev >= ORTP_WARNING) {
LinphoneNativeGenericOutputTraceHandler(lev, fmt, args);
}
}
static void LinphoneNativeVerboseOutputTraceHandler(OrtpLogLevel lev, const char *fmt, va_list args)
{
LinphoneNativeGenericOutputTraceHandler(lev, fmt, args);
}
static void CUnitNativeOutputTraceHandler(int lev, const char *fmt, va_list args)
{
nativeOutputTraceHandler(Raw, fmt, args);
}
LinphoneTesterNative::LinphoneTesterNative()
{
liblinphone_tester_init();
}
LinphoneTesterNative::~LinphoneTesterNative()
{
liblinphone_tester_uninit();
}
void LinphoneTesterNative::setOutputTraceListener(OutputTraceListener^ traceListener)
{
sTraceListener = traceListener;
}
void LinphoneTesterNative::run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose)
{
std::wstring all(L"ALL");
std::wstring wssuitename = suiteName->Data();
std::wstring wscasename = caseName->Data();
char csuitename[MAX_SUITE_NAME_SIZE] = { 0 };
char ccasename[MAX_SUITE_NAME_SIZE] = { 0 };
wcstombs(csuitename, wssuitename.c_str(), sizeof(csuitename));
wcstombs(ccasename, wscasename.c_str(), sizeof(ccasename));
if (verbose) {
linphone_core_enable_logs_with_cb(LinphoneNativeVerboseOutputTraceHandler);
} else {
linphone_core_enable_logs_with_cb(LinphoneNativeOutputTraceHandler);
}
CU_set_trace_handler(CUnitNativeOutputTraceHandler);
liblinphone_tester_run_tests(wssuitename == all ? 0 : csuitename, wscasename == all ? 0 : ccasename);
}
unsigned int LinphoneTesterNative::nbTestSuites()
{
return liblinphone_tester_nb_test_suites();
}
unsigned int LinphoneTesterNative::nbTests(Platform::String^ suiteName)
{
std::wstring suitename = suiteName->Data();
char cname[MAX_SUITE_NAME_SIZE] = { 0 };
wcstombs(cname, suitename.c_str(), sizeof(cname));
return liblinphone_tester_nb_tests(cname);
}
Platform::String^ LinphoneTesterNative::testSuiteName(int index)
{
const char *cname = liblinphone_tester_test_suite_name(index);
wchar_t wcname[MAX_SUITE_NAME_SIZE];
mbstowcs(wcname, cname, sizeof(wcname));
return ref new String(wcname);
}
Platform::String^ LinphoneTesterNative::testName(Platform::String^ suiteName, int testIndex)
{
std::wstring suitename = suiteName->Data();
char csuitename[MAX_SUITE_NAME_SIZE] = { 0 };
wcstombs(csuitename, suitename.c_str(), sizeof(csuitename));
const char *cname = liblinphone_tester_test_name(csuitename, testIndex);
wchar_t wcname[MAX_SUITE_NAME_SIZE];
mbstowcs(wcname, cname, sizeof(wcname));
return ref new String(wcname);
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "liblinphone_tester.h"
namespace linphone_tester_native
{
enum OutputTraceLevel {
Debug,
Message,
Warning,
Error,
Raw
};
public interface class OutputTraceListener
{
public:
void outputTrace(int level, Platform::String^ msg);
};
public ref class LinphoneTesterNative sealed
{
public:
LinphoneTesterNative();
virtual ~LinphoneTesterNative();
void setOutputTraceListener(OutputTraceListener^ traceListener);
unsigned int nbTestSuites();
unsigned int nbTests(Platform::String^ suiteName);
Platform::String^ testSuiteName(int index);
Platform::String^ testName(Platform::String^ suiteName, int testIndex);
void run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose);
};
}

View file

@ -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\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

View file

@ -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%\

View file

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5dfa07b4-0be9-46a9-ba32-fdf5a55c580b}</ProjectGuid>
<RootNamespace>libxml2</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v110_wp80</PlatformToolset>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
</PropertyGroup>
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>LIBXML_MODULES_ENABLED</UndefinePreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<ForcedIncludeFiles>$(ProjectDir)libxml2_port.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include</Command>
</PreBuildEvent>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<AdditionalIncludeDirectories>$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32;_WINDLL;_USRDLL;NDEBUG;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>LIBXML_MODULES_ENABLED</UndefinePreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<ForcedIncludeFiles>$(ProjectDir)libxml2_port.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include</Command>
</PreBuildEvent>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32;WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>LIBXML_MODULES_ENABLED</UndefinePreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<ForcedIncludeFiles>$(ProjectDir)libxml2_port.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include</Command>
</PreBuildEvent>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<AdditionalIncludeDirectories>$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\..\..\libxml2\win32\VC10;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32;_WINDLL;_USRDLL;NDEBUG;_CRT_SECURE_NO_WARNINGS;HAVE_WIN32_THREADS;HAVE_COMPILER_TLS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>LIBXML_MODULES_ENABLED</UndefinePreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<ForcedIncludeFiles>$(ProjectDir)libxml2_port.h</ForcedIncludeFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ImportLibrary>$(TargetDir)$(TargetName)_dll.lib</ImportLibrary>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>install_headers.bat $(SolutionDir)$(Platform)\$(Configuration)\include</Command>
</PreBuildEvent>
<CustomBuildStep>
<Outputs>$(TargetDir)$(TargetName)_dll.lib;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<Reference Include="Windows" Condition="'$(Platform)'=='ARM'">
<IsWinMDFile>true</IsWinMDFile>
</Reference>
<Reference Include="platform.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\..\..\libxml2\c14n.c" />
<ClCompile Include="..\..\..\..\..\libxml2\catalog.c" />
<ClCompile Include="..\..\..\..\..\libxml2\chvalid.c" />
<ClCompile Include="..\..\..\..\..\libxml2\debugXML.c" />
<ClCompile Include="..\..\..\..\..\libxml2\dict.c" />
<ClCompile Include="..\..\..\..\..\libxml2\DOCBparser.c" />
<ClCompile Include="..\..\..\..\..\libxml2\encoding.c" />
<ClCompile Include="..\..\..\..\..\libxml2\entities.c" />
<ClCompile Include="..\..\..\..\..\libxml2\error.c" />
<ClCompile Include="..\..\..\..\..\libxml2\globals.c" />
<ClCompile Include="..\..\..\..\..\libxml2\hash.c" />
<ClCompile Include="..\..\..\..\..\libxml2\HTMLparser.c" />
<ClCompile Include="..\..\..\..\..\libxml2\HTMLtree.c" />
<ClCompile Include="..\..\..\..\..\libxml2\legacy.c" />
<ClCompile Include="..\..\..\..\..\libxml2\list.c" />
<ClCompile Include="..\..\..\..\..\libxml2\nanoftp.c" />
<ClCompile Include="..\..\..\..\..\libxml2\nanohttp.c" />
<ClCompile Include="..\..\..\..\..\libxml2\parser.c" />
<ClCompile Include="..\..\..\..\..\libxml2\parserInternals.c" />
<ClCompile Include="..\..\..\..\..\libxml2\pattern.c" />
<ClCompile Include="..\..\..\..\..\libxml2\relaxng.c" />
<ClCompile Include="..\..\..\..\..\libxml2\SAX.c" />
<ClCompile Include="..\..\..\..\..\libxml2\SAX2.c" />
<ClCompile Include="..\..\..\..\..\libxml2\schematron.c" />
<ClCompile Include="..\..\..\..\..\libxml2\threads.c" />
<ClCompile Include="..\..\..\..\..\libxml2\tree.c" />
<ClCompile Include="..\..\..\..\..\libxml2\uri.c" />
<ClCompile Include="..\..\..\..\..\libxml2\valid.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xinclude.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xlink.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlcatalog.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlIO.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlmemory.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlmodule.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlreader.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlregexp.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlsave.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlschemas.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlschemastypes.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlstring.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlunicode.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xmlwriter.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xpath.c" />
<ClCompile Include="..\..\..\..\..\libxml2\xpointer.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\..\..\libxml2\elfgcchack.h" />
<ClInclude Include="..\..\..\..\..\libxml2\libxml.h" />
<ClInclude Include="..\..\..\..\..\libxml2\win32\VC10\config.h" />
<ClInclude Include="libxml2_port.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsPhone\v$(TargetPlatformVersion)\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets" Condition="'$(Platform)'=='ARM'" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -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 */

View file

@ -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 <libxml/xmlexports.h>
#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 <ansidecl.h>
#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

View file

@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT([linphone],[3.6.1],[linphone-developers@nongnu.org])
AC_INIT([linphone],[3.6.99],[linphone-developers@nongnu.org])
AC_CANONICAL_SYSTEM
AC_CONFIG_SRCDIR([coreapi/linphonecore.c])
@ -23,6 +23,7 @@ LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to
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)
@ -35,9 +36,13 @@ 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 don't 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
@ -59,10 +64,10 @@ case $target in
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
CFLAGS="$CFLAGS -DTARGET_OS_IPHONE=1 "
ios_found=yes
;;
x86_64-apple-darwin*|i686-apple-darwin*)
@ -74,15 +79,24 @@ case $target in
esac
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])
@ -127,13 +141,15 @@ 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(ENABLE_NLS,1,[Tells whether localisation is possible])
AC_DEFINE(HAVE_GETTEXT,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)
@ -203,15 +219,10 @@ 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.])
else
build_tools=false
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)
@ -356,6 +367,25 @@ AC_ARG_ENABLE(debug,
esac],
[debug_enabled=no]
)
AS_CASE([$debug_enabled],
[yes],[
CFLAGS="$CFLAGS -g -DDEBUG"
CXXFLAGS="$CXXFLAGS -g -DDEBUG"
OBJCFLAGS="$OBJCFLAGS -g -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,
@ -387,13 +417,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
@ -424,26 +447,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
if test "$console_ui" = "true" ; then
dnl check gnu readline
LP_CHECK_READLINE
@ -604,7 +607,15 @@ AC_ARG_ENABLE(strict,
[strictness=yes]
)
STRICT_OPTIONS="-Wall "
STRICT_OPTIONS="-Wall"
#for clang
case $CC in
*clang*)
STRICT_OPTIONS="$STRICT_OPTIONS -Qunused-arguments"
;;
esac
if test "$strictness" = "yes" ; then
STRICT_OPTIONS="$STRICT_OPTIONS -Werror"
@ -686,6 +697,15 @@ if test x$enable_msg_storage != xfalse; then
fi
PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.2.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)
@ -693,11 +713,8 @@ 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"
LINPHONE_LIBS="-L${libdir} -llinphone -llpc2xml -lxml2lpc"
if test x$mingw_found = xyes ; then
LINPHONE_LIBS="$LINPHONE_LIBS $OSIP_LIBS"
fi
AC_SUBST(LINPHONE_CFLAGS)
AC_SUBST(LINPHONE_LIBS)
@ -740,18 +757,62 @@ AC_SUBST(ORTP_LIBS)
AC_SUBST([ORTP_VERSION])
AC_SUBST([ORTP_DIR])
AC_ARG_ENABLE(tests_enabled,
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_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
@ -769,9 +830,11 @@ AC_CONFIG_FILES([
m4/Makefile
po/Makefile.in
pixmaps/Makefile
include/Makefile
coreapi/Makefile
coreapi/help/Makefile
coreapi/help/Doxyfile
tester/Makefile
gtk/Makefile
console/Makefile
daemon/Makefile
@ -804,7 +867,8 @@ printf "* %-30s %s\n" "zRTP encryption (GPLv3)" $zrtp
printf "* %-30s %s\n" "uPnP support" $build_upnp
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."

View file

@ -3,19 +3,17 @@
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) \
$(ORTP_CFLAGS) \
$(MEDIASTREAMER_CFLAGS) \
$(VIDEO_CFLAGS) \
$(READLINE_CFLAGS) \
$(OSIP_CFLAGS) \
$(ORTP_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(MEDIASTREAMER_CFLAGS)
$(SQLITE3_CFLAGS)
if BUILD_CONSOLE

View file

@ -1022,7 +1022,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 +1082,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 +1110,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 */
@ -1615,7 +1616,7 @@ linphonec_proxy_add(LinphoneCore *lc)
continue;
}
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);
@ -1852,7 +1853,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;
}
@ -1929,9 +1930,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(NULL,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 +1941,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);
@ -2030,7 +2029,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" ,
@ -2110,7 +2109,13 @@ static int lpc_cmd_speak(LinphoneCore *lc, char *args){
memset(voice,0,sizeof(voice));
sscanf(args,"%63s",voice);
sentence=args+strlen(voice);
#ifdef __APPLE__
wavfile=mktemp("/tmp/linphonec-espeak-XXXXXX");
#else
wavfile=tempnam("/tmp/","linphonec-espeak-");
#endif
snprintf(cl,sizeof(cl),"espeak -v %s -s 100 -w %s --stdin",voice,wavfile);
file=popen(cl,"w");
if (file==NULL){

View file

@ -117,13 +117,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,
@ -256,7 +255,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 +271,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 +280,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);
}
@ -628,8 +628,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,7 +638,7 @@ 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);
@ -734,7 +734,8 @@ linphonec_init(int argc, char **argv)
*/
linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
linphone_core_enable_video_capture(linphonec, vcap_enabled);
linphone_core_enable_video_display(linphonec, display_enabled);
if (display_enabled && window_id != 0)
{
printf ("Setting window_id: 0x%x\n", window_id);
@ -1074,7 +1075,7 @@ linphonec_initialize_readline()
rl_attempted_completion_function = linephonec_readline_completion;
/* printf("Readline initialized.\n"); */
setlinebuf(stdout);
setlinebuf(stdout);
return 0;
}

View file

@ -25,9 +25,9 @@
#ifdef WIN32
#include <ws2tcpip.h>
#include <windows.h>
#include <winbase.h>
#include <ws2tcpip.h>
#include <ctype.h>
#include <conio.h>
#else

View file

@ -1,7 +1,8 @@
GITVERSION_FILE=liblinphone_gitversion.h
GITVERSION_FILE_TMP=liblinphone_gitversion.h.tmp
GITDESCRIBE=`git describe --always`
GITREVISION=`git rev-parse HEAD`
GITDESCRIBE=`cd $(top_srcdir) && git describe --always`
GIT_TAG=`cd $(top_srcdir) && git describe --abbrev=0`
GITREVISION=`cd $(top_srcdir) && git rev-parse HEAD`
ECHO=/bin/echo
@ -16,7 +17,7 @@ CLEANFILES=$(GITVERSION_FILE)
## Process this file with automake to produce Makefile.in
linphone_includedir=$(includedir)/linphone
linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonecore_utils.h lpconfig.h sipsetup.h
linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonepresence.h linphonecore_utils.h lpconfig.h sipsetup.h event.h
if BUILD_TUNNEL
linphone_include_HEADERS+=linphone_tunnel.h
@ -27,10 +28,7 @@ lib_LTLIBRARIES=liblinphone.la
liblinphone_la_SOURCES=\
linphonecore.c linphonecore.h private.h\
offeranswer.c offeranswer.h\
sal.c sal.h \
sal_eXosip2.c sal_eXosip2.h\
sal_eXosip2_sdp.c \
sal_eXosip2_presence.c \
sal.c \
callbacks.c \
misc.c \
address.c \
@ -48,12 +46,27 @@ liblinphone_la_SOURCES=\
ec-calibrator.c \
conference.c \
message_storage.c \
info.c \
event.c event.h \
$(GITVERSION_FILE)
if BUILD_UPNP
liblinphone_la_SOURCES+=upnp.c upnp.h
endif
liblinphone_la_SOURCES+= bellesip_sal/sal_address_impl.c \
bellesip_sal/sal_impl.c bellesip_sal/sal_impl.h \
bellesip_sal/sal_op_impl.c \
bellesip_sal/sal_op_call.c \
bellesip_sal/sal_op_registration.c \
bellesip_sal/sal_sdp.c \
bellesip_sal/sal_op_message.c \
bellesip_sal/sal_op_presence.c \
bellesip_sal/sal_op_publish.c \
bellesip_sal/sal_op_call_transfer.c \
bellesip_sal/sal_op_info.c \
bellesip_sal/sal_op_events.c
if BUILD_WIZARD
liblinphone_la_SOURCES+=sipwizard.c
endif
@ -69,16 +82,32 @@ endif
liblinphone_la_LDFLAGS= -version-info $(LIBLINPHONE_SO_VERSION) -no-undefined
if HAVE_LD_OUTPUT_DEF
liblinphone_la_LDFLAGS += -Wl,--output-def,liblinphone-$(LIBLINPHONE_SO_CURRENT).def
defexecdir = $(libdir)
defexec_DATA = liblinphone-$(LIBLINPHONE_SO_CURRENT).def
CLEANFILES = $(defexec_DATA)
liblinphone-$(LIBLINPHONE_SO_CURRENT).def: liblinphone.la
if BUILD_WIN32
defexec_DATA += liblinphone-$(LIBLINPHONE_SO_CURRENT).lib
liblinphone-$(LIBLINPHONE_SO_CURRENT).lib: liblinphone-$(LIBLINPHONE_SO_CURRENT).def liblinphone.la
$(DLLTOOL) --dllname liblinphone-$(LIBLINPHONE_SO_CURRENT).dll --input-def liblinphone-$(LIBLINPHONE_SO_CURRENT).def --output-lib $@ liblinphone.la
endif
endif
liblinphone_la_LIBADD= \
$(EXOSIP_LIBS) \
$(SIPSTACK_LIBS) \
$(MEDIASTREAMER_LIBS) \
$(ORTP_LIBS) $(OPENSSL_LIBS) \
$(TUNNEL_LIBS) \
$(LIBSOUP_LIBS) \
$(SQLITE3_LIBS)
$(SQLITE3_LIBS) \
$(LIBXML2_LIBS)
if BUILD_TESTS
if ENABLE_TESTS
noinst_PROGRAMS=test_lsd test_ecc test_numbers
test_lsd_SOURCES=test_lsd.c
@ -95,14 +124,13 @@ test_numbers_LDADD=liblinphone.la $(liblinphone_la_LIBADD)
endif
AM_CPPFLAGS=\
-I$(top_srcdir)
-I$(top_srcdir) -I$(top_srcdir)/include
AM_CFLAGS=\
$(STRICT_OPTIONS) -DIN_LINPHONE \
$(ORTP_CFLAGS) \
$(MEDIASTREAMER_CFLAGS) \
$(OSIP_CFLAGS) \
$(EXOSIP_CFLAGS) \
$(SIPSTACK_CFLAGS) \
$(LIBSOUP_CFLAGS) \
-DENABLE_TRACE \
-DLOG_DOMAIN=\"LinphoneCore\" \
@ -110,16 +138,23 @@ AM_CFLAGS=\
-DORTP_INET6 \
$(VIDEO_CFLAGS) \
$(TUNNEL_CFLAGS) \
$(SQLITE3_CFLAGS)
$(SQLITE3_CFLAGS) \
$(LIBXML2_CFLAGS)
if BUILD_WIZARD
AM_CFLAGS+= -DBUILD_WIZARD
endif
AM_CFLAGS+= -DUSE_BELLESIP
AM_CXXFLAGS=$(AM_CFLAGS)
make_gitversion_h:
if test "$(GITDESCRIBE)" != "" ; then \
if test "$(GIT_TAG)" != "$(PACKAGE_VERSION)" ; then \
echo "*** PACKAGE_VERSION and git tag differ. Please put them identical."; \
exit 1; \
fi ; \
$(ECHO) -n "#define LIBLINPHONE_GIT_VERSION \"$(GITDESCRIBE)\"" > $(GITVERSION_FILE_TMP) ; \
elif test "$(GITREVISION)" != "" ; then \
$(ECHO) -n "#define LIBLINPHONE_GIT_VERSION \"$(LINPHONE_VERSION)_$(GITREVISION)\"" > $(GITVERSION_FILE_TMP) ; \

View file

@ -16,8 +16,11 @@
#include "ortp/rtpsession.h"
#include "linphonecore.h"
#include "linphonecore_utils.h"
#ifndef USE_BELLESIP
#include "eXosip2/eXosip_transport_hook.h"
#endif
#include "tunnel/udp_mirror.hh"
#include "private.h"
#ifdef ANDROID
#include <android/log.h>
@ -27,6 +30,8 @@
using namespace belledonnecomm;
using namespace ::std;
#ifndef USE_BELLESIP
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){
@ -99,6 +104,7 @@ int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3,
return select(max_fds,s1,s2,s3,tv);
}
}
#endif
void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) {
@ -181,7 +187,9 @@ void TunnelManager::start() {
}
mTunnelClient->start();
#ifndef USE_BELLESIP
if (mSipSocket == NULL) mSipSocket =mTunnelClient->createSocket(5060);
#endif
}
bool TunnelManager::isStarted() {
@ -209,7 +217,9 @@ int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flag
TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
,mCore(lc)
#ifndef USE_BELLESIP
,mSipSocket(NULL)
#endif
,mCallback(NULL)
,mEnabled(false)
,mTunnelClient(NULL)
@ -217,10 +227,12 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
,mReady(false)
,mHttpProxyPort(0){
#ifndef USE_BELLESIP
mExosipTransport.data=this;
mExosipTransport.recvfrom=eXosipRecvfrom;
mExosipTransport.sendto=eXosipSendto;
mExosipTransport.select=eXosipSelect;
#endif
linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this);
mTransportFactories.audio_rtcp_func=sCreateRtpTransport;
mTransportFactories.audio_rtcp_func_data=this;
@ -237,6 +249,9 @@ TunnelManager::~TunnelManager(){
}
void TunnelManager::stopClient(){
#ifdef USE_BELLESIP
sal_disable_tunnel(mCore->sal);
#else
eXosip_transport_hook_register(NULL);
if (mSipSocket != NULL){
sMutex.lock();
@ -244,6 +259,7 @@ void TunnelManager::stopClient(){
mSipSocket = NULL;
sMutex.unlock();
}
#endif
if (mTunnelClient){
delete mTunnelClient;
mTunnelClient=NULL;
@ -258,16 +274,9 @@ void TunnelManager::processTunnelEvent(const Event &ev){
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);
sal_enable_tunnel(mCore->sal, mTunnelClient);
//register
if (lProxy) {
linphone_proxy_config_done(lProxy);
@ -323,7 +332,15 @@ void TunnelManager::enable(bool isEnable) {
mReady=false;
linphone_core_set_rtp_transport_factories(mCore,NULL);
eXosip_transport_hook_register(NULL);
sal_disable_tunnel(mCore->sal);
// Set empty transports to force the setting of regular transport, otherwise it is not applied
LCSipTransports lTransport;
lTransport.udp_port = 0;
lTransport.tcp_port = 0;
lTransport.tls_port = 0;
lTransport.dtls_port = 0;
linphone_core_set_sip_transports(mCore, &lTransport);
//Restore transport and firewall policy
linphone_core_set_sip_transports(mCore, &mRegularTransport);
linphone_core_set_firewall_policy(mCore, mPreviousFirewallPolicy);
@ -366,8 +383,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;
@ -377,7 +398,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*/
@ -390,7 +411,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) {

View file

@ -15,9 +15,12 @@
#include "tunnel/client.hh"
#include "linphonecore.h"
#ifndef USE_BELLESIP
extern "C" {
#include "eXosip2/eXosip_transport_hook.h"
}
#endif
namespace belledonnecomm {
class TunnelClient;
class UdpMirrorClient;
@ -159,8 +162,10 @@ class UdpMirrorClient;
void postEvent(const Event &ev);
LinphoneCore* mCore;
LCSipTransports mRegularTransport;
#ifndef USE_BELLESIP
TunnelSocket *mSipSocket;
eXosip_transport_hooks_t mExosipTransport;
#endif
StateCallback mCallback;
void * mCallbackData;
bool mEnabled;

View file

@ -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,13 @@ 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)
**/
bool_t linphone_address_is_secure(const LinphoneAddress *uri){
return sal_address_is_secure(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;
@ -145,26 +175,33 @@ 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.
* Destroys a LinphoneAddress object (actually calls linphone_address_unref()).
**/
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);
}
LinphoneAddress * linphone_core_create_address(LinphoneCore *lc, const char *address) {
return linphone_address_new(address);
}
/** @} */

View file

@ -33,19 +33,26 @@
/**
* 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.
* 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. 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().
**/
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);
if (domain!=NULL && (strlen(domain)>0)) obj->domain=ms_strdup(domain);
obj->works=FALSE;
return obj;
}
@ -57,6 +64,7 @@ static LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo *ai){
if (ai->passwd) obj->passwd=ms_strdup(ai->passwd);
if (ai->ha1) obj->ha1=ms_strdup(ai->ha1);
if (ai->realm) obj->realm=ms_strdup(ai->realm);
if (ai->domain) obj->domain=ms_strdup(ai->domain);
obj->works=FALSE;
obj->usecount=0;
return obj;
@ -83,6 +91,11 @@ const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i){
const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *i){
return i->realm;
}
const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *i){
return i->domain;
}
const char *linphone_auth_info_get_ha1(const LinphoneAuthInfo *i){
return i->ha1;
}
@ -121,7 +134,7 @@ void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid){
}
/**
* Sets realm.
* Set realm.
**/
void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm){
if (info->realm){
@ -130,6 +143,19 @@ void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm){
}
if (realm && strlen(realm)>0) info->realm=ms_strdup(realm);
}
/**
* Set domain for which this authentication is valid. 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.
**/
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);
}
/**
* Sets ha1.
**/
@ -150,6 +176,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);
}
@ -161,28 +188,36 @@ void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, in
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)){
@ -194,14 +229,9 @@ LinphoneAuthInfo *linphone_auth_info_new_from_config_file(LpConfig * config, int
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){
@ -230,41 +260,62 @@ 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 && strcmp(realm,pinfo->realm)==0
&& 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) {
return pinfo;
} else if (!domain) {
return pinfo;
}
}
}
if (ret==NULL && candidate!=NULL)
ret=candidate;
return ret;
}
/**
* Find authentication info matching realm, username, domain criterias.
* 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);
}
return ai;
}
static void write_auth_infos(LinphoneCore *lc){
MSList *elem;
int i;
@ -277,6 +328,10 @@ 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.
*
@ -289,25 +344,33 @@ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info)
MSList *l;
/* 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);
}
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++;
}
@ -329,7 +392,7 @@ 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);*/

View file

@ -0,0 +1,174 @@
/*
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);
if (uri) {
if (belle_sip_uri_is_secure(uri)) return "sips";
else return "sip";
} else
return NULL;
}
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_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_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);
}
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* uri = belle_sip_header_address_get_uri(header_addr);
char tmp[1024]={0};
size_t off=0;
belle_sip_object_marshal((belle_sip_object_t*)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_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));
}
void sal_address_destroy(SalAddress *addr){
sal_address_unref(addr);
}

View file

@ -0,0 +1,919 @@
/*
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_logs(){
belle_sip_set_log_level(BELLE_SIP_LOG_MESSAGE);
}
void sal_disable_logs() {
belle_sip_set_log_level(BELLE_SIP_LOG_ERROR);
}
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,sal_op_ref(op));
}
}
void sal_remove_pending_auth(Sal *sal, SalOp *op){
if (ms_list_find(sal->pending_auths,op)){
sal->pending_auths=ms_list_remove(sal->pending_auths,op);
sal_op_unref(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);
sal_add_pending_auth(op->base.root,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,&auth_list)) {
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 {
ms_message("No auth info found for [%s]",sal_op_get_from(op));
if (is_within_dialog) {
belle_sip_object_unref(new_request);
}
if (op->auth_info) sal_auth_info_delete(op->auth_info);
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.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));
if (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;
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);
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,501);
belle_sip_provider_send_response(sal->prov,resp);
return;
}
if (!op->base.from_address) {
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)));
sal_op_set_from_address(op,(SalAddress*)address);
belle_sip_object_unref(address);
}
if (!op->base.to_address) {
to=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_to_t);
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)));
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.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));
if (op->state == SalOpStateTerminated) {
belle_sip_message("Op is terminated, nothing to do with this [%i]",response_code);
return;
}
if (!op->base.remote_ua) {
sal_op_set_remote_ua(op,BELLE_SIP_MESSAGE(response));
}
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.process_response_event) {
/*handle authorization*/
switch (response_code) {
case 200:
break;
case 401:
case 407:
/*belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*//*remove op from trans*/
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;
}
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;
}
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.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.process_transaction_terminated) {
op->callbacks.process_transaction_terminated(op,event);
} else {
ms_message("Unhandled transaction terminated [%p]",trans);
}
if (op && client_transaction) sal_op_unref(op); /*because every client 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);
sal->auto_contacts=TRUE;
sal->user_agent=belle_sip_header_user_agent_new();
#if defined(PACKAGE_NAME) && defined(LINPHONE_VERSION)
belle_sip_header_user_agent_add_product(sal->user_agent, PACKAGE_NAME "/" LINPHONE_VERSION);
#endif
sal_append_stack_string_to_user_agent(sal);
belle_sip_object_ref(sal->user_agent);
belle_sip_set_log_handler(_belle_sip_log);
sal->stack = belle_sip_stack_new(NULL);
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*/
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.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->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;
}
int sal_add_listen_port(Sal *ctx, SalAddress* addr){
int result;
belle_sip_listening_point_t* 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);
set_tls_properties(ctx);
} else {
return -1;
}
return result;
}
int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure) {
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);
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_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 ;
}
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);
}
}
return ;
}
int sal_enable_tunnel(Sal *ctx, void *tunnelclient) {
#ifdef TUNNEL_ENABLED
belle_sip_listening_point_t *lp;
int result;
sal_unlisten_ports(ctx);
lp = belle_sip_tunnel_listening_point_new(ctx->stack, tunnelclient);
if (lp == NULL) return -1;
belle_sip_listening_point_set_keep_alive(lp, ctx->keep_alive);
result = belle_sip_provider_add_listening_point(ctx->prov, lp);
set_tls_properties(ctx);
return result;
#else
return 0;
#endif
}
void sal_disable_tunnel(Sal *ctx) {
#ifdef TUNNEL_ENABLED
sal_unlisten_ports(ctx);
#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_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;
char *tmp=ms_strdup_printf("%s: %s\r\n",name,value);
if (msg==NULL){
msg=(belle_sip_message_t*)belle_sip_request_new();
belle_sip_object_ref(msg);
}
h=BELLE_SIP_HEADER(belle_sip_header_extension_parse(tmp));
ms_free(tmp);
if (h==NULL){
belle_sip_error("Fail to parse extension 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){
if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_extension_t)){
return belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(h));
}else{
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){
belle_sip_free(sal->uuid);
sal->uuid=NULL;
}
if (uuid)
sal->uuid=belle_sip_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;
}
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_make_supported_header(sal));
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;
}
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);
}
unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){
return belle_sip_random_bytes(ret,size);
}

View file

@ -0,0 +1,162 @@
/*
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 *up; /*user pointer*/
int session_expires;
unsigned int keep_alive;
char *root_ca;
char *uuid;
int refresher_retry_after; /*retry after value for refresher*/
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;
};
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;
belle_sip_listener_callbacks_t callbacks;
belle_sip_client_transaction_t *pending_auth_transaction;
belle_sip_server_transaction_t* pending_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;
bool_t supports_session_timers;
SalOpState state;
SalOpDir dir;
belle_sip_refresher_t* refresher;
int ref;
SalOpType type;
SalPrivacyMask privacy;
belle_sip_header_t *event; /*used by SalOpSubscribe kinds*/
bool_t auto_answer_asked;
bool_t sdp_offering;
bool_t call_released;
bool_t manual_refresher;
};
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,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size);
void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal_reason) ;
/*presence*/
void sal_op_presence_fill_cbs(SalOp*op);
/*messaging*/
void sal_op_message_fill_cbs(SalOp*op);
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);
belle_sip_header_t * sal_make_supported_header(Sal *sal);
#endif /* SAL_IMPL_H_ */

View file

@ -0,0 +1,813 @@
/*
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"
/*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;
}
}
/*used when the SalOp was ref'd by the dialog, in which case we rely only on the dialog terminated notification*/
static void call_set_released_and_unref(SalOp* op) {
call_set_released(op);
sal_op_unref(op);
}
static void call_set_error(SalOp* op,belle_sip_response_t* response){
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
char* reason=(char*)belle_sip_response_get_reason_phrase(response);
int code = belle_sip_response_get_status_code(response);
if (reason_header){
reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header)));
}
sal_compute_sal_errors_from_code(code,&error,&sr);
op->base.root->callbacks.call_failure(op,error,sr,reason,code);
if (reason_header != NULL){
ms_free(reason);
}
}
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=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);
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;i<h->result->n_active_streams;++i){
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 == 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_OK;
size_t length = 0;
char buff[2048];
if (session_desc) {
content_type = belle_sip_header_content_type_create("application","sdp");
error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,sizeof(buff),&length);
if (error != BELLE_SIP_OK) {
ms_error("Buffer too small or sdp too big");
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_set_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*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Service Unavailable",503);
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_and_unref
, 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;
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(response)))) {
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
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 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);
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,belle_sip_response_event_get_dialog(event));
dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,op->dialog,belle_sip_dialog_state_to_string(dialog_state));
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL:
case BELLE_SIP_DIALOG_EARLY: {
if (strcmp("INVITE",belle_sip_request_get_method(req))==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<300) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
} else if (code>=300){
call_set_error(op,response);
if (op->dialog==NULL) call_set_released(op);
}
}
}
break;
case BELLE_SIP_DIALOG_CONFIRMED: {
switch (op->state) {
case SalOpStateEarly:/*invite case*/
case SalOpStateActive: /*re-invite case*/
if (code >=200
&& code<300
&& strcmp("INVITE",belle_sip_request_get_method(req))==0) {
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 && strcmp("INVITE",belle_sip_request_get_method(req))==0){
call_set_error(op,response);
} else {
/*ignoring*/
}
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 (code >= 300){
call_set_error(op,response);
}
}
break;
default: {
ms_error("call op [%p] receive answer [%i] not implemented",op,code);
}
break;
}
}
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*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Request Timeout",408);
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;
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))) {
if (op->dialog==NULL) 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,500);
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}
static void process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) {
belle_sdp_session_description_t* sdp;
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(invite)))) {
op->sdp_offering=FALSE;
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
belle_sip_object_unref(sdp);
}else
op->sdp_offering=TRUE;
}
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;
if (strcmp("ACK",belle_sip_request_get_method(req))!=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),op);
sal_op_ref(op);
}
if (strcmp("INVITE",belle_sip_request_get_method(req))==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 (!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",belle_sip_request_get_method(req))==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");
}
process_sdp_for_invite(op,req);
if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) {
if( strstr(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(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",belle_sip_request_get_method(req))==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",belle_sip_request_get_method(req))==0) {
resp=sal_op_create_response_from_request(op,req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
} 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",belle_sip_request_get_method(req))==0) {
if (op->sdp_offering){
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(req)))){
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);
}
}
/*FIXME
if (op->reinvite){
op->reinvite=FALSE;
}*/
op->base.root->callbacks.call_ack(op);
} else if(strcmp("BYE",belle_sip_request_get_method(req))==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",belle_sip_request_get_method(req))==0) {
/*re-invite*/
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;
}
process_sdp_for_invite(op,req);
op->base.root->callbacks.call_updating(op);
} else if (strcmp("INFO",belle_sip_request_get_method(req))==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",belle_sip_request_get_method(req))==0) {
sal_op_process_refer(op,event,server_transaction);
} else if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) {
sal_op_call_process_notify(op,event,server_transaction);
} else if (strcmp("OPTIONS",belle_sip_request_get_method(req))==0) {
resp=sal_op_create_response_from_request(op,req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
} else if (strcmp("CANCEL",belle_sip_request_get_method(req))==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{
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(){
belle_sip_header_allow_t* header_allow;
header_allow = belle_sip_header_allow_create("INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
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()));
if (op->base.root->session_expires!=0){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "200"));
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");
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);
}
void sal_op_call_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=call_process_io_error;
op->callbacks.process_response_event=call_process_response;
op->callbacks.process_timeout=call_process_timeout;
op->callbacks.process_transaction_terminated=call_process_transaction_terminated;
op->callbacks.process_request_event=process_request_event;
op->callbacks.process_dialog_terminated=process_dialog_terminated;
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)
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(belle_sip_header_extension_create("Require","100rel")));
belle_sip_message_add_header((belle_sip_message_t*)ringing_response,BELLE_SIP_HEADER(belle_sip_header_extension_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;
if (!h->pending_server_trans) {
ms_error("No transaction to accept for op [%p]",h);
return -1;
}
/* sends a 200 OK */
response = sal_op_create_response_from_request(h,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(h->pending_server_trans)),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()));
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"));
}
}
if ((contact_header=sal_op_create_contact(h))) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact_header));
}
handle_offer_answer_response(h,response);
belle_sip_server_transaction_send_response(h->pending_server_trans,response);
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);
if (reason==SalReasonRedirect){
if (redirection!=NULL) {
if (strstr(redirection,"sip:")!=0) status=302;
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");
}
}
response = sal_op_create_response_from_request(op,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),status);
if (contact) belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact));
belle_sip_server_transaction_send_response(op->pending_server_trans,response);
return 0;
}
int sal_call_update(SalOp *op, const char *subject){
belle_sip_request_t *reinvite=belle_sip_dialog_create_request(op->dialog,"INVITE");
if (reinvite){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(reinvite),belle_sip_header_create( "Subject", subject));
sal_op_fill_invite(op, reinvite);
return sal_op_send_request(op,reinvite);
}
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[] =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
"<media_control>"
" <vc_primitive>"
" <to_encoder>"
" <picture_fast_update></picture_fast_update>"
" </to_encoder>"
" </vc_primitive>"
"</media_control>";
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;
}

View file

@ -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,501);
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_extension_get_value(BELLE_SIP_HEADER_EXTENSION(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==100){
status=SalReferTrying;
}else if (code==200){
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);
}
}

View file

@ -0,0 +1,268 @@
/*
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;
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
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_compute_sal_errors_from_code(status_code,&error,&sr);
op->base.root->callbacks.subscribe_response(op,sss,error,sr);
}
}
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));
}
}
}
void sal_op_subscribe_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=subscribe_process_io_error;
op->callbacks.process_response_event=subscribe_response_event;
op->callbacks.process_timeout=subscribe_process_timeout;
op->callbacks.process_transaction_terminated=subscribe_process_transaction_terminated;
op->callbacks.process_request_event=subscribe_process_request_event;
op->callbacks.process_dialog_terminated=subscribe_process_dialog_terminated;
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 (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);
}

View file

@ -0,0 +1,608 @@
/*
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*/
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);
if (op->auth_info) {
sal_remove_pending_auth(op->base.root,op);
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->event) belle_sip_object_unref(op->event);
__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_secure(contact_uri,sal_op_is_secure(op));
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("\"<urn:uuid:%s>\"",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;
}
belle_sip_header_t * sal_make_supported_header(Sal *sal){
return belle_sip_header_create("Supported","replaces, outbound");
}
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;
char token[10];
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 <sip:anonymous@anonymous.invalid>",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*)belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(sal_op_get_to_address(op))));
belle_sip_uri_set_secure(req_uri,sal_op_is_secure(op));
to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_to_address(op)),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 (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),sal_make_supported_header(op->base.root));
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) {
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(belle_sip_header_t *h, belle_sip_message_t *msg){
if (belle_sip_message_get_header(msg,belle_sip_header_get_name(h))==NULL)
belle_sip_message_add_header(msg,h);
}
static 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_for_each2(l,(void (*)(void *, void *))add_headers,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;
_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);
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 (belle_sip_provider_get_listening_point(prov,"UDP")==0){
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);
}
}
}
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 (add_contact) {
contact = sal_op_create_contact(op);
belle_sip_message_set_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(contact));
}
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);
}
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 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 SalReasonMedia:
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;
break;
}
return ret;
}
void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal_reason) {
switch(code) {
case 400:
*sal_err=SalErrorUnknown;
break;
case 401:
case 407:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonUnauthorized;
break;
case 403:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonForbidden;
break;
case 404:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonNotFound;
break;
case 415:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonMedia;
break;
case 422:
ms_error ("422 not implemented yet");;
break;
case 480:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonTemporarilyUnavailable;
break;
case 486:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonBusy;
break;
case 487:
break;
case 488:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonNotAcceptable;
break;
case 491:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonRequestPending;
break;
case 600:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonDoNotDisturb;
break;
case 603:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonDeclined;
break;
case 503:
*sal_err=SalErrorFailure;
*sal_reason=SalReasonServiceUnavailable;
break;
default:
if (code>=300){
*sal_err=SalErrorFailure;
*sal_reason=SalReasonUnknown;
}else if (code>=100){
*sal_err=SalErrorNone;
*sal_reason=SalReasonUnknown;
}else if (code==0){
*sal_err=SalErrorNoResponse;
}
/* no break */
}
}
/*return TRUE if error code*/
bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size) {
int code = belle_sip_response_get_status_code(response);
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
*sal_err=SalErrorUnknown;
*sal_reason = SalReasonUnknown;
if (reason_header){
snprintf(reason
,reason_size
,"%s %s"
,belle_sip_response_get_reason_phrase(response)
,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header)));
} else {
strncpy(reason,belle_sip_response_get_reason_phrase(response),reason_size);
}
if (code>=400) {
sal_compute_sal_errors_from_code(code,sal_err,sal_reason);
return TRUE;
} else {
return FALSE;
}
}
void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {
/*check if dialog has changed*/
if (dialog && dialog != op->dialog) {
ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op);
/*fixme, shouldn't we cancel previous dialog*/
if (op->dialog) {
belle_sip_dialog_set_application_data(op->dialog,NULL);
belle_sip_object_unref(op->dialog);
sal_op_unref(op);
}
op->dialog=dialog;
belle_sip_dialog_set_application_data(op->dialog,op);
sal_op_ref(op);
belle_sip_object_ref(op->dialog);
}
}
/*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))) {
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_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){
return sal_custom_header_find(op->base.recv_custom_headers,"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;
}

View file

@ -0,0 +1,30 @@
/*
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_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;
}

View file

@ -0,0 +1,170 @@
/*
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 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;
// belle_sip_object_t* source = belle_sip_io_error_event_get_source(event);
// if (BELLE_SIP_IS_INSTANCE_OF(source,belle_sip_transaction_t)) {
// /*reset op to make sure transaction terminated does not need op*/
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(source),NULL);
// }
process_error(op);
}
static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
SalOp* op=(SalOp*)user_ctx;
// belle_sip_client_transaction_t *client_transaction=belle_sip_timeout_event_get_client_transaction(event);
// belle_sip_server_transaction_t *server_transaction=belle_sip_timeout_event_get_server_transaction(event);
// /*reset op to make sure transaction terminated does not need op*/
// if (client_transaction) {
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);
// } else {
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),NULL);
// }
process_error(op);
}
static void process_response_event(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
/*belle_sip_client_transaction_t *client_transaction=belle_sip_response_event_get_client_transaction(event);*/
int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
SalTextDeliveryStatus status;
if (code>=100 && code <200)
status=SalTextDeliveryInProgress;
else if (code>=200 && code <300)
status=SalTextDeliveryDone;
else
status=SalTextDeliveryFailed;
if (status != SalTextDeliveryInProgress) {
/*reset op to make sure transaction terminated does not need op
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*/
}
op->base.root->callbacks.text_delivery_update(op,status);
}
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_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 void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
SalOp* op = (SalOp*)op_base;
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;
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);
SalMessage salmsg;
char message_id[256]={0};
int response_code=501;
char* from;
bool_t plain_text=FALSE;
bool_t external_body=FALSE;
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);
if (content_type && ((plain_text=is_plain_text(content_type))
|| (external_body=is_external_body(content_type)))) {
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;
salmsg.text=plain_text?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
salmsg.url=NULL;
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);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
response_code=200;
} else {
ms_error("Unsupported MESSAGE with content type [%s/%s]",belle_sip_header_content_type_get_type(content_type)
,belle_sip_header_content_type_get_subtype(content_type));
response_code=501; /*not implemented sound appropriate*/
}
resp = belle_sip_response_create_from_request(req,response_code);
belle_sip_server_transaction_send_response(server_transaction,resp);
sal_op_release(op);
}
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg){
belle_sip_request_t* req;
char content_type_raw[256];
size_t content_length = msg?strlen(msg):0;
time_t curtime=time(NULL);
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 (sal_op_get_contact(op)){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op)));
}
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),msg,content_length);
return sal_op_send_request(op,req);
}
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);
}
void sal_op_message_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=process_io_error;
op->callbacks.process_response_event=process_response_event;
op->callbacks.process_timeout=process_timeout;
op->callbacks.process_request_event=process_request_event;
op->type=SalOpMessage;
}

View file

@ -0,0 +1,363 @@
/*
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);
ms_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);
char reason[256]={0};
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_expires_t* expires;
if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) {
ms_error("subscription to [%s] rejected reason [%s]",sal_op_get_to(op),reason[0]!=0?reason:sal_reason_to_string(sr));
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);
}
}
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;
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;
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 (body==NULL){
ms_error("No body in NOTIFY received from [%s]",sal_op_get_from(op));
resp = sal_op_create_response_from_request(op, req, 415);
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}
presence_model = process_presence_notification(op, req);
if (presence_model != NULL) {
/* Presence notification body parsed successfully. */
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;
}
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 {
/* Formatting error in presence notification body. */
ms_error("Wrongly formatted presence notification received");
resp = sal_op_create_response_from_request(op, req, 400);
}
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;
}
}
void sal_op_presence_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=presence_process_io_error;
op->callbacks.process_response_event=presence_response_event;
op->callbacks.process_timeout=presence_process_timeout;
op->callbacks.process_transaction_terminated=presence_process_transaction_terminated;
op->callbacks.process_request_event=presence_process_request_event;
op->callbacks.process_dialog_terminated=presence_process_dialog_terminated;
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");
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);
}

View file

@ -0,0 +1,114 @@
/*
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;
/*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*/
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_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){
SalError err;
SalReason reason;
sal_compute_sal_errors_from_code(status_code,&err,&reason);
op->base.root->callbacks.on_publish_response(op,err,reason);
}
}
static void publish_response_event(void *userctx, const belle_sip_response_event_t *event){
SalOp *op=(SalOp*)userctx;
int code=belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
SalError err;
SalReason reason;
sal_compute_sal_errors_from_code(code,&err,&reason);
op->base.root->callbacks.on_publish_response(op,err,reason);
}
void sal_op_publish_fill_cbs(SalOp*op) {
op->callbacks.process_response_event=publish_response_event;
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 (sal_op_get_contact(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 (sal_op_get_contact(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);
return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener);
} 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);
}
}

View file

@ -0,0 +1,108 @@
/*
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;
SalError sal_err;
SalReason sal_reason;
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));
}
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;
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*/
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_compute_sal_errors_from_code(status_code,&sal_err,&sal_reason);
op->base.root->callbacks.register_failure(op,sal_err,sal_reason,reason_phrase);
if (op->auth_info) {
/*add pending auth*/
sal_add_pending_auth(op->base.root,op);
if (status_code==403)
op->base.root->callbacks.auth_failure(op,op->auth_info);
}
}
}
int sal_register(SalOp *op, const char *proxy, const char *from, int expires){
belle_sip_request_t *req;
belle_sip_uri_t* req_uri;
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)));
}
belle_sip_message_set_header(BELLE_SIP_MESSAGE(req),(belle_sip_header_t*)sal_op_create_contact(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);
}

View file

@ -0,0 +1,496 @@
/*
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 belle_sdp_media_description_t *stream_description_to_sdp ( 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_type_to_string ( stream->type )
,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
,stream->type==SalAudio?1:-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;
if (strchr(rtp_addr,':')!=NULL){
inet6=TRUE;
}else inet6=FALSE;
belle_sdp_media_description_set_connection(media_desc,belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr));
}
if ( stream->bandwidth>0 )
belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );
if ( stream->proto == SalProtoRtpSavp ) {
/* add crypto lines */
for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) {
switch ( stream->crypto[j].algo ) {
case AES_128_SHA1_80:
snprintf ( buffer, sizeof ( buffer ), "%d %s inline:%s",
stream->crypto[j].tag, "AES_CM_128_HMAC_SHA1_80", stream->crypto[j].master_key );
belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( "crypto",buffer ) );
break;
case AES_128_SHA1_32:
snprintf ( buffer, sizeof ( buffer ), "%d %s inline:%s",
stream->crypto[j].tag, "AES_CM_128_HMAC_SHA1_32", stream->crypto[j].master_key );
belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( "crypto",buffer ) );
break;
case AES_128_NO_AUTH:
ms_warning ( "Unsupported crypto suite: AES_128_NO_AUTH" );
break;
case NO_CIPHER_SHA1_80:
ms_warning ( "Unsupported crypto suite: NO_CIPHER_SHA1_80" );
break;
default:
j = SAL_CRYPTO_ALGO_MAX;
/* no break */
}
}
}
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);
}
}
return 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 ( "Talk" ) );
if ( (!sal_media_description_has_dir ( desc,SalStreamSendOnly ) && !sal_media_description_has_dir ( desc,SalStreamInactive ))
|| desc->ice_ufrag[0] != '\0' ) {
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));
for ( i=0; i<desc->n_total_streams; i++ ) {
belle_sdp_session_description_add_media_description ( session_desc,stream_description_to_sdp(desc,&desc->streams[i]));
}
return session_desc;
}
int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, SalMediaDescription *desc ) {
/*
typedef struct SalMediaDescription{
int refcount;
char addr[64];
char username[64];
int nstreams;
int bandwidth;
unsigned int session_ver;
unsigned int session_id;
SalStreamDescription streams[SAL_MEDIA_DESCRIPTION_MAX_STREAMS];
} SalMediaDescription;
*/
belle_sdp_connection_t* cnx;
belle_sip_list_t* media_desc_it;
belle_sdp_media_description_t* media_desc;
const char *mtype,*proto;
SalStreamDescription *stream;
belle_sdp_media_t* media;
belle_sip_list_t* mime_params=NULL;
belle_sip_list_t* mime_param_it=NULL;
belle_sdp_mime_parameter_t* mime_param;
PayloadType *pt;
belle_sip_list_t* attribute_it;
const belle_sdp_attribute_t* attribute;
int valid_count = 0;
char tmp[256], tmp2[256];
int nb=0;
SalStreamDir stream_dir=SalStreamSendRecv;
const char* value;
desc->n_active_streams = 0;
desc->n_total_streams = 0;
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 ) );
}
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" ) ) {
stream_dir=SalStreamSendRecv;
} else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) {
stream_dir=SalStreamSendOnly;
} else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) {
stream_dir=SalStreamRecvOnly;
} else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) {
stream_dir=SalStreamInactive;
}
/* 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));
value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd");
if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd));
value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite");
if (value) desc->ice_lite = TRUE;
for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc )
; media_desc_it!=NULL
; media_desc_it=media_desc_it->next ) {
int nb_ice_candidates=0;
media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data );
stream=&desc->streams[desc->n_total_streams];
media=belle_sdp_media_description_get_media ( media_desc );
memset ( stream,0,sizeof ( *stream ) );
proto = belle_sdp_media_get_protocol ( media );
stream->proto=SalProtoUnknown;
if ( proto ) {
if ( strcasecmp ( proto,"RTP/AVP" ) ==0 )
stream->proto=SalProtoRtpAvp;
else if ( strcasecmp ( proto,"RTP/SAVP" ) ==0 ) {
stream->proto=SalProtoRtpSavp;
}
}
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 ) );
}
stream->rtp_port=belle_sdp_media_get_media_port ( media );
if ( stream->rtp_port > 0 )
desc->n_active_streams++;
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=stream_dir; /*takes default value if not present*/
}
/* for each payload type */
mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc );
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 ) );
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 );
/* 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));
} else {
ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb);
}
}
/* read crypto lines if any */
if ( stream->proto == SalProtoRtpSavp ) {
valid_count=0;
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",
&stream->crypto[valid_count].tag,
tmp,
tmp2 );
ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'",
stream->crypto[valid_count].tag,
tmp,
tmp2 );
if ( nb == 3 ) {
if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_80",tmp ) == 0 )
stream->crypto[valid_count].algo = AES_128_SHA1_80;
else if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_32",tmp ) == 0 )
stream->crypto[valid_count].algo = AES_128_SHA1_32;
else {
ms_warning ( "Failed to parse crypto-algo: '%s'", tmp );
stream->crypto[valid_count].algo = 0;
}
if ( stream->crypto[valid_count].algo ) {
strncpy ( stream->crypto[valid_count].master_key, tmp2, 41 );
stream->crypto[valid_count].master_key[40] = '\0';
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 );
}
/* Get ICE candidate attributes if any */
for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) {
const char *att_name;
attribute=(belle_sdp_attribute_t*)attribute_it->data;
att_name=belle_sdp_attribute_get_name(attribute);
value=belle_sdp_attribute_get_value(attribute);
if ((keywordcmp("candidate", att_name) == 0) && (value != NULL)) {
SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates];
int nb = sscanf(value, "%s %u UDP %u %s %d typ %s raddr %s rport %d",
candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port,
candidate->type, candidate->raddr, &candidate->rport);
if ((nb == 6) || (nb == 8)) 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));
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));
} else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) {
strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd));
} else if (keywordcmp("ice-mismatch", att_name) == 0) {
stream->ice_mismatch = TRUE;
}
}
desc->n_total_streams++;
}
return 0;
}

View file

@ -18,36 +18,35 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sal.h"
#include "sal/sal.h"
#include "linphonecore.h"
#include "private.h"
#include "mediastreamer2/mediastream.h"
#include "lpconfig.h"
// stat
#ifndef WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
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);
if (call->localdesc_changed) ms_message("Local description has changed: %i", call->localdesc_changed);
return call->localdesc_changed | sal_media_description_equals(oldmd, newmd);
}
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++) {
if (new_md->streams[i].type == SalAudio) {
new_audiodesc = &new_md->streams[i];
@ -68,31 +67,15 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c
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);
}
#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;
}
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;
}
}
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
SalMediaDescription *oldmd=call->resultdesc;
if (lc->ringstream!=NULL){
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
linphone_core_stop_ringing(lc);
if (new_md!=NULL){
sal_media_description_ref(new_md);
call->media_pending=FALSE;
@ -108,24 +91,20 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
ms_message("Media descriptions are different, need to 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 );
#endif
}
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);
@ -134,17 +113,13 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
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);
linphone_call_init_media_streams (call);
}
if (oldmd)
sal_media_description_unref(oldmd);
if (new_md) {
bool_t all_muted=FALSE;
@ -166,6 +141,10 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){
linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
}
end:
if (oldmd)
sal_media_description_unref(oldmd);
}
#if 0
static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
@ -182,9 +161,9 @@ static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, c
#endif
static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) {
MSList *elem;
ms_warning(" searching for already_a_call_with_remote_address.");
MSList *elem;
for(elem=lc->calls;elem!=NULL;elem=elem->next){
const LinphoneCall *call=(LinphoneCall*)elem->data;
const LinphoneAddress *cRemote=linphone_call_get_remote_address(call);
@ -215,25 +194,37 @@ static void call_received(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call;
const char *from,*to;
char *alt_contact;
LinphoneAddress *from_addr, *to_addr;
bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE);
/* 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);
@ -282,17 +273,16 @@ static void call_ringing(SalOp *h){
if (call==NULL) return;
/*set privacy*/
call->current_params.privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
if (lc->vtable.display_status)
lc->vtable.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 !*/
linphone_core_stop_dtmf_stream(lc);
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);
@ -316,10 +306,7 @@ static void call_ringing(SalOp *h){
if (lc->vtable.display_status)
lc->vtable.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);
}
@ -339,6 +326,8 @@ static void call_accepted(SalOp *op){
ms_warning("No call to accept.");
return ;
}
/*set privacy*/
call->current_params.privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
/* Handle remote ICE attributes if any. */
if (call->ice_session != NULL) {
@ -351,7 +340,7 @@ static void call_accepted(SalOp *op){
#endif //BUILD_UPNP
md=sal_call_get_final_media_description(op);
if (md)
if (md) /*make sure re-invite will not propose video again*/
call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
if (call->state==LinphoneCallOutgoingProgress ||
@ -402,7 +391,10 @@ static void call_accepted(SalOp *op){
}
}
}
linphone_core_update_streams (lc,call,md);
linphone_core_update_streams(lc,call,md);
/*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);
if (!call->current_params.in_conference)
lc->current_call=call;
linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
@ -451,8 +443,9 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){
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))
if (md && !sal_media_description_empty(md)){
linphone_core_update_streams(lc,call,md);
}
}
static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
@ -460,7 +453,6 @@ static void call_resumed(LinphoneCore *lc, LinphoneCall *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);
}
static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
@ -472,6 +464,16 @@ static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
}
static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){
/*first check if media capabilities are compatible*/
SalMediaDescription* md;
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 && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){
sal_call_decline(call->op,SalReasonNotAcceptable,NULL);
return;
}
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("Call is updated by remote."));
call->defer_update=FALSE;
@ -533,10 +535,12 @@ static void call_terminated(SalOp *op, const char *from){
break;
}
ms_message("Current call terminated...");
if (call->refer_pending){
linphone_core_start_refered_call(lc,call);
}
//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)
@ -560,9 +564,10 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
char *msg603=_("Call declined.");
const char *msg=details;
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 ;
}
@ -612,55 +617,83 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
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");
ms_message("Outgoing call [%p] failed with SRTP (SAVP) enabled",call);
linphone_call_stop_media_streams(call);
if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
if ( call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging /*push case*/
|| call->state==LinphoneCallOutgoingEarlyMedia){
ms_message("Retrying call [%p] with AVP",call);
/* clear SRTP local params */
call->params.media_encryption = LinphoneMediaEncryptionNone;
for(i=0; i<call->localdesc->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_restart_invite(lc, call);
return;
}
return;
}
msg=_("Incompatible media parameters.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonRequestPending:
/*restore previous state, the application will decide to resubmit the action if relevant*/
call->reason=linphone_reason_from_sal(sr);
linphone_call_set_state(call,call->prevstate,msg);
return;
break;
default:
if (lc->vtable.display_status)
lc->vtable.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 error 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));
call->reason=linphone_reason_from_sal(sr);
linphone_call_set_state(call, call->prevstate,details);
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;
call->reason=linphone_reason_from_sal(sr);
if (sr==SalReasonDeclined){
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.");
linphone_core_play_named_tone(lc,LinphoneToneBusy);
} else {
linphone_call_set_state(call,LinphoneCallError,msg);
}else{
linphone_call_set_state(call,LinphoneCallError,details);
if (sr==SalReasonBusy)
linphone_core_play_named_tone(lc,LinphoneToneBusy);
}
if (referer){
/*
* 1- resume call automatically if we had to pause it before to execute the transfer
* 2- notify other party of the transfer faillure
* This must be done at the end because transferer call can't be resumed until transfer-target call is changed to error state.
* This must be done in this order because if the notify transaction will prevent the resume transaction to take place.
* On the contrary, the notify transaction is queued and then executed after the resume completes.
**/
if (linphone_call_get_state(referer)==LinphoneCallPaused && referer->was_automatically_paused){
/*resume to the call that send us the refer automatically*/
linphone_core_resume_call(lc,referer);
referer->was_automatically_paused=FALSE;
}
linphone_core_notify_refer_state(lc,referer,call);
}
}
@ -671,54 +704,14 @@ static void call_released(SalOp *op){
}else ms_error("call_released() for already destroyed call ?");
}
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));
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 (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 && (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);
static void auth_failure(SalOp *op, SalAuthInfo* info) {
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
if (ai){
ms_message("%s/%s authentication works.",realm,username);
ai->works=TRUE;
ms_message("%s/%s/%s authentication fails.",info->realm,info->username,info->domain);
}
if (lc->vtable.auth_info_requested) {
lc->vtable.auth_info_requested(lc,info->realm,info->username,info->domain);
}
}
@ -727,7 +720,7 @@ static void register_success(SalOp *op, bool_t registered){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
char *msg;
if (cfg->deletion_date!=0){
if (!cfg || cfg->deletion_date!=0){
ms_message("Registration success for removed proxy config, ignored");
return;
}
@ -763,18 +756,21 @@ static void register_failure(SalOp *op, SalError error, SalReason reason, const
lc->vtable.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);
linphone_proxy_config_set_error(cfg,linphone_reason_from_sal(reason));
if (error== SalErrorFailure
&& reason == SalReasonServiceUnavailable
&& 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;
}
}
@ -854,25 +850,25 @@ static void text_received(SalOp *op, const SalMessage *msg){
}
}
static void notify(SalOp *op, const char *from, 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);
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 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 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 subscribe_received(SalOp *op, const char *from){
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));
linphone_notify_recv(lc,op,ss,model);
}
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);
}
@ -892,6 +888,80 @@ static void ping_reply(SalOp *op){
}
}
static const char *get_client_cert_path(LinphoneCore *lc) {
static char cldir[200] = {0};
#ifdef HAVE_GETENV
if (!cldir[0]) {
static char default_path[200] = {0};
snprintf(default_path, sizeof(default_path), "%s%s", getenv("HOME"), "/linphone_certs");
snprintf(cldir, sizeof(cldir), "%s", lp_config_get_string(lc->config,"sip","client_certificates_dir", default_path));
}
#endif
return cldir;
}
static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) {
char chain_file[200];
char key_file[200];
const char *path = get_client_cert_path(lc);
snprintf(chain_file, sizeof(chain_file), "%s%s", path, "/chain.pem");
snprintf(key_file, sizeof(key_file), "%s%s", path, "/key.pem");
#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;
ai->usecount++;
ai->last_use_time=ms_time(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 {
if (lc->vtable.auth_info_requested) {
lc->vtable.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);
@ -935,6 +1005,7 @@ static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus sta
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);
@ -942,15 +1013,101 @@ static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
chat_msg->state=chatStatusSal2Linphone(status);
linphone_chat_message_store_state(chat_msg);
if (chat_msg && chat_msg->cb) {
ms_message("Notifying text delivery with status %i",chat_msg->state);
chat_msg->cb(chat_msg
,chat_msg->state
,chat_msg->cb_ud);
}
linphone_chat_message_destroy(chat_msg);
if (status != SalTextDeliveryInProgress) { /*don't release op if progress*/
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);
}
}
}
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, SalError error, SalReason reason){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
if (!ms_list_find_custom((MSList*)calls, (MSCompareFunc) op_equals, op)) {
/*op was only create for messaging purpose, destroying*/
sal_op_release(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{
linphone_event_set_reason(lev, linphone_reason_from_sal(reason));
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));
LinphoneContent content;
if (lev==NULL) {
/*out of subscribe notify */
lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionOutgoing,eventname);
}
if (lc->vtable.notify_received){
const LinphoneContent *ct=linphone_content_from_sal_body(&content,body);
if (ct) lc->vtable.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, SalError err, SalReason reason){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
if (lev==NULL) return;
if (err==SalErrorNone){
if (!lev->terminating)
linphone_event_set_publish_state(lev,LinphonePublishOk);
else
linphone_event_set_publish_state(lev,LinphonePublishCleared);
}else{
linphone_event_set_reason(lev,linphone_reason_from_sal(reason));
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);
}
}
@ -963,8 +1120,7 @@ SalCallbacks linphone_sal_callbacks={
call_terminated,
call_failure,
call_released,
auth_requested,
auth_success,
auth_failure,
register_success,
register_failure,
vfu_request,
@ -972,12 +1128,21 @@ SalCallbacks linphone_sal_callbacks={
refer_received,
text_received,
text_delivery_update,
notify,
notify_presence,
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
};

View file

@ -31,6 +31,15 @@
* @{
*/
/**
* Returns an array of chat rooms
* @param lc #LinphoneCore object
* @return An array of #LinpĥoneChatRoom
**/
MSList* linphone_core_get_chat_rooms(LinphoneCore *lc) {
return lc->chatrooms;
}
/**
* Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org
* @param lc #LinphoneCore object
@ -50,6 +59,32 @@ LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char *
}
return NULL;
}
bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from){
return linphone_address_weak_equal(cr->peer_url,from);
}
/**
* Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org if not already existing, else return exisiting one
* @param lc #LinphoneCore object
* @param to destination address for messages
* @return #LinphoneChatRoom where messaging can take place.
*/
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(lc,to);
}
return ret;
}
/**
* Destroy a LinphoneChatRoom.
@ -60,16 +95,16 @@ void linphone_chat_room_destroy(LinphoneChatRoom *cr){
lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr);
linphone_address_destroy(cr->peer_url);
ms_free(cr->peer);
ms_free(cr);
}
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);
if (lp_config_get_int(cr->lc->config,"sip","chat_use_call_dialogs",0)){
@ -82,19 +117,20 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
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);
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);
@ -105,7 +141,7 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
}
msg->dir=LinphoneChatMessageOutgoing;
msg->from=linphone_address_new(identity);
linphone_chat_message_store(msg);
msg->storage_id=linphone_chat_message_store(msg);
}
/**
@ -118,12 +154,6 @@ 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
@ -181,14 +211,15 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
msg->time=sal_msg->time;
msg->state=LinphoneChatMessageStateDelivered;
msg->is_read=FALSE;
ch=sal_op_get_custom_header(op);
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);
linphone_chat_message_store(msg);
msg->storage_id=linphone_chat_message_store(msg);
linphone_chat_room_message_received(cr,lc,msg);
ms_free(cleanfrom);
ms_free(from);
@ -238,6 +269,41 @@ LinphoneChatMessage* linphone_chat_room_create_message(LinphoneChatRoom *cr, con
return msg;
}
/**
* Create a message attached to a dedicated chat room;
* @param cr the chat room.
* @param message text message, NULL if absent.
* @param external_body_url the URL given in external body or NULL.
* @param state the LinphoneChatMessage.State of the message.
* @param time the time_t at which the message has been received/sent.
* @param is_read TRUE if the message should be flagged as read, FALSE otherwise.
* @param is_incoming TRUE if the message has been received, FALSE otherwise.
* @return a new #LinphoneChatMessage
*/
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 = ms_new0(LinphoneChatMessage,1);
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;
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;
}
/**
* Send a message to peer member of this chat room.
* @param cr #LinphoneChatRoom object
@ -246,7 +312,7 @@ LinphoneChatMessage* linphone_chat_room_create_message(LinphoneChatRoom *cr, con
* @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) {
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;
@ -261,7 +327,7 @@ const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState
case LinphoneChatMessageStateIdle:return "LinphoneChatMessageStateIdle";
case LinphoneChatMessageStateInProgress:return "LinphoneChatMessageStateInProgress";
case LinphoneChatMessageStateDelivered:return "LinphoneChatMessageStateDelivered";
case LinphoneChatMessageStateNotDelivered:return "LinphoneChatMessageStateNotDelivered";
case LinphoneChatMessageStateNotDelivered:return "LinphoneChatMessageStateNotDelivered";
default: return "Unknown state";
}
@ -336,6 +402,16 @@ const LinphoneAddress* linphone_chat_message_get_from(const LinphoneChatMessage*
return message->from;
}
/**
* Set destination of the message
*@param message #LinphoneChatMessage obj
*@param to #LinphoneAddress destination of this message (copied)
*/
void linphone_chat_message_set_to(LinphoneChatMessage* message, const LinphoneAddress* to) {
if(message->to) linphone_address_destroy(message->to);
message->to=linphone_address_clone(to);
}
/**
* Get destination of the message
*@param message #LinphoneChatMessage obj
@ -401,6 +477,31 @@ const char * linphone_chat_message_get_custom_header(LinphoneChatMessage* messag
return sal_custom_header_find(message->custom_headers,header_name);
}
/**
* Returns TRUE if the message has been read, otherwise returns FALSE.
* @param message the message
**/
bool_t linphone_chat_message_is_read(LinphoneChatMessage* message) {
return message->is_read;
}
/**
* Returns TRUE if the message has been sent, returns FALSE if the message has been received.
* @param message the message
**/
bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message) {
return message->dir == LinphoneChatMessageOutgoing;
}
/**
* Returns the id used to identify this message in the storage database
* @param message the message
* @return the id
*/
unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message) {
return message->storage_id;
}
/**
* Duplicate a LinphoneChatMessage
**/
@ -425,6 +526,7 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg)
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);
return new_message;
}

View file

@ -218,6 +218,7 @@ int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t active){
int err=0;
char *str;
if (!call->current_params.in_conference){
if (call->params.in_conference){
@ -230,7 +231,7 @@ static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t a
}
call->params.in_conference=FALSE;
char *str=linphone_call_get_remote_address_as_string(call);
str=linphone_call_get_remote_address_as_string(call);
ms_message("%s will be removed from conference", str);
ms_free(str);
if (active){
@ -289,10 +290,11 @@ static int convert_conference_to_call(LinphoneCore *lc){
* @returns 0 if successful, -1 otherwise.
**/
int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
int err;
char * str=linphone_call_get_remote_address_as_string(call);
ms_message("Removing call %s from the conference", str);
ms_free(str);
int err=remove_from_conference(lc,call, FALSE);
err=remove_from_conference(lc,call, FALSE);
if (err){
ms_error("Error removing participant from conference.");
return err;
@ -341,13 +343,14 @@ int linphone_core_leave_conference(LinphoneCore *lc){
* @returns 0 if successful, -1 otherwise
**/
int linphone_core_enter_conference(LinphoneCore *lc){
LinphoneConference *conf;
if (linphone_core_sound_resources_locked(lc)) {
return -1;
}
if (lc->current_call != NULL) {
_linphone_core_pause_call(lc, lc->current_call);
}
LinphoneConference *conf=&lc->conf_ctx;
conf=&lc->conf_ctx;
if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
return 0;
}

View file

@ -265,11 +265,13 @@ void ec_calibrator_destroy(EcCalibrator *ecc){
int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb,
LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){
unsigned int rate;
if (lc->ecc!=NULL){
ms_error("Echo calibration is still on going !");
return -1;
}
unsigned int rate = lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
rate = lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
lc->ecc=ec_calibrator_new(lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,rate,cb,audio_init_cb,audio_uninit_cb,cb_data);
return 0;
}

282
coreapi/event.c Normal file
View file

@ -0,0 +1,282 @@
/*
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 LinphoneSubscriptionOutoingInit: return "LinphoneSubscriptionOutoingInit";
case LinphoneSubscriptionPending: return "LinphoneSubscriptionPending";
case LinphoneSubscriptionActive: return "LinphoneSubscriptionActive";
case LinphoneSubscriptionTerminated: return "LinphoneSubscriptionTerminated";
case LinphoneSubscriptionError: return "LinphoneSubscriptionError";
}
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){
LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, sal_op_new(lc->sal));
return lev;
}
LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name){
LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, op);
if (dir==LinphoneSubscriptionIncoming){
lev->resource_addr=linphone_address_clone((LinphoneAddress*)sal_op_get_to_address(op));
lev->from=linphone_address_clone((LinphoneAddress*)sal_op_get_from_address(lev->op));
}else{
lev->resource_addr=linphone_address_clone((LinphoneAddress*)sal_op_get_from_address(op));
}
return lev;
}
void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state){
LinphoneCore *lc=lev->lc;
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;
if (lc->vtable.subscription_state_changed){
lc->vtable.subscription_state_changed(lev->lc,lev,state);
}
if (state==LinphoneSubscriptionTerminated){
linphone_event_unref(lev);
}
}
}
void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state){
LinphoneCore *lc=lev->lc;
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;
if (lc->vtable.publish_state_changed){
lc->vtable.publish_state_changed(lev->lc,lev,state);
}
}
}
LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){
return lev->publish_state;
}
void linphone_event_set_reason(LinphoneEvent *lev, LinphoneReason reason){
lev->reason=reason;
}
LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){
return lev->reason;
}
LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){
LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event);
SalBody salbody;
linphone_configure_op(lc,lev->op,resource,NULL,TRUE);
lev->resource_addr=linphone_address_clone(resource);
lev->from=linphone_address_clone((LinphoneAddress*)sal_op_get_from_address(lev->op));
sal_subscribe(lev->op,NULL,NULL,event,expires,sal_body_from_content(&salbody,body));
linphone_event_set_state(lev,LinphoneSubscriptionOutoingInit);
return lev;
}
int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){
SalBody salbody;
if (lev->subscription_state!=LinphoneSubscriptionActive){
ms_error("linphone_event_update_subscribe(): cannot update subscription if subscription wasn't accepted.");
return -1;
}
if (lev->dir!=LinphoneSubscriptionOutgoing){
ms_error("linphone_event_deny_subscription(): cannot update an incoming subscription.");
return -1;
}
return sal_subscribe(lev->op,NULL,NULL,NULL,-1,sal_body_from_content(&salbody,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_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){
SalBody salbody;
int err;
LinphoneEvent *lev=linphone_event_new(lc,LinphoneSubscriptionInvalidDir, event);
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));
err=sal_publish(lev->op,NULL,NULL,event,expires,sal_body_from_content(&salbody,body));
if (err==0){
linphone_event_set_publish_state(lev,LinphonePublishProgress);
}else{
linphone_event_unref(lev);
lev=NULL;
}
return lev;
}
int linphone_event_update_publish(LinphoneEvent *lev, const LinphoneContent *body){
SalBody salbody;
int err;
err=sal_publish(lev->op,NULL,NULL,NULL,-1,sal_body_from_content(&salbody,body));
if (err==0){
linphone_event_set_publish_state(lev,LinphonePublishProgress);
}else{
linphone_event_set_publish_state(lev,LinphonePublishError);
}
return err;
}
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_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){
sal_publish(lev->op,NULL,NULL,NULL,0,NULL);
}
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);
if (lev->resource_addr) linphone_address_destroy(lev->resource_addr);
if (lev->from) linphone_address_destroy(lev->from);
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){
return lev->from;
}
const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){
return lev->resource_addr;
}

237
coreapi/event.h Normal file
View file

@ -0,0 +1,237 @@
/*
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 subscriptions
* @{
**/
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,
LinphoneSubscriptionOutgoing,
LinphoneSubscriptionInvalidDir
};
/**
* Typedef alias for _LinphoneSubscriptionDir
**/
typedef enum _LinphoneSubscriptionDir LinphoneSubscriptionDir;
/**
* Enum for subscription states.
**/
enum _LinphoneSubscriptionState{
LinphoneSubscriptionNone, /**< Initial state, should not be used.**/
LinphoneSubscriptionOutoingInit, /**<An outgoing subcription was created*/
LinphoneSubscriptionIncomingReceived, /**<An incoming subcription is received*/
LinphoneSubscriptionPending, /**<Subscription is pending, waiting for user approval*/
LinphoneSubscriptionActive, /**<Subscription is accepted.*/
LinphoneSubscriptionTerminated, /**<Subscription is terminated normally*/
LinphoneSubscriptionError /**<Subscription encountered an error, indicated by linphone_event_get_reason()*/
};
/**
* 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, /**<An outgoing subcription was created*/
LinphonePublishOk, /**<Publish is accepted.*/
LinphonePublishError, /**<Publish encoutered an error, linphone_event_get_reason() gives reason code*/
LinphonePublishExpiring, /**<Publish is about to expire, only sent if [sip]->refresh_generic_publish property is set to 0.*/
LinphonePublishCleared /**<Event has been un published*/
};
/**
* Typedef for publish state enum
**/
typedef enum _LinphonePublishState LinphonePublishState;
LINPHONE_PUBLIC const char *linphone_publish_state_to_string(LinphonePublishState state);
/**
* Callback prototype for notifying the application about notification received from the network.
**/
typedef void (*LinphoneCoreNotifyReceivedCb)(LinphoneCore *lc, LinphoneEvent *lev, const char *notified_event, const LinphoneContent *body);
/**
* Callback prototype for notifying the application about changes of subscription states, including arrival of new subscriptions.
**/
typedef void (*LinphoneCoreSubscriptionStateChangedCb)(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state);
/**
* Callback prototype for notifying the application about changes of publish states.
**/
typedef void (*LinphoneCorePublishStateChangedCb)(LinphoneCore *lc, LinphoneEvent *lev, LinphonePublishState state);
/**
* Create an outgoing subscription, specifying the destination resource, the event name, and an optional content body.
* If accepted, the subscription runs for a finite period, but is automatically renewed if not terminated before.
* @param lc the #LinphoneCore
* @param resource the destination resource
* @param event the event name
* @param expires the whished duration of the subscription
* @param body an optional body, may be NULL.
* @return a LinphoneEvent holding the context of the created subcription.
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body);
/**
* Update an outgoing subscription.
* @param lev a LinphoneEvent
* @param body an optional body to include in the subscription update, may be NULL.
**/
LINPHONE_PUBLIC int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body);
/**
* Accept an incoming subcription.
**/
LINPHONE_PUBLIC int linphone_event_accept_subscription(LinphoneEvent *lev);
/**
* Deny an incoming subscription with given reason.
**/
LINPHONE_PUBLIC int linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason);
/**
* Send a notification.
* @param lev a #LinphoneEvent corresponding to an incoming subscription previously received and accepted.
* @param body an optional body containing the actual notification data.
* @return 0 if successful, -1 otherwise.
**/
LINPHONE_PUBLIC int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body);
/**
* Publish an event state.
* After expiry, the publication is refreshed unless it is terminated before.
* @param lc the #LinphoneCore
* @param resource the resource uri for the event
* @param event the event name
* @param expires the lifetime of the publication
* @param body the actual published data
* @return the LinphoneEvent holding the context of the publish.
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body);
/**
* Update a publication.
* @param lev the #LinphoneEvent
* @param body the new data to be published
**/
LINPHONE_PUBLIC int linphone_event_update_publish(LinphoneEvent *lev, const LinphoneContent *body);
/**
* Return reason code (in case of error state reached).
**/
LINPHONE_PUBLIC LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev);
/**
* Get subscription state. If the event object was not created by a subscription mechanism, #LinphoneSubscriptionNone is returned.
**/
LINPHONE_PUBLIC LinphoneSubscriptionState linphone_event_get_subscription_state(const LinphoneEvent *lev);
/**
* Get publish state. If the event object was not created by a publish mechanism, #LinphonePublishNone is returned.
**/
LINPHONE_PUBLIC LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev);
/**
* Get subscription direction.
* If the object wasn't created by a subscription mechanism, #LinphoneSubscriptionInvalidDir is returned.
**/
LINPHONE_PUBLIC LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev);
/**
* Set a user (application) pointer.
**/
LINPHONE_PUBLIC void linphone_event_set_user_data(LinphoneEvent *ev, void *up);
/**
* Retrieve user pointer.
**/
LINPHONE_PUBLIC void *linphone_event_get_user_data(const LinphoneEvent *ev);
/**
* Terminate an incoming or outgoing subscription that was previously acccepted, or a previous publication.
* This function does not unref the object. The core will unref() if it does not need this object anymore.
*
* For subscribed event, when the subscription is terminated normally or because of an error, the core will unref.
* For published events, no unref is performed. This is because it is allowed to re-publish an expired publish, as well as retry it in case of error.
**/
LINPHONE_PUBLIC void linphone_event_terminate(LinphoneEvent *lev);
/**
* Increase reference count of LinphoneEvent.
* By default LinphoneEvents created by the core are owned by the core only.
* An application that wishes to retain a reference to it must call linphone_event_ref().
* When this reference is no longer needed, linphone_event_unref() must be called.
*
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_event_ref(LinphoneEvent *lev);
/**
* Decrease reference count.
* @see linphone_event_ref()
**/
LINPHONE_PUBLIC void linphone_event_unref(LinphoneEvent *lev);
/**
* Get the name of the event as specified in the event package RFC.
**/
LINPHONE_PUBLIC const char *linphone_event_get_name(const LinphoneEvent *lev);
/**
* Get the "from" address of the subscription.
**/
LINPHONE_PUBLIC const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev);
/**
* Get the resource address of the subscription or publish.
**/
LINPHONE_PUBLIC const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev);
/**
* @}
**/
#endif

View file

@ -62,6 +62,8 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
case LinphoneStatusPending:
str=_("Pending");
break;
case LinphoneStatusVacation:
str=_("Vacation");
default:
str=_("Unknown-bug");
}
@ -71,16 +73,16 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
static int friend_compare(const void * a, const void * b){
LinphoneAddress *fa=((LinphoneFriend*)a)->uri;
LinphoneAddress *fb=((LinphoneFriend*)b)->uri;
if (linphone_address_weak_equal (fa,fb)) return 0;
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;
@ -97,35 +99,20 @@ LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op){
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;
LinphoneCore *lc=fr->lc;
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);
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,36 +121,42 @@ 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);
obj->pol=LinphoneSPAccept;
obj->status=LinphoneStatusOffline;
obj->presence=NULL;
obj->subscribe=TRUE;
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;
if (linphone_address == NULL) {
ms_error("Cannot create friend for address [%s]",addr?addr:"null");
return NULL;
}
LinphoneFriend *fr=linphone_friend_new();
if (linphone_friend_set_addr(fr,linphone_address)<0){
linphone_friend_destroy(fr);
return NULL;
}
fr=linphone_friend_new();
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->up=data;
}
void* linphone_friend_get_user_data(const LinphoneFriend *lf){
return lf->up;
}
bool_t linphone_friend_in_list(const LinphoneFriend *lf){
return lf->lc!=NULL;
}
@ -185,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);
@ -198,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;
}
@ -231,54 +226,12 @@ 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;
}
return SalPresenceOffline;
}
void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os){
void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){
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);
sal_notify_presence(lf->insub,(SalPresenceModel *)presence);
}
}
@ -289,10 +242,19 @@ static void linphone_friend_unsubscribe(LinphoneFriend *lf){
}
}
static void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
if (lf->outsub!=NULL) {
sal_op_release(lf->outsub);
lf->outsub=NULL;
lf->subscribe_active=FALSE;
}
lf->initial_subscribes_sent=FALSE;
}
void linphone_friend_close_subscriptions(LinphoneFriend *lf){
linphone_friend_unsubscribe(lf);
if (lf->insub){
sal_notify_close(lf->insub);
sal_notify_presence_close(lf->insub);
}
}
@ -306,6 +268,7 @@ void linphone_friend_destroy(LinphoneFriend *lf){
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);
@ -315,6 +278,12 @@ 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;
}
@ -324,7 +293,87 @@ 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){
@ -332,27 +381,28 @@ BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){
}
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;
@ -360,6 +410,8 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){
if (fr->subscribe && fr->subscribe_active==FALSE){
ms_message("Sending a new SUBSCRIBE");
__linphone_friend_do_subscribe(fr);
}else if (fr->subscribe_active && !fr->subscribe){
linphone_friend_unsubscribe(fr);
}
ms_message("linphone_friend_apply() done.");
lc->bl_refresh=TRUE;
@ -375,6 +427,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);
@ -388,27 +448,57 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf)
return ;
}
lc->friends=ms_list_append(lc->friends,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){
const MSList *elem;
if (lc->initial_subscribes_sent) return;
lc->initial_subscribes_sent=TRUE; /*set to true and see if looping on friends will change this status*/
for(elem=lc->friends;elem!=NULL;elem=elem->next){
LinphoneFriend *f=(LinphoneFriend*)elem->data;
if (f->commit)
LinphoneProxyConfig* cfg;
if (f->subscribe && !f->initial_subscribes_sent) {
lc->initial_subscribes_sent=FALSE; /*at least 1 was not sent */
if ((cfg=linphone_core_lookup_known_proxy(f->lc,linphone_friend_get_address(f)))) {
/*check if already registered*/
if (linphone_proxy_config_get_state(cfg) != LinphoneRegistrationOk)
continue; /*skip this friend because not registered yet*/
else {
char* lf_string = linphone_address_as_string(linphone_friend_get_address(f));
ms_message("Identity [%s] registered, we can now subscribe to [%s]",linphone_proxy_config_get_identity(cfg),lf_string);
ms_free(lf_string);
}
}
linphone_friend_apply(f,lc);
f->initial_subscribes_sent=TRUE;
}
}
}
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){
@ -426,38 +516,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;
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;
const char *it_username=linphone_address_get_username(lf->uri);
const char *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;
}
@ -511,7 +585,7 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde
if (tmp==NULL) {
return NULL;
}
lf=linphone_friend_new_with_addr(tmp);
lf=linphone_friend_new_with_address(tmp);
if (lf==NULL) {
return NULL;
}

View file

@ -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
#---------------------------------------------------------------------------

View file

@ -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,21 +21,32 @@ $(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
helloworld_SOURCES=helloworld.c
LINPHONE_TUTOS=$(helloworld_SOURCES)
@ -57,17 +69,18 @@ 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)
AM_CFLAGS=\
-I$(top_srcdir)/coreapi \
$(STRICT_OPTIONS) \
-DIN_LINPHONE \
$(ORTP_CFLAGS) \
$(OSIP_CFLAGS) \
$(MEDIASTREAMER_CFLAGS) \
$(EXOSIP_CFLAGS) \
-DENABLE_TRACE \
-DLOG_DOMAIN=\"LinphoneCore\" \
$(IPV6_CFLAGS) \
@ -78,3 +91,4 @@ AM_CFLAGS=\
tutodir=$(datadir)/tutorials/linphone
tuto_DATA=$(LINPHONE_TUTOS)
endif

View file

@ -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));
@ -103,11 +106,11 @@ int main(int argc, char *argv[]){
#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*/
/*
@ -126,7 +129,7 @@ int main(int argc, char *argv[]){
}
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*/
}
@ -152,7 +155,7 @@ int main(int argc, char *argv[]){
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 +167,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 +176,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 */

View file

@ -152,7 +152,7 @@ linphone_friend_done(my_friend); /*commit changes triggering an UNSUBSCRIBE mess
<b> Publishing presence status </b>
<br>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.
<br>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.
<b>Handling incoming subscription request</b>
<br> 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

View file

@ -23,18 +23,23 @@ import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.OnlineStatus;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneFriend.SubscribePolicy;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;
/**
*
@ -156,7 +161,7 @@ public class TutorialBuddyStatus implements LinphoneCoreListener {
if (mySipPassword != null) {
// create authentication structure from identity and add to linphone
lc.addAuthInfo(lcFactory.createAuthInfo(username, mySipPassword, null));
lc.addAuthInfo(lcFactory.createAuthInfo(username, mySipPassword, null, domain));
}
// create proxy config
@ -241,5 +246,39 @@ public class TutorialBuddyStatus implements LinphoneCoreListener {
}
@Override
public void transferState(LinphoneCore lc, LinphoneCall call,
State new_call_state) {
// TODO Auto-generated method stub
}
@Override
public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
// TODO Auto-generated method stub
}
@Override
public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
SubscriptionState state) {
// TODO Auto-generated method stub
}
@Override
public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
String eventName, LinphoneContent content) {
// TODO Auto-generated method stub
}
@Override
public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
PublishState state) {
// TODO Auto-generated method stub
}
}

View file

@ -24,6 +24,7 @@ import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCore.GlobalState;
@ -31,8 +32,12 @@ import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;
/**
@ -115,7 +120,7 @@ public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessa
try {
// Next step is to create a chat room
LinphoneChatRoom chatRoom = lc.createChatRoom(destinationSipAddress);
LinphoneChatRoom chatRoom = lc.getOrCreateChatRoom(destinationSipAddress);
// Send message
LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage("Hello world");
@ -163,5 +168,39 @@ public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessa
write("Sent message [" + msg.getText() + "] new state is " + state.toString());
}
@Override
public void transferState(LinphoneCore lc, LinphoneCall call,
State new_call_state) {
// TODO Auto-generated method stub
}
@Override
public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
// TODO Auto-generated method stub
}
@Override
public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
SubscriptionState state) {
// TODO Auto-generated method stub
}
@Override
public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
String eventName, LinphoneContent content) {
// TODO Auto-generated method stub
}
@Override
public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
PublishState state) {
// TODO Auto-generated method stub
}
}

View file

@ -23,16 +23,21 @@ import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;
/**
@ -167,5 +172,39 @@ public class TutorialHelloWorld implements LinphoneCoreListener {
}
@Override
public void transferState(LinphoneCore lc, LinphoneCall call,
State new_call_state) {
// TODO Auto-generated method stub
}
@Override
public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
// TODO Auto-generated method stub
}
@Override
public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
SubscriptionState state) {
// TODO Auto-generated method stub
}
@Override
public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
String eventName, LinphoneContent content) {
// TODO Auto-generated method stub
}
@Override
public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
PublishState state) {
// TODO Auto-generated method stub
}
}

View file

@ -23,16 +23,21 @@ import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;
/**
@ -126,7 +131,7 @@ public class TutorialRegistration implements LinphoneCoreListener {
if (password != null) {
// create authentication structure from identity and add to linphone
lc.addAuthInfo(lcFactory.createAuthInfo(username, password, null));
lc.addAuthInfo(lcFactory.createAuthInfo(username, password, null, domain));
}
// create proxy config
@ -198,6 +203,40 @@ public class TutorialRegistration implements LinphoneCoreListener {
}
@Override
public void transferState(LinphoneCore lc, LinphoneCall call,
State new_call_state) {
// TODO Auto-generated method stub
}
@Override
public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
// TODO Auto-generated method stub
}
@Override
public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
SubscriptionState state) {
// TODO Auto-generated method stub
}
@Override
public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
String eventName, LinphoneContent content) {
// TODO Auto-generated method stub
}
@Override
public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
PublishState state) {
// TODO Auto-generated method stub
}
}

183
coreapi/help/notify.c Normal file
View file

@ -0,0 +1,183 @@
/*
linphone
Copyright (C) 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.
*/
/**
* @defgroup notify_tutorials Generic subscribe/notify example
* @ingroup tutorials
*This program is a _very_ simple usage example of liblinphone.
*It demonstrates how to listen to a SIP subscription.
*It then sends notify requests back periodically.
*first argument must be like sip:jehan@sip.linphone.org , second must be password .
*<br>
*ex registration sip:jehan@sip.linphone.org secret
*<br>Registration is cleared on SIGINT
*<br>
*@include registration.c
*
*/
#define DEBUG 1
#ifdef IN_LINPHONE
#include "linphonecore.h"
#else
#include "linphone/linphonecore.h"
#endif
#include <signal.h>
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;
/* 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);
LinphoneProxyConfig* proxy_cfg;
/*create proxy config*/
proxy_cfg = linphone_proxy_config_new();
/*parse identity*/
LinphoneAddress *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,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*/
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){
LinphoneContent content;
content.type="application";
content.subtype="goodxml";
content.data="really cool";
content.size=strlen((const char*)content.data);
linphone_event_notify(data->ev,&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;
}

View file

@ -102,7 +102,7 @@ int main(int argc, char *argv[]){
}
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*/
}

197
coreapi/info.c Normal file
View file

@ -0,0 +1,197 @@
/***************************************************************************
* 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;
};
#define SET_STRING(ptr,field,val) \
if (ptr->field) { \
ms_free(ptr->field); \
ptr->field=NULL; \
} \
if (val){ \
ptr->field=ms_strdup(val); \
}
static void linphone_content_copy(LinphoneContent *obj, const LinphoneContent *ref){
SET_STRING(obj,type,ref->type);
SET_STRING(obj,subtype,ref->subtype);
SET_STRING(obj,encoding,ref->encoding);
if (obj->data) {
ms_free(obj->data);
obj->data=NULL;
}
if (ref->data){
obj->data=ms_malloc(ref->size+1);
memcpy(obj->data, ref->data, ref->size);
((char*)obj->data)[ref->size]='\0';
}
obj->size=ref->size;
}
void linphone_content_uninit(LinphoneContent * obj){
if (obj->type) ms_free(obj->type);
if (obj->subtype) ms_free(obj->subtype);
if (obj->data) ms_free(obj->data);
if (obj->encoding) ms_free(obj->encoding);
}
LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj, const SalBody *ref){
SET_STRING(obj,type,ref->type);
SET_STRING(obj,subtype,ref->subtype);
SET_STRING(obj,encoding,ref->encoding);
if (obj->data) {
ms_free(obj->data);
obj->data=NULL;
}
if (ref->data){
obj->data=ms_malloc(ref->size+1);
memcpy(obj->data, ref->data, ref->size);
((char*)obj->data)[ref->size]='\0';
}
obj->size=ref->size;
return obj;
}
const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref){
if (ref && ref->type){
obj->type=(char*)ref->type;
obj->subtype=(char*)ref->subtype;
obj->data=(void*)ref->data;
obj->encoding=(char*)ref->encoding;
obj->size=ref->size;
return obj;
}
return NULL;
}
SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *lc){
if (lc && lc->type){
body->type=lc->type;
body->subtype=lc->subtype;
body->data=lc->data;
body->size=lc->size;
body->encoding=lc->encoding;
return body;
}
return NULL;
}
/**
* Destroy a LinphoneInfoMessage
**/
void linphone_info_message_destroy(LinphoneInfoMessage *im){
linphone_content_uninit(&im->content);
sal_custom_header_free(im->headers);
ms_free(im);
}
LinphoneInfoMessage *linphone_info_message_copy(const LinphoneInfoMessage *orig){
LinphoneInfoMessage *im=ms_new0(LinphoneInfoMessage,1);
linphone_content_copy(&im->content,&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){
linphone_content_copy(&im->content,content);
}
/**
* Returns the info message's content as a #LinphoneContent structure.
**/
const LinphoneContent * linphone_info_message_get_content(const LinphoneInfoMessage *im){
return im->content.type ? &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) linphone_content_copy_from_sal_body(&info->content,body);
if (lc->vtable.info_received)
lc->vtable.info_received(lc,call,info);
linphone_info_message_destroy(info);
}
}

View file

@ -36,6 +36,7 @@ LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){
struct _LinphoneTunnel {
belledonnecomm::TunnelManager *manager;
MSList *config_list;
bool_t auto_detect_enabled;
};
extern "C" LinphoneTunnel* linphone_core_tunnel_new(LinphoneCore *lc){
@ -225,6 +226,7 @@ void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){
}
void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){
tunnel->auto_detect_enabled = FALSE;
lp_config_set_int(config(tunnel),"tunnel","enabled",(int)enabled);
bcTunnel(tunnel)->enable(enabled);
}
@ -311,9 +313,14 @@ void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){
}
void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){
tunnel->auto_detect_enabled = TRUE;
bcTunnel(tunnel)->autoDetect();
}
bool_t linphone_tunnel_auto_detect_enabled(LinphoneTunnel *tunnel) {
return tunnel->auto_detect_enabled;
}
static void my_ortp_logv(OrtpLogLevel level, const char *fmt, va_list args){
ortp_logv(level,fmt,args);
}

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