diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..5dea97796 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 2.6) +project(LINPHONE C) + +option(LINPHONE_ENABLE_VIDEO "Build linphone with video support." ON) + +if(NOT ORTP_ROOT_DIR) + set(ORTP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/oRTP) +endif() +if(NOT ORTP_INCLUDE_DIR) + set(ORTP_INCLUDE_DIR ${ORTP_ROOT_DIR}/include) +endif() +if(NOT MS2_ROOT_DIR) + set(MS2_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/mediastreamer2) +endif() +if(NOT MS2_INCLUDE_DIR) + set(MS2_INCLUDE_DIR ${MS2_ROOT_DIR}/include) +endif() +if(NOT LIBXML2_ROOT_DIR) + set(LIBXML2_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../libxml2) +endif() +if(NOT LIBXML2_INCLUDE_DIR) + set(LIBXML2_INCLUDE_DIR ${LIBXML2_ROOT_DIR}/include) +endif() +if(NOT BELLESIP_ROOT_DIR) + set(BELLESIP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../belle-sip) +endif() +if(NOT BELLESIP_INCLUDE_DIR) + set(BELLESIP_INCLUDE_DIR ${BELLESIP_ROOT_DIR}/include) +endif() + +include_directories( + include/ + coreapi/ + ${CMAKE_CURRENT_BINARY_DIR}/coreapi/ + ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/libxml2/ +) + +if(USE_INSTALLED_COMPONENTS) + include_directories( + ${CMAKE_INSTALL_PREFIX}/include + ${CMAKE_INSTALL_PREFIX}/include/libxml2 + ) +else() + include_directories( + ${ORTP_INCLUDE_DIR} + ${MS2_INCLUDE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${BELLESIP_INCLUDE_DIR} + ) + if(WIN32) + include_directories(${ORTP_ROOT_DIR}/build/vsx/oRTP/oRTP/) + endif(WIN32) +endif() + +add_subdirectory(coreapi) +add_subdirectory(share) + +if(INSTALL_COMPONENT_IN_POSTBUILD) + add_install_target(INSTALL_liblinphone COMP_liblinphone liblinphone) +endif() diff --git a/README b/README index 8913f1205..1e3e0ced0 100644 --- a/README +++ b/README @@ -44,11 +44,12 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol. $ sudo make install + Install zrtpcpp (optional), for unbreakable call encryption - $ sudo apt-get install cmake libssl-dev - $ git clone git://git.linphone.org/zrtpcpp.git - $ cd zrtpcpp && cmake -Denable-ccrtp=false . && make + $ sudo apt-get install cmake + $ git clone https://github.com/wernerd/ZRTPCPP.git + $ cd ZRTPCPP + $ cmake -DCORE_LIB=true -DSDES=false . && make $ sudo make install - + If you get this error: "cc1plus: error: unrecognized command line option ‘-std=c++11’", edit CMakeLists.txt and replace c++11 by c++0x . - Compile linphone diff --git a/README.mingw b/README.mingw index cb1f60c01..4ac4661a7 100644 --- a/README.mingw +++ b/README.mingw @@ -9,7 +9,7 @@ In the feature list, select: * Mingw developer toolkit Let the installer fetch and install everything. -In mingw shell, run +In mingw shell (also refered as msys), run mingw-get install msys-zip mingw-get install msys-unzip @@ -45,70 +45,63 @@ libintl.a libintl.la libintl.dll.a * Download and install Inno Setup Compiler (required only if you run 'make setup.exe'). Add it to your windows Path environment variable. -Get Linphone source code -************************ +* Install msys-git from (http://code.google.com/p/msysgit/). During installation you are asked to make a choice about how line endings are treated by git. Choose "Checkout line endings as they are, commit as they are". THIS CHOICE IS VERY IMPORTANT. OTHERS BREAK AUTOMAKE. -Install msys-git from (http://code.google.com/p/msysgit/). During installation you are asked to make a choice about how line endings are treated by git. -Choose "Checkout line endings as they are, commit as they are". THIS CHOICE IS VERY IMPORTANT. OTHERS BREAK AUTOMAKE. -It is recommended that you create a directory somewhere with a path without any spaces or ~ characters, for example -c:\sources\ -Within msys-git bash, do +General rules for compilation +***************************** + +- It is recommended that you create a directory somewhere with a path without any spaces or ~ characters, for example c:\sources\. + This is the place where source code must be compiled. +- git commands (to retrieve source code) must be performed within msys-git terminal. +- all other commands (configure, autogen.sh, make) must be done within the mingw shell (msys). +In both msys and msys-git windows, change into the directory you created for sources: cd /c/sources -git clone git://git.linphone.org/linphone.git --recursive +Building belle-sip +****************** + * download the sources with msys-git shell using the following command: + $ git clone git://git.linphone.org/belle-sip.git + * compile and install + $ ./autogen.sh + $ ./configure --prefix=/usr --enable-shared --disable-static + $ make && make install -Building -******** +Building Linphone +***************** -WARNING: During the build, windows might slow down suddenly. Using ctl+alt+del to start the windows system monitor, - 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. + * download the sources using the following command: + $ git clone git://git.linphone.org/linphone.git --recursive -#Build linphone itself: -#run autogen.sh after a git checkout or update + * compile + #always run autogen.sh after a git checkout or update + $ ./autogen.sh -./autogen.sh + $ ./configure --prefix=/usr --enable-shared --disable-static + #note: in order to use the tunnel (commercial extension), append --enable-tunnel to the configure line above. -./configure --prefix=/usr --enable-shared --disable-static -#note: in order to use the tunnel, append --enable-tunnel to the configure line above. + $ make + $ make install -#compile: + #Option: make a portable binary zip of linphone + $ make zip -make + #additionally you can make binary installer if you have Inno Setup 5 installed in its default path -#now install to /usr, required for compilation of plugins. + $ make setup.exe + #now you're done, you have a fresh linphone windows installer in the current directory. -make install +Building plugins (optional) +*************************** -#make a binary zip of linphone - -make zip - -#additionally you can make binary installer if you have Inno Setup 5 installed in its default path - -make setup.exe - -#now you're done, you have a fresh linphone windows installer in the current directory. - - - -#build plugins -cd mediastreamer2/plugins/msx264 -./autogen.sh -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 -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=/usr/lib/pkgconfig ./configure --prefix=/usr --enable-shared --disable-static -make -#make a binary zip of this plugin -make zip + This the example for msx264 (H264 plugin), the same applies for other linphone plugins. + $ cd mediastreamer2/plugins/msx264 + $ ./autogen.sh + $ 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 + $ make setup.exe ****************************************************** @@ -161,14 +154,6 @@ When running "make install DESTDIR=", somepath must be absolute and sh $ make install DESTDIR=/usr $ make install DESTDIR=/home//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//belle-sip-install - - building libsrtp * download the sources with $ git clone git://git.linphone.org/srtp.git diff --git a/README.zrtp b/README.zrtp deleted file mode 100644 index c71c8d2d7..000000000 --- a/README.zrtp +++ /dev/null @@ -1,94 +0,0 @@ -ZRTP guide - -== Downloads == -- SRTP -http://sourceforge.net/projects/srtp/ -or "apt-get source libsrtp0" on Debian - -- ZRTP (libzrtpcpp-2.0) -http://www.gnutelephony.org/index.php/GNU_ZRTP - - -== Patch libzrtpcpp == -Index: src/ZIDFile.cpp -=================================================================== ---- src/ZIDFile.cpp (révision 754) -+++ src/ZIDFile.cpp (copie de travail) -@@ -78,10 +78,11 @@ - - // create save file name, rename and re-open - // if rename fails, just unlink old ZID file and create a brand new file -- // just a little inconnvenience for the user, need to verify new SAS -+ // just a little inconvenience for the user, need to verify new SAS - std::string fn = std::string(name) + std::string(".save"); - if (rename(name, fn.c_str()) < 0) { -- unlink(name); -+ // unlink(name); - createZIDFile(name); - return; - } -Index: src/libzrtpcpp/ZrtpCallback.h -=================================================================== ---- src/libzrtpcpp/ZrtpCallback.h (révision 754) -+++ src/libzrtpcpp/ZrtpCallback.h (copie de travail) -@@ -27,7 +27,7 @@ - - #include - #include --#include -+//#include - #include - - /** -Index: src/libzrtpcpp/ZIDRecord.h -=================================================================== ---- src/libzrtpcpp/ZIDRecord.h (révision 754) -+++ src/libzrtpcpp/ZIDRecord.h (copie de travail) -@@ -33,7 +33,7 @@ - - #include - #include --#include -+//#include - - #define IDENTIFIER_LEN 12 - #define RS_LENGTH 32 -Index: CMakeLists.txt -=================================================================== ---- CMakeLists.txt (révision 754) -+++ CMakeLists.txt (copie de travail) -@@ -124,11 +124,15 @@ - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-Wno-long-long -Wno-char-subscripts) - add_definitions(-Wall -ansi -pedantic) -+ add_definitions(-DNEW_STDCPP) - endif() - - add_subdirectory(src) --add_subdirectory(demo) - -+if (enable_ccrtp) -+ add_subdirectory(demo) -+endif() -+ - if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/package/) - MESSAGE(STATUS "package dir not found") - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/package/) - - - -== Create simlinks or move folders == -submodules/external/srtp -> path_to_your_srtp_source -submodules/external/libzrtpcpp -> path_to_your_patched_zrtpcpp_source - - - -== Compilation for Android == -ndk-build BUILD_GPLV3_ZRTP=1 -j5 - - -== Compilation for Desktop version == -First ortp: ./autogen.sh && ./configure --enable-zrtp && make -j5 && sudo make install -Then mediastreamer2: ./autogen.sh && ./configure && make -j5 && sudo make install -Finally linphone: ./autogen.sh && ./configure --enable-external-ortp && make -j5 && sudo make install - diff --git a/build/android/Android.mk b/build/android/Android.mk index e9c9fa1f6..9263f748f 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -60,7 +60,8 @@ LOCAL_SRC_FILES := \ linphone_tunnel_config.c \ message_storage.c \ info.c \ - event.c + event.c \ + xml.c ifndef LINPHONE_VERSION LINPHONE_VERSION = "Devel" diff --git a/build/redhat/INSTALL b/build/redhat/INSTALL new file mode 100644 index 000000000..0b43a5b51 --- /dev/null +++ b/build/redhat/INSTALL @@ -0,0 +1,13 @@ +INSTALL : + +Download and install the repo rpmforge : + http://repoforge.org/use/ + + $ sudo rpm -Uvh + +Download the linphone-release rpm + $ sudo rpm -Uvh + + $ sudo yum install linphone + + diff --git a/build/redhat/README b/build/redhat/README new file mode 100644 index 000000000..7831df9c8 --- /dev/null +++ b/build/redhat/README @@ -0,0 +1,57 @@ +********************************** +* Compiling linphone on RedHat * +********************************** + +Download and install the repo rpmforge : + http://repoforge.org/use/ + + $ sudo rpm -Uhv + $ yum -y update + +- Install build time dependencies + $ sudo yum install libtool intltool + +- Install others dependencies + $ sudo yum install gtk2-devel + $ sudo yum install ffmpeg-devel + $ sudo yum install openldap-devel + +- Download and install packages + $ sudo rpm -Uhv ftp://ftp.icm.edu.pl/vol/rzm2/linux-fedora/linux/epel/6/x86_64/polarssl-1.3.2-1.el6.x86_64.rpm + $ sudo rpm -Uvh ftp://ftp.icm.edu.pl/vol/rzm2/linux-fedora/linux/epel/6/x86_64/polarssl-devel-1.3.2-1.el6.x86_64.rpm + $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Everything/x86_64/os/Packages/antlr3-C-3.2-14.fc15.x86_64.rpm + $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Everything/x86_64/os/Packages/antlr3-C-devel-3.2-14.fc15.x86_64.rpm + $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Everything/x86_64/os/Packages/antlr3-tool-3.2-14.fc15.noarch.rpm + $ sudo rpm -Uhv ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/15/Fedora/i386/os/Packages/antlr3-java-3.2-14.fc15.noarch.rpm + +- Git repository + + Belle-sip : git clone git://git.linphone.org/belle-sip.git + oRTP : git clone git://git.linphone.org/ortp.git + Mediastreamer : git clone git://git.linphone.org/mediastreamer2.git + +- Compile Belle-sip / oRTP / mediastreamer + $ ./autogen.sh + $ ./configure + $ make && make rpm + $ sudo rpm -Uvh + +- Compile Linphone + $ ./autogen.sh + $ ./configure + $ make && make rpm + + +-Create yum repo : + $ cd rpmbuild/RPMS/*arch*/ + $ createrepo . + + Create a file "linphone-release.repo" in /etc/yum.repos.d/ with : + + [linphone-release] + name = Linphone for redhat + baseurl = file/// *path to the new repo* + enabled = 1 + gpgcheck = 0 + + $ sudo yum install linphone diff --git a/build/vsx/LibLinphone/LibLinphone.vcxproj b/build/vsx/LibLinphone/LibLinphone.vcxproj index c2d421126..9bc9aadf9 100644 --- a/build/vsx/LibLinphone/LibLinphone.vcxproj +++ b/build/vsx/LibLinphone/LibLinphone.vcxproj @@ -117,7 +117,7 @@ Level4 $(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories) - __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) + __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;_XKEYCHECK_H;%(PreprocessorDefinitions) false Default NotUsing @@ -147,7 +147,7 @@ Level4 $(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\..\tunnel\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories) - __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) + __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;_XKEYCHECK_H;%(PreprocessorDefinitions) true true Default @@ -207,6 +207,7 @@ + diff --git a/console/Makefile.am b/console/Makefile.am index 3a975e9c4..314f5d612 100644 --- a/console/Makefile.am +++ b/console/Makefile.am @@ -13,7 +13,8 @@ COMMON_CFLAGS=\ $(MEDIASTREAMER_CFLAGS) \ $(VIDEO_CFLAGS) \ $(READLINE_CFLAGS) \ - $(SQLITE3_CFLAGS) + $(SQLITE3_CFLAGS) \ + $(LIBXML2_CFLAGS) if BUILD_CONSOLE @@ -29,7 +30,8 @@ linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \ $(READLINE_LIBS) \ $(SQLITE3_LIBS) \ $(X11_LIBS) \ - $(BELLESIP_LIBS) + $(BELLESIP_LIBS) \ + $(LIBXML2_LIBS) if BUILD_WIN32 #special build of linphonec to detach from the windows console diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt new file mode 100644 index 000000000..628695414 --- /dev/null +++ b/coreapi/CMakeLists.txt @@ -0,0 +1,140 @@ +find_program(GIT git) + +set(GIT_VERSION "unknown") +if(GIT) + execute_process( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GIT} describe --always + OUTPUT_VARIABLE GIT_DESCRIBE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GIT} describe --abbrev=0 + OUTPUT_VARIABLE GIT_TAG + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GIT} rev-parse HEAD + OUTPUT_VARIABLE GIT_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(GIT_DESCRIBE) + set(GIT_VERSION ${GIT_DESCRIBE}) + else(GIT_DESCRIBE) + if(GIT_REVISION) + set(GIT_VERSION ${GIT_REVISION}) + endif(GIT_REVISION) + endif(GIT_DESCRIBE) +endif(GIT) +execute_process( + COMMAND ${CMAKE_COMMAND} -E echo "#define LIBLINPHONE_GIT_VERSION \"${GIT_VERSION}\"" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/liblinphone_gitversion.h +) + +set(SOURCE_FILES + address.c + authentication.c + 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_events.c + bellesip_sal/sal_op_impl.c + bellesip_sal/sal_op_info.c + bellesip_sal/sal_op_message.c + bellesip_sal/sal_op_presence.c + bellesip_sal/sal_op_publish.c + bellesip_sal/sal_op_registration.c + bellesip_sal/sal_sdp.c + callbacks.c + chat.c + conference.c + ec-calibrator.c + enum.c + event.c + friend.c + info.c + linphonecall.c + linphonecore.c + #linphone_tunnel.cc + linphone_tunnel_stubs.c + linphone_tunnel_config.c + lpconfig.c + lsd.c + message_storage.c + misc.c + offeranswer.c + presence.c + proxy.c + sal.c + siplogin.c + sipsetup.c + #TunnelManager.cc + xml.c + bellesip_sal/sal_impl.h + enum.h + event.h + linphonecore.h + linphonecore_utils.h + linphonefriend.h + linphone_tunnel.h + lpconfig.h + offeranswer.h + private.h + sipsetup.h +) + +add_definitions( + -D_TRUE_TIME + -DIN_LINPHONE + -DUSE_BELLESIP + #-DTUNNEL_ENABLED + #-DVIDEO_ENABLED + -DLINPHONE_PACKAGE_NAME="linphone" + -DLINPHONE_VERSION="Devel" + -DLIBLINPHONE_EXPORTS + -DLINPHONE_PLUGINS_DIR="" +) + +if(LINPHONE_ENABLE_VIDEO) + add_definitions(-DVIDEO_ENABLED) +endif(LINPHONE_ENABLE_VIDEO) + +if(WIN32) +add_definitions( + -DWINDOW_NATIVE + /FIliblinphone_gitversion.h +) + +set(LIBS ws2_32) +endif(WIN32) +set(LIBS ${LIBS} libortp libmediastreamer_base libmediastreamer_voip libbellesip libxml2) + +add_library(liblinphone SHARED ${SOURCE_FILES}) +set_target_properties(liblinphone PROPERTIES VERSION 3.6.99 SOVERSION 5) + +target_link_libraries(liblinphone ${LIBS}) + +install(TARGETS liblinphone + COMPONENT COMP_liblinphone + DESTINATION ${LIB_INSTALL_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +if(USE_INSTALLED_COMPONENTS) + add_dependencies(liblinphone + INSTALL_libortp + INSTALL_libmediastreamer2 + INSTALL_libbellesip + INSTALL_libxml2) +endif() + + + +file(GLOB HEADER_FILES "*.h") + +install(FILES ${HEADER_FILES} + COMPONENT COMP_liblinphone + DESTINATION include/linphone + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 3933ddb05..61d240464 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -51,6 +51,7 @@ liblinphone_la_SOURCES=\ contactprovider.c contactprovider.h contact_providers_priv.h \ ldap/ldapprovider.c ldap/ldapprovider.h \ dict.c \ + xml.c \ $(GITVERSION_FILE) if BUILD_UPNP diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 08986eb89..f15b07ffb 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -464,6 +464,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub; if (ctx->callbacks.text_received==NULL) ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub; + if (ctx->callbacks.is_composing_received==NULL) + ctx->callbacks.is_composing_received=(SalOnIsComposingReceived)unimplemented_stub; if (ctx->callbacks.ping_reply==NULL) ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub; if (ctx->callbacks.auth_requested==NULL) @@ -915,5 +917,12 @@ unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){ return belle_sip_random_bytes(ret,size); } +belle_sip_source_t * sal_create_timer(Sal *sal, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms, const char* timer_name) { + belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack); + return belle_sip_main_loop_create_timeout(ml, func, data, timeout_value_ms, timer_name); +} - +void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer) { + belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack); + belle_sip_main_loop_remove_source(ml, timer); +} diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index a286001b2..e6c7229f4 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -19,6 +19,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal_impl.h" #include "offeranswer.h" +static int extract_sdp(belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error); + /*used for calls terminated before creation of a dialog*/ static void call_set_released(SalOp* op){ if (!op->call_released){ @@ -84,8 +86,8 @@ static void sdp_process(SalOp *h){ } } } - } + 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; @@ -159,10 +161,13 @@ static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminat 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); + SalReason reason; + if (extract_sdp(BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) { + if (sdp){ + op->base.remote_media=sal_media_description_new(); + sdp_to_media_description(sdp,op->base.remote_media); + if (op->base.local_media) sdp_process(op); + }/*if no sdp in response, what can we do ?*/ } } @@ -322,15 +327,61 @@ static void unsupported_method(belle_sip_server_transaction_t* server_transactio return; } -static void process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) { +/* + * Extract the sdp from a sip message. + * If there is no body in the message, the session_desc is set to null, 0 is returned. + * If body was present is not a SDP or parsing of SDP failed, -1 is returned and SalReason is set appropriately. + * +**/ +static int extract_sdp(belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error) { + belle_sip_header_content_type_t* content_type=belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); + if (content_type){ + if (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 + && strcmp("sdp",belle_sip_header_content_type_get_subtype(content_type))==0) { + *session_desc=belle_sdp_session_description_parse(belle_sip_message_get_body(message)); + if (*session_desc==NULL) { + ms_error("Failed to parse SDP message."); + *error=SalReasonNotAcceptable; + return -1; + } + }else{ + *error=SalReasonUnsupportedContent; + return -1; + } + }else *session_desc=NULL; + return 0; +} + +static int is_media_description_acceptable(SalMediaDescription *md){ + if (md->n_total_streams==0){ + ms_warning("Media description does not define any stream."); + return FALSE; + } + return TRUE; +} + +static int process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) { belle_sdp_session_description_t* sdp; - 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; + int err=0; + SalReason reason; + if (extract_sdp(BELLE_SIP_MESSAGE(invite),&sdp,&reason)==0) { + if (sdp){ + op->sdp_offering=FALSE; + op->base.remote_media=sal_media_description_new(); + sdp_to_media_description(sdp,op->base.remote_media); + /*make some sanity check about the SDP received*/ + if (!is_media_description_acceptable(op->base.remote_media)){ + err=-1; + reason=SalReasonNotAcceptable; + } + belle_sip_object_unref(sdp); + }else op->sdp_offering=TRUE; /*INVITE without SDP*/ + }else err=-1; + + if (err==-1){ + sal_call_decline(op,reason,NULL); + } + return err; } static void process_request_event(void *op_base, const belle_sip_request_event_t *event) { @@ -415,13 +466,18 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t /*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); + SalReason reason; + if (extract_sdp(BELLE_SIP_MESSAGE(req),&sdp,&reason)==0){ + if (sdp){ + if (op->base.remote_media) + sal_media_description_unref(op->base.remote_media); + op->base.remote_media=sal_media_description_new(); + sdp_to_media_description(sdp,op->base.remote_media); + sdp_process(op); + belle_sip_object_unref(sdp); + }else{ + ms_warning("SDP expected in ACK but not found."); + } } } /*FIXME @@ -445,9 +501,8 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t sal_media_description_unref(op->result); op->result=NULL; } - process_sdp_for_invite(op,req); - - op->base.root->callbacks.call_updating(op); + if (process_sdp_for_invite(op,req)==0) + 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")) { diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 47958ac6e..51dec9b01 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -334,7 +334,7 @@ SalReason sal_reason_to_sip_code(SalReason r){ case SalReasonForbidden: ret=403; break; - case SalReasonMedia: + case SalReasonUnsupportedContent: ret=415; break; case SalReasonNotFound: @@ -356,7 +356,7 @@ SalReason sal_reason_to_sip_code(SalReason r){ ret=401; break; case SalReasonNotAcceptable: - ret=488; + ret=488; /*or maybe 606 Not Acceptable ?*/ break; case SalReasonNoMatch: ret=481; @@ -385,7 +385,7 @@ void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal break; case 415: *sal_err=SalErrorFailure; - *sal_reason=SalReasonMedia; + *sal_reason=SalReasonUnsupportedContent; break; case 422: ms_error ("422 not implemented yet");; diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index 1e3dc12f0..25402aef2 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -76,6 +76,10 @@ static bool_t is_external_body(belle_sip_header_content_type_t* content_type) { return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0; } +static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) { + return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 + && strcmp("im-iscomposing+xml",belle_sip_header_content_type_get_subtype(content_type))==0; +} static void process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; @@ -88,8 +92,6 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t 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; @@ -99,7 +101,8 @@ static void process_request_event(void *op_base, const belle_sip_request_event_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)))) { - + SalMessage salmsg; + char message_id[256]={0}; 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)); @@ -121,6 +124,17 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t belle_sip_free(from); if (salmsg.url) ms_free((char*)salmsg.url); response_code=200; + } else if (content_type && is_im_iscomposing(content_type)) { + SalIsComposing saliscomposing; + address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) + ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); + from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address)); + saliscomposing.from=from; + saliscomposing.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); + op->base.root->callbacks.is_composing_received(op,&saliscomposing); + belle_sip_object_unref(address); + belle_sip_free(from); + 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)); diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index a9c364c52..c2c382114 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -612,7 +612,8 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de if (lc->vtable.display_status) lc->vtable.display_status(lc,msg600); break; - case SalReasonMedia: + case SalReasonUnsupportedContent: /*params.media_encryption == LinphoneMediaEncryptionSRTP && !linphone_core_is_media_encryption_mandatory(lc)) { @@ -843,6 +844,11 @@ static void text_received(SalOp *op, const SalMessage *msg){ } } +static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) { + LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + linphone_core_is_composing_received(lc, op, is_composing); +} + 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); } @@ -1001,8 +1007,14 @@ static int op_equals(LinphoneCall *a, SalOp *b) { 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); - + const MSList* calls; + + if (chat_msg == NULL) { + // Do not handle delivery status for isComposing messages. + return; + } + calls = linphone_core_get_calls(chat_msg->chat_room->lc); + chat_msg->state=chatStatusSal2Linphone(status); linphone_chat_message_store_state(chat_msg); if (chat_msg && chat_msg->cb) { @@ -1123,6 +1135,7 @@ SalCallbacks linphone_sal_callbacks={ refer_received, text_received, text_delivery_update, + is_composing_received, notify_refer, subscribe_received, subscribe_closed, diff --git a/coreapi/chat.c b/coreapi/chat.c index c2e6a9cf5..6dc8ae57a 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -26,6 +26,12 @@ #include "private.h" #include "lpconfig.h" +#include + +#define COMPOSING_DEFAULT_IDLE_TIMEOUT 15 +#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60 +#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120 + /** * @addtogroup chatroom * @{ @@ -85,13 +91,40 @@ LinphoneChatRoom* linphone_core_get_or_create_chat_room(LinphoneCore* lc, const } return ret; } - + +static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) { + if (cr->composing_idle_timer) { + sal_cancel_timer(cr->lc->sal, cr->composing_idle_timer); + belle_sip_object_unref(cr->composing_idle_timer); + cr->composing_idle_timer = NULL; + } +} + +static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr) { + if (cr->composing_refresh_timer) { + sal_cancel_timer(cr->lc->sal, cr->composing_refresh_timer); + belle_sip_object_unref(cr->composing_refresh_timer); + cr->composing_refresh_timer = NULL; + } +} + +static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr) { + if (cr->remote_composing_refresh_timer) { + sal_cancel_timer(cr->lc->sal, cr->remote_composing_refresh_timer); + belle_sip_object_unref(cr->remote_composing_refresh_timer); + cr->remote_composing_refresh_timer = NULL; + } +} + /** * Destroy a LinphoneChatRoom. * @param cr #LinphoneChatRoom object */ void linphone_chat_room_destroy(LinphoneChatRoom *cr){ LinphoneCore *lc=cr->lc; + linphone_chat_room_delete_composing_idle_timer(cr); + linphone_chat_room_delete_composing_refresh_timer(cr); + linphone_chat_room_delete_remote_composing_refresh_timer(cr); lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr); linphone_address_destroy(cr->peer_url); ms_free(cr->peer); @@ -142,6 +175,12 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM msg->dir=LinphoneChatMessageOutgoing; msg->from=linphone_address_new(identity); msg->storage_id=linphone_chat_message_store(msg); + + if (cr->is_composing == LinphoneIsComposingActive) { + cr->is_composing = LinphoneIsComposingIdle; + } + linphone_chat_room_delete_composing_idle_timer(cr); + linphone_chat_room_delete_composing_refresh_timer(cr); } /** @@ -159,7 +198,10 @@ void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, //legacy API if (lc->vtable.text_received!=NULL) lc->vtable.text_received(lc, cr, msg->from, msg->message); if (lc->vtable.message_received!=NULL) lc->vtable.message_received(lc, cr,msg); - + if (cr->lc->vtable.is_composing_received != NULL) { + cr->remote_is_composing = LinphoneIsComposingIdle; + cr->lc->vtable.is_composing_received(cr->lc, cr); + } } /** @@ -225,6 +267,85 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag ms_free(from); } +static int linphone_chat_room_remote_refresh_composing_expired(void *data, unsigned int revents) { + LinphoneChatRoom *cr = (LinphoneChatRoom *)data; + belle_sip_object_unref(cr->remote_composing_refresh_timer); + cr->remote_composing_refresh_timer = NULL; + cr->remote_is_composing = LinphoneIsComposingIdle; + if (cr->lc->vtable.is_composing_received != NULL) + cr->lc->vtable.is_composing_received(cr->lc, cr); + return BELLE_SIP_STOP; +} + +static const char *iscomposing_prefix = "/xsi:isComposing"; + +static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsing_context_t *xml_ctx) { + char xpath_str[MAX_XPATH_LENGTH]; + xmlXPathObjectPtr iscomposing_object; + const char *state_str = NULL; + const char *refresh_str = NULL; + int refresh_duration = lp_config_get_int(cr->lc->config, "sip", "composing_remote_refresh_timeout", COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT); + int i; + LinphoneIsComposingState state = LinphoneIsComposingIdle; + + if (linphone_create_xml_xpath_context(xml_ctx) < 0) return; + + xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"xsi", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing"); + iscomposing_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, iscomposing_prefix); + if ((iscomposing_object != NULL) && (iscomposing_object->nodesetval != NULL)) { + for (i = 1; i <= iscomposing_object->nodesetval->nodeNr; i++) { + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:state", iscomposing_prefix, i); + state_str = linphone_get_xml_text_content(xml_ctx, xpath_str); + if (state_str == NULL) continue; + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:refresh", iscomposing_prefix, i); + refresh_str = linphone_get_xml_text_content(xml_ctx, xpath_str); + } + } + + if (state_str != NULL) { + if (strcmp(state_str, "active") == 0) { + state = LinphoneIsComposingActive; + if (refresh_str != NULL) { + refresh_duration = atoi(refresh_str); + } + if (!cr->remote_composing_refresh_timer) { + cr->remote_composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_remote_refresh_composing_expired, cr, refresh_duration * 1000, "composing remote refresh timeout"); + } else { + belle_sip_source_set_timeout(cr->remote_composing_refresh_timer, refresh_duration * 1000); + } + } else { + linphone_chat_room_delete_remote_composing_refresh_timer(cr); + } + + cr->remote_is_composing = state; + if (cr->lc->vtable.is_composing_received != NULL) + cr->lc->vtable.is_composing_received(cr->lc, cr); + } +} + +static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text) { + xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); + xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); + xml_ctx->doc = xmlReadDoc((const unsigned char*)text, 0, NULL, 0); + if (xml_ctx->doc != NULL) { + process_im_is_composing_notification(cr, xml_ctx); + } else { + ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer); + } + linphone_xmlparsing_context_destroy(xml_ctx); +} + +void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing) { + LinphoneChatRoom *cr = linphone_core_get_or_create_chat_room(lc, is_composing->from); + if (cr != NULL) { + linphone_chat_room_notify_is_composing(cr, is_composing->text); + } +} + +bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) { + return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE; +} + /** * Returns back pointer to LinphoneCore object. **/ @@ -319,6 +440,131 @@ void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* _linphone_chat_room_send_message(cr, msg); } +static char * linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) { + xmlBufferPtr buf; + xmlTextWriterPtr writer; + int err; + char *content = NULL; + + buf = xmlBufferCreate(); + if (buf == NULL) { + ms_error("Error creating the XML buffer"); + return content; + } + writer = xmlNewTextWriterMemory(buf, 0); + if (writer == NULL) { + ms_error("Error creating the XML writer"); + return content; + } + + err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); + if (err >= 0) { + err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"isComposing", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing"); + } + if (err >= 0) { + err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", + NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance"); + } + if (err >= 0) { + err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xsi", (const xmlChar *)"schemaLocation", + NULL, (const xmlChar *)"urn:ietf:params:xml:ns:im-composing iscomposing.xsd"); + } + if (err >= 0) { + err = xmlTextWriterWriteElement(writer, (const xmlChar *)"state", + (cr->is_composing == LinphoneIsComposingActive) ? (const xmlChar *)"active" : (const xmlChar *)"idle"); + } + if ((err >= 0) && (cr->is_composing == LinphoneIsComposingActive)) { + char refresh_str[4] = { 0 }; + int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT); + snprintf(refresh_str, sizeof(refresh_str), "%u", refresh_timeout); + err = xmlTextWriterWriteElement(writer, (const xmlChar *)"refresh", (const xmlChar *)refresh_str); + } + if (err >= 0) { + /* Close the "isComposing" element. */ + err = xmlTextWriterEndElement(writer); + } + if (err >= 0) { + err = xmlTextWriterEndDocument(writer); + } + if (err > 0) { + /* xmlTextWriterEndDocument returns the size of the content. */ + content = ms_strdup((char *)buf->content); + } + xmlFreeTextWriter(writer); + xmlBufferFree(buf); + return content; +} + +static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *cr) { + SalOp *op = NULL; + LinphoneCall *call; + const char *identity = NULL; + char *content = NULL; + + if (lp_config_get_int(cr->lc->config, "sip", "chat_use_call_dialogs", 0)) { + if ((call = linphone_core_get_call_by_remote_address(cr->lc, cr->peer)) != NULL) { + if (call->state == LinphoneCallConnected || + call->state == LinphoneCallStreamsRunning || + call->state == LinphoneCallPaused || + call->state == LinphoneCallPausing || + call->state == LinphoneCallPausedByRemote) { + ms_message("send SIP message through the existing call."); + op = call->op; + identity = linphone_core_find_best_identity(cr->lc, linphone_call_get_remote_address(call)); + } + } + } + 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); + linphone_configure_op(cr->lc, op, cr->peer_url, NULL, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0)); + } + content = linphone_chat_room_create_is_composing_xml(cr); + if (content != NULL) { + sal_message_send(op, identity, cr->peer, "application/im-iscomposing+xml", content); + ms_free(content); + } +} + +static int linphone_chat_room_stop_composing(void *data, unsigned int revents) { + LinphoneChatRoom *cr = (LinphoneChatRoom *)data; + cr->is_composing = LinphoneIsComposingIdle; + linphone_chat_room_send_is_composing_notification(cr); + linphone_chat_room_delete_composing_refresh_timer(cr); + belle_sip_object_unref(cr->composing_idle_timer); + cr->composing_idle_timer = NULL; + return BELLE_SIP_STOP; +} + +static int linphone_chat_room_refresh_composing(void *data, unsigned int revents) { + LinphoneChatRoom *cr = (LinphoneChatRoom *)data; + linphone_chat_room_send_is_composing_notification(cr); + return BELLE_SIP_CONTINUE; +} + +void linphone_chat_room_compose(LinphoneChatRoom *cr) { + int idle_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_idle_timeout", COMPOSING_DEFAULT_IDLE_TIMEOUT); + int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT); + if (cr->is_composing == LinphoneIsComposingIdle) { + cr->is_composing = LinphoneIsComposingActive; + linphone_chat_room_send_is_composing_notification(cr); + if (!cr->composing_refresh_timer) { + cr->composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_refresh_composing, cr, refresh_timeout * 1000, "composing refresh timeout"); + } else { + belle_sip_source_set_timeout(cr->composing_refresh_timer, refresh_timeout * 1000); + } + if (!cr->composing_idle_timer) { + cr->composing_idle_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_stop_composing, cr, idle_timeout * 1000, "composing idle timeout"); + } + } + belle_sip_source_set_timeout(cr->composing_idle_timer, idle_timeout * 1000); +} + /** * Returns a #LinphoneChatMessageState as a string. */ diff --git a/coreapi/contact_providers_priv.h b/coreapi/contact_providers_priv.h index 41b7bd327..b3d667df6 100644 --- a/coreapi/contact_providers_priv.h +++ b/coreapi/contact_providers_priv.h @@ -17,7 +17,7 @@ #ifndef CONTACT_PROVIDERS_PRIV_H #define CONTACT_PROVIDERS_PRIV_H -#include +#include "private.h" #include "linphonecore.h" /* Base for contact search and contact provider */ diff --git a/coreapi/contactprovider.c b/coreapi/contactprovider.c index cd40887b1..2ee037c78 100644 --- a/coreapi/contactprovider.c +++ b/coreapi/contactprovider.c @@ -59,7 +59,7 @@ int linphone_contact_search_compare(const void* a, const void* b) { return !(ra->id == rb->id); // return 0 if id is equal, 1 otherwise } -LinphoneContactSearch*linphone_ldap_contact_search_ref(void* obj) +LinphoneContactSearch*linphone_contact_search_ref(void* obj) { return LINPHONE_CONTACT_SEARCH(belle_sip_object_ref(obj)); } @@ -136,4 +136,3 @@ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider) NULL, /* begin_search -> pure virtual */ NULL /* cancel_search -> pure virtual */ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END - diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index 53b9331fb..c41ca2933 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -280,5 +280,11 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { } + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 371b9af8e..3278c990d 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -202,5 +202,13 @@ public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessa } + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + if (cr.isRemoteComposing()) + write("Remote is writing a message"); + else + write("Remote has stop writing"); + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index e8ba341ad..763e18f12 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -206,5 +206,11 @@ public class TutorialHelloWorld implements LinphoneCoreListener { } + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index 93aa87d04..8384ed16e 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -237,6 +237,12 @@ public class TutorialRegistration implements LinphoneCoreListener { } + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 4510ef12f..b386a3e33 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -595,9 +595,14 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, &call->params); + + /* + * Initialize call parameters according to incoming call parameters. This is to avoid to ask later (during reINVITEs) for features that the remote + * end apparently does not support. This features are: privacy, video + */ /*set privacy*/ call->current_params.privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); - + /*set video support */ md=sal_call_get_remote_media_description(op); call->params.has_video &= !!lc->video_policy.automatically_accept; if (md) { @@ -605,6 +610,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro // In this case WE chose the media parameters according to policy. call->params.has_video &= linphone_core_media_description_contains_video_stream(md); } + switch (linphone_core_get_firewall_policy(call->core)) { case LinphonePolicyUseIce: call->ice_session = ice_session_new(); @@ -1760,21 +1766,20 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna /* valid local tags are > 0 */ if (stream->proto == SalProtoRtpSavp) { - local_st_desc=sal_media_description_find_stream(call->localdesc, - SalProtoRtpSavp,SalAudio); - crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag); + local_st_desc=sal_media_description_find_stream(call->localdesc,SalProtoRtpSavp,SalAudio); + crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag); - if (crypto_idx >= 0) { - audio_stream_enable_srtp( - call->audiostream, - stream->crypto[0].algo, - local_st_desc->crypto[crypto_idx].master_key, - stream->crypto[0].master_key); - call->audiostream_encrypted=TRUE; - } else { - ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag); - call->audiostream_encrypted=FALSE; - } + if (crypto_idx >= 0) { + audio_stream_enable_srtp( + call->audiostream, + stream->crypto[0].algo, + local_st_desc->crypto[crypto_idx].master_key, + stream->crypto[0].master_key); + call->audiostream_encrypted=TRUE; + } else { + ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag); + call->audiostream_encrypted=FALSE; + } }else call->audiostream_encrypted=FALSE; if (call->params.in_conference){ /*transform the graph to connect it to the conference filter */ @@ -1929,7 +1934,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut params.zid_file=lc->zrtp_secrets_cache; audio_stream_enable_zrtp(call->audiostream,¶ms); - }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){ + }else{ call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ? LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; } @@ -2247,6 +2252,111 @@ const LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call) { return stats; } +float linphone_call_stats_update_sender_loss_rate(const LinphoneCallStats *stats) { + const report_block_t *srb = NULL; + + if (!stats || !stats->sent_rtcp) + return 0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return 0.0; + return 100.0 * report_block_get_fraction_lost(srb) / 256.0; +} + +float linphone_call_stats_update_receiver_loss_rate(const LinphoneCallStats *stats) { + const report_block_t *rrb = NULL; + + if (!stats || !stats->received_rtcp) + return 0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return 0.0; + return 100.0 * report_block_get_fraction_lost(rrb) / 256.0; +} + +float linphone_call_stats_update_sender_interarrival_jitter(const LinphoneCallStats *stats, LinphoneCall *call) { + const LinphoneCallParams *params; + const PayloadType *pt; + const report_block_t *srb = NULL; + + if (!stats || !call || !stats->sent_rtcp) + return 0.0; + params = linphone_call_get_current_params(call); + if (!params) + return 0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return 0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + if (!pt || (pt->clock_rate == 0)) + return 0.0; + return (float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate; +} + +float linphone_call_stats_update_receiver_interarrival_jitter(const LinphoneCallStats *stats, LinphoneCall *call) { + const LinphoneCallParams *params; + const PayloadType *pt; + const report_block_t *rrb = NULL; + + if (!stats || !call || !stats->received_rtcp) + return 0.0; + params = linphone_call_get_current_params(call); + if (!params) + return 0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return 0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + if (!pt || (pt->clock_rate == 0)) + return 0.0; + return (float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate; +} + +uint64_t linphone_call_stats_update_late_packets_cumulative_number(const LinphoneCallStats *stats, LinphoneCall *call) { + rtp_stats_t rtp_stats; + + if (!stats || !call) + return 0; + memset(&rtp_stats, 0, sizeof(rtp_stats)); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + audio_stream_get_local_rtp_stats(call->audiostream, &rtp_stats); +#ifdef VIDEO_ENABLED + else + video_stream_get_local_rtp_stats(call->videostream, &rtp_stats); +#endif + return rtp_stats.outoftime; +} + /** * Enable recording of the call (voice-only). * This function must be used before the call parameters are assigned to the call. @@ -2572,7 +2682,6 @@ bool_t linphone_call_is_in_conference(const LinphoneCall *call) { return call->params.in_conference; } - /** * Perform a zoom of the video displayed during a call. * @param call the call. @@ -2608,28 +2717,16 @@ void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, }else ms_warning("Could not apply zoom: video output wasn't activated."); } -#ifndef USE_BELLESIP -static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){ -#else static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){ -#endif LinphoneAddress *ctt=NULL; -#ifdef USE_BELLESIP LinphoneAddress *ret=NULL; -#else - char* ret; -#endif const char *localip=call->localip; /* first use user's supplied ip address if asked*/ if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ ctt=linphone_core_get_primary_contact_parsed(lc); linphone_address_set_domain(ctt,linphone_core_get_nat_address_resolved(lc)); - #ifdef USE_BELLESIP ret=ctt; - #else - ret=linphone_address_as_string(ctt); - #endif } else if (call->op && sal_op_get_contact(call->op)!=NULL){ /* if already choosed, don't change it */ return NULL; @@ -2637,19 +2734,11 @@ static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , /* if the ping OPTIONS request succeeded use the contact guessed from the received, rport*/ ms_message("Contact has been fixed using OPTIONS"/* to %s",guessed*/); -#ifdef USE_BELLESIP ret=linphone_address_clone(sal_op_get_contact(call->ping_op));; -#else - ret=ms_strdup(sal_op_get_contact(call->ping_op)); -#endif } else if (dest_proxy && dest_proxy->op && sal_op_get_contact(dest_proxy->op)){ /*if using a proxy, use the contact address as guessed with the REGISTERs*/ ms_message("Contact has been fixed using proxy" /*to %s",fixed_contact*/); -#ifdef USE_BELLESIP ret=linphone_address_clone(sal_op_get_contact(dest_proxy->op)); -#else - ret=ms_strdup(sal_op_get_contact(dest_proxy->op)); -#endif } else { ctt=linphone_core_get_primary_contact_parsed(lc); if (ctt!=NULL){ @@ -2657,16 +2746,9 @@ static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , linphone_address_set_domain(ctt,localip); linphone_address_set_port(ctt,linphone_core_get_sip_port(lc)); ms_message("Contact has been fixed using local ip"/* to %s",ret*/); -#ifdef USE_BELLESIP ret=ctt; -#else - ret=linphone_address_as_string_uri_only(ctt); -#endif } } -#ifndef USE_BELLESIP - if (ctt) linphone_address_destroy(ctt); -#endif return ret; @@ -2688,4 +2770,4 @@ void linphone_call_set_contact_op(LinphoneCall* call) { sal_op_set_contact(call->op, contact); linphone_address_destroy(contact); } -} +} \ No newline at end of file diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index f4773e111..f21f9af6e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2873,17 +2873,21 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ return FALSE; } -bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md){ - if (linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP){ - int i; - for(i=0;in_active_streams;i++){ - SalStreamDescription *sd=&md->streams[i]; - if (sd->proto!=SalProtoRtpSavp){ - return TRUE; - } +bool_t linphone_core_media_description_has_srtp(const SalMediaDescription *md){ + int i; + if (md->n_active_streams==0) return FALSE; + + for(i=0;in_active_streams;i++){ + const SalStreamDescription *sd=&md->streams[i]; + if (sd->proto!=SalProtoRtpSavp){ + return FALSE; } } - return FALSE; + return TRUE; +} + +bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md){ + return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !linphone_core_media_description_has_srtp(md); } void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ @@ -2899,7 +2903,7 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ md=sal_call_get_final_media_description(call->op); if (md){ if (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md)){ - sal_call_decline(call->op,SalReasonMedia,NULL); + sal_call_decline(call->op,SalReasonNotAcceptable,NULL); linphone_call_stop_media_streams(call); linphone_core_del_call(lc,call); linphone_call_unref(call); @@ -3325,6 +3329,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, linphone_call_set_state(call,LinphoneCallConnected,"Connected"); new_md=sal_call_get_final_media_description(call->op); linphone_core_update_streams(lc, call, new_md); + linphone_call_fix_call_parameters(call); if (new_md){ linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); }else call->media_pending=TRUE; @@ -6267,7 +6272,7 @@ void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){ * **/ int linphone_core_get_video_dscp(const LinphoneCore *lc){ - return lp_config_get_int(lc->config,"rtp","video_dscp",0x2e); + return lp_config_get_int(lc->config,"rtp","video_dscp",0); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index c6a3332df..0ad6030da 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ortp/payloadtype.h" #include "mediastreamer2/mscommon.h" #include "mediastreamer2/msvideo.h" +#include "mediastreamer2/mediastream.h" #ifdef IN_LINPHONE #include "sipsetup.h" @@ -32,9 +33,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "lpconfig.h" -#include -#include - #define LINPHONE_IPADDR_SIZE 64 #define LINPHONE_HOSTNAME_SIZE 128 @@ -94,6 +92,7 @@ typedef struct _LCSipTransports{ /** * Enum describing transport type for LinphoneAddress. + * @ingroup linphone_address **/ enum _LinphoneTransportType{ LinphoneTransportUdp, @@ -103,6 +102,10 @@ enum _LinphoneTransportType{ }; /*this enum MUST be kept in sync with the SalTransport from sal.h*/ +/** + * Typedef for transport type enum. + * @ingroup linphone_address +**/ typedef enum _LinphoneTransportType LinphoneTransportType; /** @@ -166,7 +169,7 @@ enum _LinphoneReason{ LinphoneReasonNotFound, /**GetMethodID(listenerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V"); + isComposingReceivedId = env->GetMethodID(listenerClass,"isComposingReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;)V"); dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V"); infoReceivedId = env->GetMethodID(listenerClass,"infoReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneInfoMessage;)V"); @@ -307,6 +309,7 @@ public: jmethodID notifyPresenceReceivedId; jmethodID textReceivedId; jmethodID messageReceivedId; + jmethodID isComposingReceivedId; jmethodID dtmfReceivedId; jmethodID callStatsUpdatedId; jmethodID transferStateId; @@ -550,6 +553,19 @@ public: ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room) ,env->NewObject(lcData->chatMessageClass,lcData->chatMessageCtrId,(jlong)msg)); } + static void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->isComposingReceivedId + ,lcData->core + ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room)); + } static void ecCalibrationStatus(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data) { JNIEnv *env = 0; jint result = jvm->AttachCurrentThread(&env,NULL); @@ -2311,6 +2327,12 @@ extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_deleteHistory(JNIEnv ,jlong ptr) { linphone_chat_room_delete_history((LinphoneChatRoom*)ptr); } +JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneChatRoomImpl_compose(JNIEnv *env, jobject thiz, jlong ptr) { + linphone_chat_room_compose((LinphoneChatRoom *)ptr); +} +JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneChatRoomImpl_isRemoteComposing(JNIEnv *env, jobject thiz, jlong ptr) { + return (jboolean)linphone_chat_room_is_remote_composing((LinphoneChatRoom *)ptr); +} extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_deleteMessage(JNIEnv* env ,jobject thiz ,jlong room diff --git a/coreapi/misc.c b/coreapi/misc.c index aaf8c2167..e1d17e8be 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -1239,16 +1239,16 @@ SalReason linphone_reason_to_sal(LinphoneReason reason){ return SalReasonTemporarilyUnavailable; case LinphoneReasonBusy: return SalReasonBusy; - case LinphoneReasonMedia: - return SalReasonMedia; + case LinphoneReasonNotAcceptable: + return SalReasonNotAcceptable; case LinphoneReasonIOError: return SalReasonServiceUnavailable; case LinphoneReasonDoNotDisturb: return SalReasonDoNotDisturb; case LinphoneReasonUnauthorized: return SalReasonUnauthorized; - case LinphoneReasonNotAcceptable: - return SalReasonNotAcceptable; + case LinphoneReasonUnsupportedContent: + return SalReasonUnsupportedContent; case LinphoneReasonNoMatch: return SalReasonNoMatch; } @@ -1273,8 +1273,8 @@ LinphoneReason linphone_reason_from_sal(SalReason r){ case SalReasonForbidden: ret=LinphoneReasonBadCredentials; break; - case SalReasonMedia: - ret=LinphoneReasonMedia; + case SalReasonNotAcceptable: + ret=LinphoneReasonNotAcceptable; break; case SalReasonNotFound: ret=LinphoneReasonNotFound; @@ -1294,8 +1294,8 @@ LinphoneReason linphone_reason_from_sal(SalReason r){ case SalReasonUnauthorized: ret=LinphoneReasonUnauthorized; break; - case SalReasonNotAcceptable: - ret=LinphoneReasonNotAcceptable; + case SalReasonUnsupportedContent: + ret=LinphoneReasonUnsupportedContent; break; case SalReasonNoMatch: ret=LinphoneReasonNoMatch; diff --git a/coreapi/presence.c b/coreapi/presence.c index 3d50548bb..668d8ad4b 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -22,15 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "lpconfig.h" #include "linphonepresence.h" -#include -#include -#include -#include - - -#define XMLPARSING_BUFFER_LEN 2048 -#define MAX_XPATH_LENGTH 256 - extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol); @@ -83,13 +74,6 @@ struct _LinphonePresenceModel { MSList *notes; /**< A list of _LinphonePresenceNote structures. */ }; -typedef struct _xmlparsing_context { - xmlDoc *doc; - xmlXPathContextPtr xpath_ctx; - char errorBuffer[XMLPARSING_BUFFER_LEN]; - char warningBuffer[XMLPARSING_BUFFER_LEN]; -} xmlparsing_context_t; - static const char *person_prefix = "/pidf:presence/dm:person"; @@ -98,38 +82,6 @@ static const char *person_prefix = "/pidf:presence/dm:person"; * PRIVATE FUNCTIONS * ****************************************************************************/ -static xmlparsing_context_t * xmlparsing_context_new() { - xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)malloc(sizeof(xmlparsing_context_t)); - if (xmlCtx != NULL) { - xmlCtx->doc = NULL; - xmlCtx->xpath_ctx = NULL; - xmlCtx->errorBuffer[0] = '\0'; - xmlCtx->warningBuffer[0] = '\0'; - } - return xmlCtx; -} - -static void xmlparsing_context_destroy(xmlparsing_context_t *ctx) { - if (ctx->doc != NULL) { - xmlFreeDoc(ctx->doc); - ctx->doc = NULL; - } - if (ctx->xpath_ctx != NULL) { - xmlXPathFreeContext(ctx->xpath_ctx); - ctx->xpath_ctx = NULL; - } - free(ctx); -} - -static void xmlparsing_genericxml_error(void *ctx, const char *fmt, ...) { - xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)ctx; - int sl = strlen(xmlCtx->errorBuffer); - va_list args; - va_start(args, fmt); - vsnprintf(xmlCtx->errorBuffer + sl, XMLPARSING_BUFFER_LEN - sl, fmt, args); - va_end(args); -} - static char presence_id_valid_characters[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static char * generate_presence_id(void) { @@ -1183,45 +1135,6 @@ void * linphone_presence_note_get_user_data(LinphonePresenceNote *note) { * XML PRESENCE INTERNAL HANDLING * ****************************************************************************/ -static int create_xml_xpath_context(xmlparsing_context_t *xml_ctx) { - if (xml_ctx->xpath_ctx != NULL) { - xmlXPathFreeContext(xml_ctx->xpath_ctx); - } - xml_ctx->xpath_ctx = xmlXPathNewContext(xml_ctx->doc); - if (xml_ctx->xpath_ctx == NULL) return -1; - return 0; -} - -static char * get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { - xmlXPathObjectPtr xpath_obj; - xmlChar *text = NULL; - int i; - - xpath_obj = xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); - if (xpath_obj != NULL) { - if (xpath_obj->nodesetval != NULL) { - xmlNodeSetPtr nodes = xpath_obj->nodesetval; - for (i = 0; i < nodes->nodeNr; i++) { - xmlNodePtr node = nodes->nodeTab[i]; - if (node->children != NULL) { - text = xmlNodeListGetString(xml_ctx->doc, node->children, 1); - } - } - } - xmlXPathFreeObject(xpath_obj); - } - - return (char *)text; -} - -static void free_xml_text_content(const char *text) { - xmlFree((xmlChar *)text); -} - -static xmlXPathObjectPtr get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { - return xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); -} - static const char *service_prefix = "/pidf:presence/pidf:tuple"; static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx, LinphonePresenceService *service, unsigned int service_idx) { @@ -1233,19 +1146,19 @@ static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx int i; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note", service_prefix, service_idx); - note_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); + note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((note_object != NULL) && (note_object->nodesetval != NULL)) { for (i = 1; i <= note_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]", service_prefix, service_idx, i); - note_str = get_xml_text_content(xml_ctx, xpath_str); + note_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (note_str == NULL) continue; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]/@xml:lang", service_prefix, service_idx, i); - lang = get_xml_text_content(xml_ctx, xpath_str); + lang = linphone_get_xml_text_content(xml_ctx, xpath_str); note = linphone_presence_note_new(note_str, lang); presence_service_add_note(service, note); - if (lang != NULL) free_xml_text_content(lang); - free_xml_text_content(note_str); + if (lang != NULL) linphone_free_xml_text_content(lang); + linphone_free_xml_text_content(note_str); } } if (note_object != NULL) xmlXPathFreeObject(note_object); @@ -1264,11 +1177,11 @@ static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, Lin LinphonePresenceBasicStatus basic_status; int i; - service_object = get_xml_xpath_object_for_node_list(xml_ctx, service_prefix); + service_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, service_prefix); if ((service_object != NULL) && (service_object->nodesetval != NULL)) { for (i = 1; i <= service_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:status/pidf:basic", service_prefix, i); - basic_status_str = get_xml_text_content(xml_ctx, xpath_str); + basic_status_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (basic_status_str == NULL) continue; @@ -1278,33 +1191,33 @@ static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, Lin basic_status = LinphonePresenceBasicStatusClosed; } else { /* Invalid value for basic status. */ - free_xml_text_content(basic_status_str); + linphone_free_xml_text_content(basic_status_str); return -1; } snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:timestamp", service_prefix, i); - timestamp_str = get_xml_text_content(xml_ctx, xpath_str); + timestamp_str = linphone_get_xml_text_content(xml_ctx, xpath_str); snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:contact", service_prefix, i); - contact_str = get_xml_text_content(xml_ctx, xpath_str); + contact_str = linphone_get_xml_text_content(xml_ctx, xpath_str); snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/@id", service_prefix, i); - service_id_str = get_xml_text_content(xml_ctx, xpath_str); + service_id_str = linphone_get_xml_text_content(xml_ctx, xpath_str); service = presence_service_new(service_id_str, basic_status); if (service != NULL) { if (timestamp_str != NULL) { presence_service_set_timestamp(service, parse_timestamp(timestamp_str)); - free_xml_text_content(timestamp_str); + linphone_free_xml_text_content(timestamp_str); } if (contact_str != NULL) { linphone_presence_service_set_contact(service, contact_str); - free_xml_text_content(contact_str); + linphone_free_xml_text_content(contact_str); } process_pidf_xml_presence_service_notes(xml_ctx, service, i); linphone_presence_model_add_service(model, service); } - free_xml_text_content(basic_status_str); - if (service_id_str != NULL) free_xml_text_content(service_id_str); + linphone_free_xml_text_content(basic_status_str); + if (service_id_str != NULL) linphone_free_xml_text_content(service_id_str); } } if (service_object != NULL) xmlXPathFreeObject(service_object); @@ -1333,11 +1246,11 @@ static int process_pidf_xml_presence_person_activities(xmlparsing_context_t *xml int err = 0; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities", person_prefix, person_idx); - activities_nodes_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); + activities_nodes_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((activities_nodes_object != NULL) && (activities_nodes_object->nodesetval != NULL)) { for (i = 1; i <= activities_nodes_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities[%i]/rpid:*", person_prefix, person_idx, i); - activities_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); + activities_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((activities_object != NULL) && (activities_object->nodesetval != NULL)) { for (j = 0; j < activities_object->nodesetval->nodeNr; j++) { activity_node = activities_object->nodesetval->nodeTab[j]; @@ -1345,14 +1258,14 @@ static int process_pidf_xml_presence_person_activities(xmlparsing_context_t *xml LinphonePresenceActivityType acttype; description = (const char *)xmlNodeGetContent(activity_node); if ((description != NULL) && (description[0] == '\0')) { - free_xml_text_content(description); + linphone_free_xml_text_content(description); description = NULL; } err = activity_name_to_presence_activity_type((const char *)activity_node->name, &acttype); if (err < 0) break; activity = linphone_presence_activity_new(acttype, description); linphone_presence_person_add_activity(person, activity); - if (description != NULL) free_xml_text_content(description); + if (description != NULL) linphone_free_xml_text_content(description); } } } @@ -1374,37 +1287,37 @@ static int process_pidf_xml_presence_person_notes(xmlparsing_context_t *xml_ctx, int i; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note", person_prefix, person_idx); - note_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); + note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((note_object != NULL) && (note_object->nodesetval != NULL)) { for (i = 1; i <= note_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note[%i]", person_prefix, person_idx, i); - note_str = get_xml_text_content(xml_ctx, xpath_str); + note_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (note_str == NULL) continue; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note[%i]/@xml:lang", person_prefix, person_idx, i); - lang = get_xml_text_content(xml_ctx, xpath_str); + lang = linphone_get_xml_text_content(xml_ctx, xpath_str); note = linphone_presence_note_new(note_str, lang); presence_person_add_activities_note(person, note); - if (lang != NULL) free_xml_text_content(lang); - free_xml_text_content(note_str); + if (lang != NULL) linphone_free_xml_text_content(lang); + linphone_free_xml_text_content(note_str); } } if (note_object != NULL) xmlXPathFreeObject(note_object); snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note", person_prefix, person_idx); - note_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); + note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str); if ((note_object != NULL) && (note_object->nodesetval != NULL)) { for (i = 1; i <= note_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note[%i]", person_prefix, person_idx, i); - note_str = get_xml_text_content(xml_ctx, xpath_str); + note_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (note_str == NULL) continue; snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note[%i]/@xml:lang", person_prefix, person_idx, i); - lang = get_xml_text_content(xml_ctx, xpath_str); + lang = linphone_get_xml_text_content(xml_ctx, xpath_str); note = linphone_presence_note_new(note_str, lang); presence_person_add_note(person, note); - if (lang != NULL) free_xml_text_content(lang); - free_xml_text_content(note_str); + if (lang != NULL) linphone_free_xml_text_content(lang); + linphone_free_xml_text_content(note_str); } } if (note_object != NULL) xmlXPathFreeObject(note_object); @@ -1422,13 +1335,13 @@ static int process_pidf_xml_presence_persons(xmlparsing_context_t *xml_ctx, Linp int i; int err = 0; - person_object = get_xml_xpath_object_for_node_list(xml_ctx, person_prefix); + person_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, person_prefix); if ((person_object != NULL) && (person_object->nodesetval != NULL)) { for (i = 1; i <= person_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/@id", person_prefix, i); - person_id_str = get_xml_text_content(xml_ctx, xpath_str); + person_id_str = linphone_get_xml_text_content(xml_ctx, xpath_str); snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:timestamp", person_prefix, i); - person_timestamp_str = get_xml_text_content(xml_ctx, xpath_str); + person_timestamp_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (person_timestamp_str == NULL) timestamp = time(NULL); else @@ -1446,8 +1359,8 @@ static int process_pidf_xml_presence_persons(xmlparsing_context_t *xml_ctx, Linp break; } } - if (person_id_str != NULL) free_xml_text_content(person_id_str); - if (person_timestamp_str != NULL) free_xml_text_content(person_timestamp_str); + if (person_id_str != NULL) linphone_free_xml_text_content(person_id_str); + if (person_timestamp_str != NULL) linphone_free_xml_text_content(person_timestamp_str); } } if (person_object != NULL) xmlXPathFreeObject(person_object); @@ -1467,19 +1380,19 @@ static int process_pidf_xml_presence_notes(xmlparsing_context_t *xml_ctx, Linpho const char *lang; int i; - note_object = get_xml_xpath_object_for_node_list(xml_ctx, "/pidf:presence/pidf:note"); + note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/pidf:presence/pidf:note"); if ((note_object != NULL) && (note_object->nodesetval != NULL)) { for (i = 1; i <= note_object->nodesetval->nodeNr; i++) { snprintf(xpath_str, sizeof(xpath_str), "/pidf:presence/pidf:note[%i]", i); - note_str = get_xml_text_content(xml_ctx, xpath_str); + note_str = linphone_get_xml_text_content(xml_ctx, xpath_str); if (note_str == NULL) continue; snprintf(xpath_str, sizeof(xpath_str), "/pidf:presence/pidf:note[%i]/@xml:lang", i); - lang = get_xml_text_content(xml_ctx, xpath_str); + lang = linphone_get_xml_text_content(xml_ctx, xpath_str); note = linphone_presence_note_new(note_str, lang); presence_model_add_note(model, note); - if (lang != NULL) free_xml_text_content(lang); - free_xml_text_content(note_str); + if (lang != NULL) linphone_free_xml_text_content(lang); + linphone_free_xml_text_content(note_str); } } if (note_object != NULL) xmlXPathFreeObject(note_object); @@ -1491,7 +1404,7 @@ static LinphonePresenceModel * process_pidf_xml_presence_notification(xmlparsing LinphonePresenceModel *model = NULL; int err; - if (create_xml_xpath_context(xml_ctx) < 0) + if (linphone_create_xml_xpath_context(xml_ctx) < 0) return NULL; model = linphone_presence_model_new(); @@ -1606,15 +1519,15 @@ void linphone_notify_parse_presence(SalOp *op, const char *content_type, const c } if (strcmp(content_subtype, "pidf+xml") == 0) { - xml_ctx = xmlparsing_context_new(); - xmlSetGenericErrorFunc(xml_ctx, xmlparsing_genericxml_error); + xml_ctx = linphone_xmlparsing_context_new(); + xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { model = process_pidf_xml_presence_notification(xml_ctx); } else { ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer); } - xmlparsing_context_destroy(xml_ctx); + linphone_xmlparsing_context_destroy(xml_ctx); } else { ms_error("Unknown content type '%s/%s' for presence", content_type, content_subtype); } diff --git a/coreapi/private.h b/coreapi/private.h index 5225b6a3c..297535c34 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -34,6 +34,9 @@ extern "C" { #include "sal/sal.h" #include "sipsetup.h" +#include +#include + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -201,22 +204,22 @@ struct _LinphoneCall LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ LinphoneCall *transfer_target;/*if this call received a transfer request, then transfer_target points to the new call created to the refer target */ int localdesc_changed; - + bool_t refer_pending; bool_t media_pending; bool_t audio_muted; bool_t camera_enabled; - + bool_t all_muted; /*this flag is set during early medias*/ bool_t playing_ringbacktone; bool_t owns_call_log; bool_t ringing_beep; /* whether this call is ringing through an already existent current call*/ - + bool_t videostream_encrypted; bool_t audiostream_encrypted; bool_t auth_token_verified; bool_t defer_update; - + bool_t was_automatically_paused; bool_t ping_replied; bool_t record_active; @@ -320,6 +323,7 @@ void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call); void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session); void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md); +bool_t linphone_core_media_description_has_srtp(const SalMediaDescription *md); void linphone_core_send_initial_subscribes(LinphoneCore *lc); void linphone_core_write_friends_config(LinphoneCore* lc); @@ -338,6 +342,7 @@ void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,Linphon int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char *username, char *result, size_t result_len); void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *msg); +void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing); void linphone_core_play_tone(LinphoneCore *lc); @@ -379,9 +384,9 @@ LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer); static const int linphone_proxy_config_magic=0x7979; -/*chat*/ +/*chat*/ void linphone_chat_message_destroy(LinphoneChatMessage* msg); -/**/ +/**/ struct _LinphoneProxyConfig { @@ -426,16 +431,26 @@ struct _LinphoneAuthInfo bool_t works; }; +typedef enum _LinphoneIsComposingState { + LinphoneIsComposingIdle, + LinphoneIsComposingActive +} LinphoneIsComposingState; + struct _LinphoneChatRoom{ struct _LinphoneCore *lc; char *peer; LinphoneAddress *peer_url; void * user_data; MSList *messages_hist; + LinphoneIsComposingState remote_is_composing; + LinphoneIsComposingState is_composing; + belle_sip_source_t *remote_composing_refresh_timer; + belle_sip_source_t *composing_idle_timer; + belle_sip_source_t *composing_refresh_timer; }; - + struct _LinphoneFriend{ LinphoneAddress *uri; SalOp *insub; @@ -487,7 +502,7 @@ typedef struct rtp_config int nortp_timeout; int disable_upnp; bool_t rtp_no_xmit_on_audio_mute; - /* stop rtp xmit when audio muted */ + /* stop rtp xmit when audio muted */ bool_t audio_adaptive_jitt_comp_enabled; bool_t video_adaptive_jitt_comp_enabled; bool_t pad; @@ -633,12 +648,12 @@ struct _LinphoneCore bool_t apply_nat_settings; bool_t initial_subscribes_sent; bool_t bl_refresh; - + bool_t preview_finished; bool_t auto_net_state_mon; bool_t network_reachable; bool_t use_preview_window; - + time_t network_last_check; bool_t network_last_status; @@ -788,6 +803,46 @@ LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatu const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref); void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc); + +/***************************************************************************** + * XML UTILITY FUNCTIONS * + ****************************************************************************/ + +#include +#include +#include +#include + +#define XMLPARSING_BUFFER_LEN 2048 +#define MAX_XPATH_LENGTH 256 + +typedef struct _xmlparsing_context { + xmlDoc *doc; + xmlXPathContextPtr xpath_ctx; + char errorBuffer[XMLPARSING_BUFFER_LEN]; + char warningBuffer[XMLPARSING_BUFFER_LEN]; +} xmlparsing_context_t; + +xmlparsing_context_t * linphone_xmlparsing_context_new(void); +void linphone_xmlparsing_context_destroy(xmlparsing_context_t *ctx); +void linphone_xmlparsing_genericxml_error(void *ctx, const char *fmt, ...); +int linphone_create_xml_xpath_context(xmlparsing_context_t *xml_ctx); +char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression); +void linphone_free_xml_text_content(const char *text); +xmlXPathObjectPtr linphone_get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression); + + +/** Belle Sip-based objects need unique ids + */ + +BELLE_SIP_DECLARE_TYPES_BEGIN(linphone,10000) +BELLE_SIP_TYPE_ID(LinphoneContactSearch), +BELLE_SIP_TYPE_ID(LinphoneContactProvider), +BELLE_SIP_TYPE_ID(LinphoneLDAPContactProvider), +BELLE_SIP_TYPE_ID(LinphoneLDAPContactSearch) +BELLE_SIP_DECLARE_TYPES_END + + #ifdef __cplusplus } #endif diff --git a/coreapi/sal.c b/coreapi/sal.c index 25fa8dcd8..a5bcd89c2 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -530,7 +530,7 @@ const char* sal_reason_to_string(const SalReason reason) { case SalReasonTemporarilyUnavailable: return "SalReasonTemporarilyUnavailable"; case SalReasonNotFound: return "SalReasonNotFound"; case SalReasonDoNotDisturb: return "SalReasonDoNotDisturb"; - case SalReasonMedia: return "SalReasonMedia"; + case SalReasonUnsupportedContent: return "SalReasonUnsupportedContent"; case SalReasonForbidden: return "SalReasonForbidden"; case SalReasonUnknown: return "SalReasonUnknown"; case SalReasonServiceUnavailable: return "SalReasonServiceUnavailable"; diff --git a/coreapi/xml.c b/coreapi/xml.c new file mode 100644 index 000000000..b4b994174 --- /dev/null +++ b/coreapi/xml.c @@ -0,0 +1,98 @@ +/* +linphone +Copyright (C) 2010-2013 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include "private.h" + +#include +#include +#include +#include + + +xmlparsing_context_t * linphone_xmlparsing_context_new(void) { + xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)malloc(sizeof(xmlparsing_context_t)); + if (xmlCtx != NULL) { + xmlCtx->doc = NULL; + xmlCtx->xpath_ctx = NULL; + xmlCtx->errorBuffer[0] = '\0'; + xmlCtx->warningBuffer[0] = '\0'; + } + return xmlCtx; +} + +void linphone_xmlparsing_context_destroy(xmlparsing_context_t *ctx) { + if (ctx->doc != NULL) { + xmlFreeDoc(ctx->doc); + ctx->doc = NULL; + } + if (ctx->xpath_ctx != NULL) { + xmlXPathFreeContext(ctx->xpath_ctx); + ctx->xpath_ctx = NULL; + } + free(ctx); +} + +void linphone_xmlparsing_genericxml_error(void *ctx, const char *fmt, ...) { + xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)ctx; + int sl = strlen(xmlCtx->errorBuffer); + va_list args; + va_start(args, fmt); + vsnprintf(xmlCtx->errorBuffer + sl, XMLPARSING_BUFFER_LEN - sl, fmt, args); + va_end(args); +} + +int linphone_create_xml_xpath_context(xmlparsing_context_t *xml_ctx) { + if (xml_ctx->xpath_ctx != NULL) { + xmlXPathFreeContext(xml_ctx->xpath_ctx); + } + xml_ctx->xpath_ctx = xmlXPathNewContext(xml_ctx->doc); + if (xml_ctx->xpath_ctx == NULL) return -1; + return 0; +} + +char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { + xmlXPathObjectPtr xpath_obj; + xmlChar *text = NULL; + int i; + + xpath_obj = xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); + if (xpath_obj != NULL) { + if (xpath_obj->nodesetval != NULL) { + xmlNodeSetPtr nodes = xpath_obj->nodesetval; + for (i = 0; i < nodes->nodeNr; i++) { + xmlNodePtr node = nodes->nodeTab[i]; + if (node->children != NULL) { + text = xmlNodeListGetString(xml_ctx->doc, node->children, 1); + } + } + } + xmlXPathFreeObject(xpath_obj); + } + + return (char *)text; +} + +void linphone_free_xml_text_content(const char *text) { + xmlFree((xmlChar *)text); +} + +xmlXPathObjectPtr linphone_get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { + return xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); +} diff --git a/gtk/chat.c b/gtk/chat.c index b44605ae3..0c9aa400b 100644 --- a/gtk/chat.c +++ b/gtk/chat.c @@ -279,6 +279,16 @@ static void on_chat_state_changed(LinphoneChatMessage *msg, LinphoneChatMessageS update_chat_state_message(state,msg); } +void linphone_gtk_compose_text(void) { + GtkWidget *main_window=linphone_gtk_get_main_window(); + GtkWidget *friendlist=linphone_gtk_get_widget(main_window,"contact_list"); + GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(friendlist),"chatview"); + LinphoneChatRoom *cr=g_object_get_data(G_OBJECT(w),"cr"); + if (cr) { + linphone_chat_room_compose(cr); + } +} + void linphone_gtk_send_text(){ GtkWidget *main_window=linphone_gtk_get_main_window(); GtkWidget *friendlist=linphone_gtk_get_widget(main_window,"contact_list"); @@ -293,7 +303,11 @@ void linphone_gtk_send_text(){ linphone_chat_room_send_message2(cr,msg,on_chat_state_changed,NULL); linphone_gtk_push_text(w,linphone_chat_message_get_from(msg), TRUE,cr,msg,FALSE); + + // Disconnect and reconnect the "changed" signal to prevent triggering it when clearing the text entry. + g_signal_handlers_disconnect_by_func(G_OBJECT(entry),(GCallback)linphone_gtk_compose_text,NULL); gtk_entry_set_text(GTK_ENTRY(entry),""); + g_signal_connect_swapped(G_OBJECT(entry),"changed",(GCallback)linphone_gtk_compose_text,NULL); } } @@ -410,6 +424,7 @@ GtkWidget* linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const LinphoneAddres g_signal_connect_swapped(G_OBJECT(button),"clicked",(GCallback)linphone_gtk_send_text,NULL); entry = linphone_gtk_get_widget(chat_view,"text_entry"); g_signal_connect_swapped(G_OBJECT(entry),"activate",(GCallback)linphone_gtk_send_text,NULL); + g_signal_connect_swapped(G_OBJECT(entry),"changed",(GCallback)linphone_gtk_compose_text,NULL); g_signal_connect(G_OBJECT(notebook),"switch_page",(GCallback)linphone_gtk_notebook_tab_select,NULL); ms_free(with_str); return chat_view; @@ -417,7 +432,7 @@ GtkWidget* linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const LinphoneAddres LinphoneChatRoom * linphone_gtk_create_chatroom(const LinphoneAddress *with){ char *tmp=linphone_address_as_string(with); - LinphoneChatRoom *cr=linphone_core_create_chat_room(linphone_gtk_get_core(),tmp); + LinphoneChatRoom *cr=linphone_core_get_or_create_chat_room(linphone_gtk_get_core(),tmp); ms_free(tmp); return cr; } @@ -516,3 +531,7 @@ void linphone_gtk_text_received ( LinphoneCore *lc, LinphoneChatRoom *room, linphone_gtk_show_friends(); } + +void linphone_gtk_is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) { + linphone_gtk_friend_list_update_chat_picture(); +} diff --git a/gtk/friendlist.c b/gtk/friendlist.c index 523cf13b3..0316dd699 100644 --- a/gtk/friendlist.c +++ b/gtk/friendlist.c @@ -87,6 +87,18 @@ static GdkPixbuf *create_chat_picture(){ return pixbuf; } +static GdkPixbuf *create_composing_unread_msg(){ + GdkPixbuf *pixbuf; + pixbuf = create_pixbuf("composing_active_chat.png"); + return pixbuf; +} + +static GdkPixbuf *create_composing_chat_picture(){ + GdkPixbuf *pixbuf; + pixbuf = create_pixbuf("composing_chat.png"); + return pixbuf; +} + /* void linphone_gtk_set_friend_status(GtkWidget *friendlist , LinphoneFriend * fid, const gchar *url, const gchar *status, const gchar *img){ GtkTreeIter iter; @@ -227,15 +239,23 @@ void linphone_gtk_friend_list_update_chat_picture(){ GtkWidget *friendlist=linphone_gtk_get_widget(w,"contact_list"); GtkTreeModel *model=gtk_tree_view_get_model(GTK_TREE_VIEW(friendlist)); LinphoneChatRoom *cr=NULL; + bool_t is_composing; int nbmsg=0; if (gtk_tree_model_get_iter_first(model,&iter)) { do{ gtk_tree_model_get (model, &iter,FRIEND_CHATROOM , &cr, -1); nbmsg=linphone_chat_room_get_unread_messages_count(cr); + is_composing=linphone_chat_room_is_remote_composing(cr); if(nbmsg != 0){ - gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_unread_msg(),-1); + if (is_composing == TRUE) + gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_composing_unread_msg(),-1); + else + gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_unread_msg(),-1); } else { - gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_chat_picture(),-1); + if (is_composing == TRUE) + gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_composing_chat_picture(),-1); + else + gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_CHAT,create_chat_picture(),-1); } }while(gtk_tree_model_iter_next(model,&iter)); } diff --git a/gtk/ldap.ui b/gtk/ldap.ui index ba3bf7163..a5df241d1 100644 --- a/gtk/ldap.ui +++ b/gtk/ldap.ui @@ -1,6 +1,6 @@ - + False diff --git a/gtk/linphone.h b/gtk/linphone.h index 1d674f876..f9d918c51 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -116,6 +116,7 @@ void linphone_gtk_send_text(); GtkWidget * linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const LinphoneAddress *with); LinphoneChatRoom * linphone_gtk_create_chatroom(const LinphoneAddress *with); void linphone_gtk_text_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg); +void linphone_gtk_is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room); void linphone_gtk_friend_list_update_chat_picture(); void linphone_gtk_friend_list_set_chat_conversation(const LinphoneAddress *la); diff --git a/gtk/main.c b/gtk/main.c index 135dfe5f4..a7de9323b 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -266,6 +266,7 @@ static void linphone_gtk_init_liblinphone(const char *config_file, vtable.call_log_updated=linphone_gtk_call_log_updated; //vtable.text_received=linphone_gtk_text_received; vtable.message_received=linphone_gtk_text_received; + vtable.is_composing_received=linphone_gtk_is_composing_received; vtable.refer_received=linphone_gtk_refer_received; vtable.buddy_info_updated=linphone_gtk_buddy_info_updated; vtable.call_encryption_changed=linphone_gtk_call_encryption_changed; @@ -846,7 +847,7 @@ static gboolean launch_contact_provider_search(void *userdata) ); if(search) - belle_sip_object_ref(search); + linphone_contact_search_ref(search); } return FALSE; } diff --git a/include/sal/sal.h b/include/sal/sal.h index b76d70b05..7e2ac70b9 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/mscommon.h" #include "ortp/ortp_srtp.h" +#include "belle-sip/belle-sip.h" #ifndef LINPHONE_PUBLIC #define LINPHONE_PUBLIC MS2_PUBLIC @@ -224,6 +225,11 @@ typedef struct SalMessage{ time_t time; }SalMessage; +typedef struct SalIsComposing { + const char *from; + const char *text; +} SalIsComposing; + #define SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES 5 SalMediaDescription *sal_media_description_new(); @@ -241,11 +247,7 @@ typedef struct SalOpBase{ Sal *root; char *route; /*or request-uri for REGISTER*/ MSList* route_addresses; /*list of SalAddress* */ -#ifndef USE_BELLESIP - char *contact; -#else SalAddress* contact_address; -#endif char *from; SalAddress* from_address; char *to; @@ -279,7 +281,7 @@ typedef enum SalReason{ SalReasonTemporarilyUnavailable, SalReasonNotFound, SalReasonDoNotDisturb, - SalReasonMedia, + SalReasonUnsupportedContent, SalReasonForbidden, SalReasonUnknown, SalReasonServiceUnavailable, @@ -390,6 +392,7 @@ typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf); typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto); typedef void (*SalOnTextReceived)(SalOp *op, const SalMessage *msg); typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus status); +typedef void (*SalOnIsComposingReceived)(SalOp *op, const SalIsComposing *is_composing); typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state); typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, SalError error, SalReason reason); typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, const SalBody *body); @@ -425,6 +428,7 @@ typedef struct SalCallbacks{ SalOnRefer refer_received; SalOnTextReceived text_received; SalOnTextDeliveryUpdate text_delivery_update; + SalOnIsComposingReceived is_composing_received; SalOnNotifyRefer notify_refer; SalOnSubscribeReceived subscribe_received; SalOnSubscribeClosed subscribe_closed; @@ -690,6 +694,8 @@ LINPHONE_PUBLIC bool_t sal_dns_srv_enabled(const Sal *sal); LINPHONE_PUBLIC void sal_set_dns_user_hosts_file(Sal *sal, const char *hosts_file); LINPHONE_PUBLIC const char *sal_get_dns_user_hosts_file(const Sal *sal); unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size); +belle_sip_source_t * sal_create_timer(Sal *sal, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms, const char* timer_name); +void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer); int sal_body_has_type(const SalBody *body, const char *type, const char *subtype); /*this function parses a document with key=value pairs separated by new lines, and extracts the value for a given key*/ diff --git a/java/common/org/linphone/core/LinphoneChatRoom.java b/java/common/org/linphone/core/LinphoneChatRoom.java index 5c87f5647..e84538269 100644 --- a/java/common/org/linphone/core/LinphoneChatRoom.java +++ b/java/common/org/linphone/core/LinphoneChatRoom.java @@ -80,6 +80,17 @@ public interface LinphoneChatRoom { * Deletes all the messages associated with the peer of this chat room */ void deleteHistory(); + + /** + * Notify the destination of the chat message being composed that the user is typing a new message. + */ + void compose(); + + /** + * Tells whether the remote is currently composing a message. + * @return true if the remote is currently composing a message, false otherwise. + */ + boolean isRemoteComposing(); /** * Marks all the messages in this conversation as read diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index 60e9e3d59..d01fa01a7 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -91,6 +91,13 @@ public interface LinphoneCoreListener { */ void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); + /** + * invoked when a composing notification is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in the conversation. + */ + void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); + /** * invoked when a new dtmf is received * @param lc LinphoneCore diff --git a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java index 7f4d684ee..c2021e379 100644 --- a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java +++ b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java @@ -31,6 +31,8 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { private native void destroy(long ptr); private native int getUnreadMessagesCount(long ptr); private native void deleteHistory(long ptr); + private native void compose(long ptr); + private native boolean isRemoteComposing(long ptr); private native void markAsRead(long ptr); private native void deleteMessage(long room, long message); private native void updateUrl(long room, long message); @@ -87,6 +89,14 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { public void deleteHistory() { deleteHistory(nativePtr); } + + public void compose() { + compose(nativePtr); + } + + public boolean isRemoteComposing() { + return isRemoteComposing(nativePtr); + } public void markAsRead() { markAsRead(nativePtr); diff --git a/linphone.spec.in b/linphone.spec.in index e8e7b68a6..728ad5151 100644 --- a/linphone.spec.in +++ b/linphone.spec.in @@ -53,7 +53,7 @@ Libraries and headers required to develop software with linphone. %if !%{video} --disable-video \ %endif - --disable-tests --docdir=%{_docdir} --enable-ipv6 --enable-static --enable-external-mediastreamer --enable-external-ortp + --disable-tests --docdir=%{_docdir} --enable-ipv6 --enable-static --enable-external-mediastreamer --enable-external-ortp --enable-ldap %__make %{?_smp_mflags} diff --git a/mediastreamer2 b/mediastreamer2 index 4103f0b1d..ce1127bbf 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4103f0b1d7757558d06e6e32b657308bb2556c9b +Subproject commit ce1127bbf74a1bc1113a048195db3cf2ed49c946 diff --git a/oRTP b/oRTP index 37b81a118..d42419299 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 37b81a118760caa22a5a89d56945acea9aa52523 +Subproject commit d4241929983c8f0114f8de04dca6ecbc42b9607e diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am index 2386d68cf..5e9b7a177 100644 --- a/pixmaps/Makefile.am +++ b/pixmaps/Makefile.am @@ -11,7 +11,7 @@ status-orange.png \ status-red.png \ status-offline.png \ call.png \ -chat.png active_chat.png\ +chat.png active_chat.png composing_chat.png composing_active_chat.png\ chat_message_inprogress.png chat_message_delivered.png chat_message_not_delivered.png\ contact-orange.png dialer-orange.png history-orange.png\ startcall-green.png startcall-small.png stopcall-red.png stopcall-small.png addcall-green.png linphone.icns \ diff --git a/pixmaps/active_chat.png b/pixmaps/active_chat.png index d82b7c595..ef3cedd1d 100644 Binary files a/pixmaps/active_chat.png and b/pixmaps/active_chat.png differ diff --git a/pixmaps/composing_active_chat.png b/pixmaps/composing_active_chat.png new file mode 100644 index 000000000..77da9e37f Binary files /dev/null and b/pixmaps/composing_active_chat.png differ diff --git a/pixmaps/composing_chat.png b/pixmaps/composing_chat.png new file mode 100644 index 000000000..2d329ed21 Binary files /dev/null and b/pixmaps/composing_chat.png differ diff --git a/po/POTFILES.in b/po/POTFILES.in index 8f1c5d3a6..96893ed99 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -27,6 +27,7 @@ gtk/loginframe.c [type: gettext/glade]gtk/call_statistics.ui [type: gettext/glade]gtk/tunnel_config.ui [type: gettext/glade]gtk/keypad.ui +[type: gettext/glade]gtk/ldap.ui coreapi/linphonecore.c coreapi/misc.c coreapi/presence.c diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt new file mode 100644 index 000000000..8d15a6f5c --- /dev/null +++ b/share/CMakeLists.txt @@ -0,0 +1,17 @@ +install(FILES archived-rootca.pem + RENAME rootca.pem + COMPONENT COMP_liblinphone + DESTINATION share/linphone + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +install(FILES ringback.wav + COMPONENT COMP_liblinphone + DESTINATION share/sounds/linphone + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +add_subdirectory(rings) + +install(FILES ../mediastreamer2/src/voip/nowebcamCIF.jpg + COMPONENT COMP_liblinphone + DESTINATION share/images + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/share/rings/CMakeLists.txt b/share/rings/CMakeLists.txt new file mode 100644 index 000000000..cf359bb4e --- /dev/null +++ b/share/rings/CMakeLists.txt @@ -0,0 +1,4 @@ +install(FILES oldphone.wav toy-mono.wav + COMPONENT COMP_liblinphone + DESTINATION share/sounds/linphone/rings + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/tester/Makefile.am b/tester/Makefile.am index 4388ac483..ba8432bfa 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -23,11 +23,11 @@ liblinphone_tester_SOURCES= liblinphone_tester.c liblinphone_tester.h\ AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/coreapi -LDADD=$(top_builddir)/coreapi/liblinphone.la $(BELLESIP_LIBS) +LDADD=$(top_builddir)/coreapi/liblinphone.la $(BELLESIP_LIBS) $(LIBXML2_LIBS) AM_LDFLAGS=$(CUNIT_LIBS) -AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) +AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS) test: liblinphone_tester ./liblinphone_tester --config $(abs_srcdir) diff --git a/tester/call_tester.c b/tester/call_tester.c index 615886606..f65a4028d 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -331,9 +331,9 @@ static void call_failed_because_of_codecs(void) { linphone_call_ref(out_call); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1)); - /*flexisip will retain the 415 until the "urgent reply" timeout arrives.*/ + /*flexisip will retain the 488 until the "urgent reply" timeout arrives.*/ CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,6000)); - CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonMedia); + CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonNotAcceptable); CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0); linphone_call_unref(out_call); @@ -1387,6 +1387,41 @@ static void call_established_with_rejected_reinvite(void) { linphone_core_manager_destroy(pauline); } +static void call_established_with_rejected_incoming_reinvite(void) { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + + CU_ASSERT_TRUE(call(pauline,marie)); + + /*wait for ACK to be transmitted before going to reINVITE*/ + wait_for_until(marie->lc,pauline->lc,NULL,0,1000); + + linphone_core_enable_payload_type(pauline->lc,linphone_core_find_payload_type(pauline->lc,"PCMU",8000,1),FALSE); /*disable PCMU*/ + linphone_core_enable_payload_type(pauline->lc,linphone_core_find_payload_type(pauline->lc,"PCMA",8000,1),TRUE); /*enable PCMA*/ + + linphone_core_update_call(marie->lc + ,linphone_core_get_current_call(marie->lc) + ,linphone_call_get_current_params(linphone_core_get_current_call(marie->lc))); + + + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallUpdating,1)); + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + + CU_ASSERT_EQUAL(linphone_call_get_reason(linphone_core_get_current_call(marie->lc)),LinphoneReasonNotAcceptable); + + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallStreamsRunning,1); + check_call_state(pauline,LinphoneCallStreamsRunning); + check_call_state(marie,LinphoneCallStreamsRunning); + + /*just to sleep*/ + linphone_core_terminate_all_calls(pauline->lc); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + static void call_established_with_rejected_reinvite_with_error(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); @@ -1399,8 +1434,8 @@ static void call_established_with_rejected_reinvite_with_error(void) { sal_enable_unconditional_answer(marie->lc->sal,TRUE); linphone_core_update_call( pauline->lc - ,linphone_core_get_current_call(pauline->lc) - ,linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))); + ,linphone_core_get_current_call(pauline->lc) + ,linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))); CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); @@ -1460,6 +1495,7 @@ test_t call_tests[] = { { "Call with custom headers",call_with_custom_headers}, { "Call established with rejected INFO",call_established_with_rejected_info}, { "Call established with rejected RE-INVITE",call_established_with_rejected_reinvite}, + { "Call established with rejected incoming RE-INVITE", call_established_with_rejected_incoming_reinvite }, { "Call established with rejected RE-INVITE in error", call_established_with_rejected_reinvite_with_error} }; diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 910cb43b3..dbbba9a46 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -136,9 +136,11 @@ bool_t wait_for_until(LinphoneCore* lc_1, LinphoneCore* lc_2,int* counter,int va ms_list_free(lcs); return result; } + bool_t wait_for(LinphoneCore* lc_1, LinphoneCore* lc_2,int* counter,int value) { return wait_for_until(lc_1, lc_2,counter,value,3000); } + bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) { int retry=0; MSList* iterator; @@ -189,6 +191,7 @@ LinphoneCoreManager* linphone_core_manager_new2(const char* rc_file, int check_f mgr->v_table.call_state_changed=call_state_changed; mgr->v_table.text_received=text_message_received; mgr->v_table.message_received=message_received; + mgr->v_table.is_composing_received=is_composing_received; mgr->v_table.new_subscription_requested=new_subscription_requested; mgr->v_table.notify_presence_received=notify_presence_received; mgr->v_table.transfer_state_changed=linphone_transfer_state_changed; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2a8e33af0..93bde611a 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -117,7 +117,8 @@ typedef struct _stats { int number_of_LinphoneMessageInProgress; int number_of_LinphoneMessageDelivered; int number_of_LinphoneMessageNotDelivered; - + int number_of_LinphoneIsComposingActiveReceived; + int number_of_LinphoneIsComposingIdleReceived; int number_of_IframeDecoded; @@ -194,6 +195,7 @@ void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, void notify_presence_received(LinphoneCore *lc, LinphoneFriend * lf); void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from_address, const char *message); void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage* message); +void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room); void info_message_received(LinphoneCore *lc, LinphoneCall *call, const LinphoneInfoMessage *msg); void new_subscription_requested(LinphoneCore *lc, LinphoneFriend *lf, const char *url); void auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain); diff --git a/tester/marie_rc b/tester/marie_rc index d5b7d7c87..a8365e91e 100644 --- a/tester/marie_rc +++ b/tester/marie_rc @@ -5,6 +5,7 @@ sip_tls_port=5083 default_proxy=0 ping_with_options=0 register_only_when_network_is_up=0 +composing_idle_timeout=1 [auth_info_0] username=marie diff --git a/tester/message_tester.c b/tester/message_tester.c index c3001c226..ef0901c93 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -44,6 +44,15 @@ void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMess } } +void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) { + stats *counters = get_stats(lc); + if (room->remote_is_composing == LinphoneIsComposingActive) { + counters->number_of_LinphoneIsComposingActiveReceived++; + } else { + counters->number_of_LinphoneIsComposingIdleReceived++; + } +} + void linphone_chat_message_state_change(LinphoneChatMessage* msg,LinphoneChatMessageState state,void* ud) { LinphoneCore* lc=(LinphoneCore*)ud; stats* counters = get_stats(lc); @@ -264,6 +273,24 @@ static void info_message_with_body(){ info_message_with_args(TRUE); } +static void is_composing_notification(void) { + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + char* to = linphone_address_as_string(marie->identity); + LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc, to); + int dummy = 0; + + ms_free(to); + linphone_chat_room_compose(chat_room); + wait_for_until(pauline->lc, marie->lc, &dummy, 1, 1500); /*just to sleep while iterating*/ + linphone_chat_room_send_message(chat_room, "Composing a message"); + CU_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneIsComposingActiveReceived, 1)); + CU_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneIsComposingIdleReceived, 2)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + test_t message_tests[] = { { "Text message", text_message }, { "Text message with privacy", text_message_with_privacy }, @@ -272,7 +299,8 @@ test_t message_tests[] = { { "Text message with send error", text_message_with_send_error }, { "Text message with external body", text_message_with_external_body }, { "Info message", info_message }, - { "Info message with body", info_message_with_body } + { "Info message with body", info_message_with_body }, + { "IsComposing notification", is_composing_notification } }; test_suite_t message_test_suite = { diff --git a/tester/pauline_rc b/tester/pauline_rc index 204486f66..4d01058fe 100644 --- a/tester/pauline_rc +++ b/tester/pauline_rc @@ -5,6 +5,7 @@ sip_tls_port=5073 default_proxy=0 ping_with_options=0 register_only_when_network_is_up=0 +composing_idle_timeout=1 [auth_info_0] username=pauline