diff --git a/build/android/Android.mk b/build/android/Android.mk index c0e8df8a2..6b9dc85c1 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -70,7 +70,7 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../../externals/exosip/include \ $(LOCAL_PATH)/../../externals/osip/include -LOCAL_LDLIBS += -llog +LOCAL_LDLIBS += -llog -ldl LOCAL_STATIC_LIBRARIES := \ libmediastreamer2 \ diff --git a/configure.ac b/configure.ac index 5f803f662..93f4f1b00 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([linphone],[3.3.99.7],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.3.99.9],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM AC_CONFIG_SRCDIR([coreapi/linphonecore.c]) diff --git a/console/commands.c b/console/commands.c index d22d4b818..a7aa22f76 100644 --- a/console/commands.c +++ b/console/commands.c @@ -170,6 +170,12 @@ static LPC_COMMAND commands[] = { { "resume", lpc_cmd_resume, "resume a call", "'resume' : resume the unique call\n" "'resume ' : hold off the call with given id\n"}, + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri\n" + "'transfer ': transfers the call with 'id' to the destination sip-uri\n" + "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" + }, { "mute", lpc_cmd_mute_mic, "Mute microphone and suspend voice transmission."}, #ifdef VIDEO_ENABLED @@ -209,11 +215,6 @@ static LPC_COMMAND commands[] = { "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, - { "transfer", lpc_cmd_transfer, - "Transfer a call to a specified destination.", - "'transfer ' : transfers the current active call to the destination sip-uri" - "'transfer ': transfers the call with 'id' to the destination sip-uri" - }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" "'nat ' : set nat address.\n" @@ -637,10 +638,12 @@ lpc_cmd_transfer(LinphoneCore *lc, char *args) { if (args){ LinphoneCall *call; + LinphoneCall *call2; const char *refer_to=NULL; char arg1[256]={0}; char arg2[266]={0}; - int n=sscanf(args,"%s %s",arg1,arg2); + long id2=0; + int n=sscanf(args,"%s %s %li",arg1,arg2,&id2); if (n==1 || isalpha(*arg1)){ call=linphone_core_get_current_call(lc); if (call==NULL && ms_list_size(linphone_core_get_calls(lc))==1){ @@ -651,13 +654,24 @@ lpc_cmd_transfer(LinphoneCore *lc, char *args) linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); return 0; } - }else{ + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==2){ long id=atoi(arg1); refer_to=args+strlen(arg1)+1; call=linphonec_get_call(id); if (call==NULL) return 0; - } - linphone_core_transfer_call(lc, call, refer_to); + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==3){ + long id=atoi(arg1); + call=linphonec_get_call(id); + call2=linphonec_get_call(id2); + if (call==NULL || call2==NULL) return 0; + if (strcmp(arg2,"--to-call")!=0){ + return 0; + } + linphonec_out("Performing attended transfer of call %i to call %i",id,id2); + linphone_core_transfer_call_to_another (lc,call,call2); + }else return 0; }else{ linphonec_out("Transfer command requires at least one argument\n"); return 0; diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index add616d85..d4f5193f3 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -110,7 +110,6 @@ static void call_received(SalOp *h){ linphone_address_clean(from_parsed); tmp=linphone_address_as_string(from_parsed); linphone_address_destroy(from_parsed); - linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_(".")); if (lc->vtable.show) lc->vtable.show(lc); @@ -146,6 +145,11 @@ static void call_received(SalOp *h){ #endif ms_free(barmesg); ms_free(tmp); + + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ + linphone_core_accept_call(lc,call); + } } static void call_ringing(SalOp *h){ @@ -343,7 +347,6 @@ static void call_updating(SalOp *op){ linphone_call_set_state (call,prevstate,"Connected (streams running)"); } } - if (lc->current_call==NULL) linphone_core_start_pending_refered_calls (lc); } static void call_terminated(SalOp *op, const char *from){ @@ -445,8 +448,10 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de } linphone_call_stop_media_streams (call); if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg); - else linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); - + else{ + call->reason=LinphoneReasonDeclined; + linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); + } } static void auth_requested(SalOp *h, const char *realm, const char *username){ @@ -486,7 +491,7 @@ static void register_success(SalOp *op, bool_t registered){ char *msg; cfg->registered=registered; - linphone_proxy_config_set_error(cfg,LinphoneErrorNone); + linphone_proxy_config_set_error(cfg,LinphoneReasonNone); linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared , registered ? "Registration sucessful" : "Unregistration done"); if (lc->vtable.display_status){ @@ -515,9 +520,9 @@ static void register_failure(SalOp *op, SalError error, SalReason reason, const ms_free(msg); } if (error== SalErrorFailure && reason == SalReasonForbidden) { - linphone_proxy_config_set_error(cfg, LinphoneErrorBadCredentials); + linphone_proxy_config_set_error(cfg, LinphoneReasonBadCredentials); } else if (error == SalErrorNoResponse) { - linphone_proxy_config_set_error(cfg, LinphoneErrorNoResponse); + linphone_proxy_config_set_error(cfg, LinphoneReasonNoResponse); } linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details); } @@ -556,11 +561,15 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ lc->vtable.display_status(lc,msg); ms_free(msg); } - if (lc->current_call==NULL) linphone_core_start_pending_refered_calls (lc); - sal_refer_accept(op); + if (call->state!=LinphoneCallPaused){ + ms_message("Automatically pausing current call to accept transfer."); + linphone_core_pause_call(lc,call); + } + linphone_core_start_refered_call(lc,call); + sal_call_accept_refer(op); }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - sal_refer_accept(op); + sal_call_accept_refer(op); } } diff --git a/coreapi/help/buddy_status.c b/coreapi/help/buddy_status.c index 7ee06a8f5..1032cf17b 100644 --- a/coreapi/help/buddy_status.c +++ b/coreapi/help/buddy_status.c @@ -46,13 +46,13 @@ static void stop(int signum){ /** * presence state change notification callback */ -static void notify_presence_recv_updated (struct _LinphoneCore *lc, LinphoneFriend *friend) { +static void notify_presence_recv_updated (LinphoneCore *lc, LinphoneFriend *friend) { const LinphoneAddress* friend_address = linphone_friend_get_address(friend); printf("New state state [%s] for user id [%s] \n" ,linphone_online_status_to_string(linphone_friend_get_status(friend)) ,linphone_address_as_string (friend_address)); } -static void new_subscription_request (struct _LinphoneCore *lc, LinphoneFriend *friend, const char* url) { +static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *friend, const char* url) { const LinphoneAddress* friend_address = linphone_friend_get_address(friend); printf(" [%s] wants to see your status, accepting\n" ,linphone_address_as_string (friend_address)); diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java new file mode 100644 index 000000000..25dae37d7 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -0,0 +1,194 @@ +/* +TutorialBuddyStatus +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.OnlineStatus; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; +import org.linphone.core.LinphoneFriend.SubscribePolicy; + +/** + * + * This program is a _very_ simple usage example of liblinphone, + * demonstrating how to initiate SIP subscriptions and receive notifications + * from a sip uri identity passed from the command line. + *
Argument must be like sip:jehan@sip.linphone.org . + * ex budy_list sip:jehan@sip.linphone.org + *
Subscription is cleared on SIGINT + * + * Ported from buddy_status.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialBuddyStatus implements LinphoneCoreListener { + + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialBuddyStatus(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialBuddyStatus() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) { + write("["+lf.getAddress().getUserName()+"] wants to see your status, accepting"); + lf.edit(); // start editing friend + lf.setIncSubscribePolicy(SubscribePolicy.SPAccept); // accept incoming subscription request for this friend + lf.done(); // commit change + try { + // add this new friend to the buddy list + lc.addFriend(lf); + } catch (LinphoneCoreException e) { + write("Error while adding friend [" + lf.getAddress().getUserName() + "] to linphone in the callback"); + } + } + + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { + write("New state [" + lf.getStatus() +"] for user id ["+lf.getAddress().getUserName()+"]"); + } + + + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + + + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialBuddyStatus tutorial = new TutorialBuddyStatus(); + try { + // takes sip uri identity from the command line arguments + String userSipAddress = args[1]; + tutorial.launchTutorial(userSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String sipAddress) throws LinphoneCoreException { + final LinphoneCoreFactory lcFactory = LinphoneCoreFactory.instance(); + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = lcFactory.createLinphoneCore(this); + + + try { + + // Create friend object from string address + LinphoneFriend lf = lcFactory.createLinphoneFriend(sipAddress); + if (lf == null) { + write("Could not create friend; weird SIP address?"); + return; + } + + // configure this friend to emit SUBSCRIBE message after being added to LinphoneCore + lf.enableSubscribes(true); + + // accept incoming subscription request for this friend + lf.setIncSubscribePolicy(SubscribePolicy.SPAccept); + try { + // add my friend to the buddy list, initiate SUBSCRIBE message + lc.addFriend(lf); + } catch (LinphoneCoreException e) { + write("Error while adding friend " + lf.getAddress().getUserName() + " to linphone"); + return; + } + + // set my status to online + lc.setPresenceInfo(0, null, OnlineStatus.Online); + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); // first iterate initiates subscription + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + // change my presence status to offline + lc.setPresenceInfo(0, null, OnlineStatus.Offline); + // just to make sure new status is initiate message is issued + lc.iterate(); + + + lf.edit(); // start editing friend + lf.enableSubscribes(false); // disable subscription for this friend + lf.done(); // commit changes triggering an UNSUBSCRIBE message + lc.iterate(); // just to make sure unsubscribe message is issued + + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java new file mode 100644 index 000000000..8b8fd341b --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -0,0 +1,147 @@ +/* +TutorialChatRoom.java +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * It demonstrates how to send/receive SIP MESSAGE from a sip uri identity + * passed from the command line. + * + * Argument must be like sip:jehan@sip.linphone.org . + * + * ex chatroom sip:jehan@sip.linphone.org + * just takes a sip-uri as first argument and attempts to call it. + * + * Ported from chatroom.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialChatRoom implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialChatRoom(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialChatRoom() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} + public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){} + + + + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) { + write("Message ["+message+"] received from ["+from.asString()+"]"); + } + + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + // Takes the sip uri identity from the command line arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialChatRoom tutorial = new TutorialChatRoom(); + try { + String destinationSipAddress = args[1]; + tutorial.launchTutorial(destinationSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String destinationSipAddress) throws LinphoneCoreException { + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = LinphoneCoreFactory.instance().createLinphoneCore(this); + + try { + // Next step is to create a chat room + LinphoneChatRoom chatRoom = lc.createChatRoom(destinationSipAddress); + + // Send message + chatRoom.sendMessage("Hello world"); + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java new file mode 100644 index 000000000..12451c940 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -0,0 +1,156 @@ +/* +TutorialHelloWorld.java +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * It just takes a sip-uri as first argument and attempts to call it. + * + * Ported from helloworld.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialHelloWorld implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialHelloWorld(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialHelloWorld() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + + /* + * Call state notification listener + */ + public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){ + write("State: " + msg); + + if (State.CallEnd.equals(cstate)) + running = false; + } + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialHelloWorld helloWorld = new TutorialHelloWorld(); + try { + String destinationSipAddress = args[1]; + helloWorld.launchTutorial(destinationSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String destinationSipAddress) throws LinphoneCoreException { + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = LinphoneCoreFactory.instance().createLinphoneCore(this); + + + + try { + // Send the INVITE message to destination SIP address + LinphoneCall call = lc.invite(destinationSipAddress); + if (call == null) { + write("Could not place call to " + destinationSipAddress); + write("Aborting"); + return; + } + write("Call to " + destinationSipAddress + " is in progress..."); + + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + + if (!State.CallEnd.equals(call.getState())) { + write("Terminating the call"); + lc.terminateCall(call); + } + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java new file mode 100644 index 000000000..62a540267 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java @@ -0,0 +1,33 @@ +/* +TutorialNotifier.java +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package org.linphone.core.tutorials; + +/** + * Notify to the standard output. + * Subclass to define another text output. + * + * @author Guillaume Beraudo + * + */ +public class TutorialNotifier { + + public void notify(String s) { + System.out.println(s); + } +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java new file mode 100644 index 000000000..08e3c4ea3 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -0,0 +1,170 @@ +/* +TutorialRegistration.java +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * Demonstrating how to initiate a SIP registration from a sip uri identity + * passed from the command line. + * + * First argument must be like sip:jehan@sip.linphone.org, second must be password. + *
+ * ex registration sip:jehan@sip.linphone.org secret + * + * Ported from registration.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialRegistration implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialRegistration(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialRegistration() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + /* + * Registration state notification listener + */ + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) { + write(cfg.getIdentity() + " : "+smessage+"\n"); + + if (RegistrationState.RegistrationOk.equals(cstate)) + running = false; + } + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 2) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialRegistration tutorial = new TutorialRegistration(); + try { + // takes sip uri identity from the command line arguments + String userSipAddress = args[1]; + // takes password from the command line arguments + String userSipPassword = args[2]; + tutorial.launchTutorial(userSipAddress, userSipPassword); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String sipAddress, String password) throws LinphoneCoreException { + final LinphoneCoreFactory lcFactory = LinphoneCoreFactory.instance(); + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = lcFactory.createLinphoneCore(this); + + + try { + + // Parse identity + LinphoneAddress address = lcFactory.createLinphoneAddress(sipAddress); + String username = address.getUserName(); + String domain = address.getDomain(); + + + if (password != null) { + // create authentication structure from identity and add to linphone + lc.addAuthInfo(lcFactory.createAuthInfo(username, password, null)); + } + + // create proxy config + LinphoneProxyConfig proxyCfg = lcFactory.createProxyConfig(sipAddress, domain, null, true); + lc.addProxyConfig(proxyCfg); // add it to linphone + lc.setDefaultProxyConfig(proxyCfg); + + + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); // first iterate initiates registration + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + // Automatic unregistration on exit + + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8139bbdb2..ee2910e35 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -161,6 +161,9 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) linphone_core_run_stun_tests(call->core,call); discover_mtu(lc,linphone_address_get_domain (to)); + if (params->referer){ + sal_call_set_referer (call->op,params->referer->op); + } return call; } @@ -213,7 +216,10 @@ static void linphone_call_set_terminated(LinphoneCall *call){ linphone_core_update_allocated_audio_bandwidth(lc); if (call->state==LinphoneCallEnd){ - status=LinphoneCallSuccess; + if (call->reason==LinphoneReasonDeclined){ + status=LinphoneCallDeclined; + } + else status=LinphoneCallSuccess; } linphone_call_log_completed(call->log,call, status); @@ -221,7 +227,6 @@ static void linphone_call_set_terminated(LinphoneCall *call){ if (call == lc->current_call){ ms_message("Resetting the current call"); lc->current_call=NULL; - linphone_core_start_pending_refered_calls(lc); } if (linphone_core_del_call(lc,call) != 0){ @@ -382,6 +387,13 @@ LinphoneCallState linphone_call_get_state(const LinphoneCall *call){ return call->state; } +/** + * Returns the reason for a call termination (either error or normal termination) +**/ +LinphoneReason linphone_call_get_reason(const LinphoneCall *call){ + return call->reason; +} + /** * Get the user_pointer in the LinphoneCall * @@ -795,7 +807,7 @@ static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_e recfile, playcard, captcard, - send_early_media ? FALSE : linphone_core_echo_cancellation_enabled(lc)); + captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc)); post_configure_audio_streams(call); if (send_early_media) setup_ring_player(lc,call); audio_stream_set_rtcp_information(call->audiostream, cname, tool); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b9e7fe518..6ceb9b127 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -946,6 +946,14 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta linphone_core_assign_payload_type(&payload_type_ilbc,113,"mode=30"); linphone_core_assign_payload_type(&payload_type_amr,114,"octet-align=1"); +#if defined(ANDROID) || defined (__IPHONE_OS_VERSION_MIN_REQUIRED) + /*shorten the DNS lookup time and send more retransmissions on mobiles: + - to workaround potential packet losses + - to avoid hanging for 30 seconds when the network doesn't work despite the phone thinks it does. + */ + _linphone_core_configure_resolver(); +#endif + #ifdef ENABLE_NONSTANDARD_GSM { PayloadType *pt; @@ -1839,16 +1847,14 @@ const char * linphone_core_get_route(LinphoneCore *lc){ return route; } -void linphone_core_start_pending_refered_calls(LinphoneCore *lc){ - MSList *elem; - for(elem=lc->calls;elem!=NULL;elem=elem->next){ - LinphoneCall *call=(LinphoneCall*)elem->data; - if (call->refer_pending){ - ms_message("Starting new call to refered address %s",call->refer_to); - call->refer_pending=FALSE; - linphone_core_invite(lc,call->refer_to); - break; - } +void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ + if (call->refer_pending){ + LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + cp->referer=call; + ms_message("Starting new call to refered address %s",call->refer_to); + call->refer_pending=FALSE; + linphone_core_invite_with_params(lc,call->refer_to,cp); + linphone_call_params_destroy(cp); } } @@ -2143,12 +2149,28 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char } //lc->call=NULL; //Do not do that you will lose the call afterward . . . real_url=linphone_address_as_string (real_parsed_url); - sal_refer(call->op,real_url); + sal_call_refer(call->op,real_url); ms_free(real_url); linphone_address_destroy(real_parsed_url); return 0; } +/** + * Transfer a call to destination of another running call. This is used for "attended transfer" scenarios. + * @param lc linphone core object + * @param call a running call you want to transfer + * @param dest a running call whose remote person will receive the transfer + * + * The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately. + * The destination call is a call previously established to introduce the transfered person. + * This method will send a transfer request to the transfered person. The phone of the transfered is then + * expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically + * close the call with us (the 'dest' call). +**/ +int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){ + return sal_call_refer_with_replaces (call->op,dest->op); +} + bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ LinphoneCall *call = linphone_core_get_current_call(lc); if(call != NULL) @@ -2198,6 +2220,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) { LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; + SalOp *replaced; if (call==NULL){ //if just one call is present answer the only one ... @@ -2207,16 +2230,27 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) call = (LinphoneCall*)linphone_core_get_calls(lc)->data; } + if (call->state==LinphoneCallConnected){ + /*call already accepted*/ + return -1; + } + + /* check if this call is supposed to replace an already running one*/ + replaced=sal_call_get_replaces(call->op); + if (replaced){ + LinphoneCall *rc=(LinphoneCall*)sal_op_get_user_pointer (replaced); + if (rc){ + ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.", + call,rc); + linphone_core_terminate_call (lc,rc); + } + } + if (lc->current_call!=NULL && lc->current_call!=call){ ms_warning("Cannot accept this call, there is already one running."); return -1; } - if (call->state==LinphoneCallConnected){ - /*call already accepted*/ - return -1; - } - /*can accept a new call only if others are on hold */ { MSList *elem; @@ -2306,7 +2340,9 @@ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) call = the_call; } sal_call_terminate(call->op); - + if (call->state==LinphoneCallIncomingReceived){ + call->reason=LinphoneReasonDeclined; + } /*stop ringing*/ if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); @@ -2394,7 +2430,6 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *the_call) lc->current_call=NULL; if (call->audiostream || call->videostream) linphone_call_stop_media_streams (call); - linphone_core_start_pending_refered_calls(lc); return 0; } @@ -3298,7 +3333,13 @@ unsigned long linphone_core_get_native_video_window_id(const LinphoneCore *lc){ * If not set the core will create its own window. **/ void linphone_core_set_native_video_window_id(LinphoneCore *lc, unsigned long id){ +#ifdef VIDEO_ENABLED + LinphoneCall *call=linphone_core_get_current_call(lc); lc->video_window_id=id; + if (call!=NULL && call->videostream){ + video_stream_set_native_window_id(call->videostream,id); + } +#endif } /** @@ -3986,14 +4027,17 @@ LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *l return p; } -const char *linphone_error_to_string(LinphoneError err){ +const char *linphone_error_to_string(LinphoneReason err){ switch(err){ - case LinphoneErrorNone: + case LinphoneReasonNone: return "No error"; - case LinphoneErrorNoResponse: + case LinphoneReasonNoResponse: return "No response"; - case LinphoneErrorBadCredentials: + case LinphoneReasonBadCredentials: return "Bad credentials"; + case LinphoneReasonDeclined: + return "Call declined"; } return "unknown error"; } + diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1a75f5bad..012437f62 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -131,7 +131,8 @@ typedef enum _LinphoneCallDir LinphoneCallDir; typedef enum _LinphoneCallStatus { LinphoneCallSuccess, /**< The call was sucessful*/ LinphoneCallAborted, /**< The call was aborted */ - LinphoneCallMissed /**< The call was missed (unanswered)*/ + LinphoneCallMissed, /**< The call was missed (unanswered)*/ + LinphoneCallDeclined /**< The call was declined, either locally or by remote end*/ } LinphoneCallStatus; /** @@ -182,15 +183,16 @@ void linphone_call_params_destroy(LinphoneCallParams *cp); /** * Enum describing failure reasons. **/ -enum _LinphoneError{ - LinphoneErrorNone, - LinphoneErrorNoResponse, /**NewGlobalRef(env->GetObjectClass( alistener)); /*displayStatus(LinphoneCore lc,String message);*/ @@ -96,12 +104,30 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); + /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/ + newSubscriptionRequestId = env->GetMethodID(listernerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V"); + + /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/ + notifyPresenceReceivedId = env->GetMethodID(listernerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V"); + + /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/ + textReceivedId = env->GetMethodID(listernerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); + proxyClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl")); proxyCtrId = env->GetMethodID(proxyClass,"", "(J)V"); callClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallImpl")); callCtrId = env->GetMethodID(callClass,"", "(J)V"); + chatRoomClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatRoomImpl")); + chatRoomCtrId = env->GetMethodID(chatRoomClass,"", "(J)V"); + + friendClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneFriendImpl"));; + friendCtrId =env->GetMethodID(friendClass,"", "(J)V"); + + addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl")); + addressCtrId =env->GetMethodID(addressClass,"", "(J)V"); + } ~LinphoneCoreData() { @@ -116,6 +142,8 @@ public: env->DeleteGlobalRef(callStateClass); env->DeleteGlobalRef(proxyClass); env->DeleteGlobalRef(callClass); + env->DeleteGlobalRef(chatRoomClass); + env->DeleteGlobalRef(friendClass); } jobject core; @@ -124,6 +152,9 @@ public: jclass listernerClass; jmethodID displayStatusId; + jmethodID newSubscriptionRequestId; + jmethodID notifyPresenceReceivedId; + jmethodID textReceivedId; jclass globalStateClass; jmethodID globalStateId; @@ -143,6 +174,15 @@ public: jclass callClass; jmethodID callCtrId; + jclass chatRoomClass; + jmethodID chatRoomCtrId; + + jclass friendClass; + jmethodID friendCtrId; + + jclass addressClass; + jmethodID addressCtrId; + LinphoneCoreVTable vTable; static void showInterfaceCb(LinphoneCore *lc) { @@ -211,6 +251,48 @@ public: ,env->CallStaticObjectMethod(lcData->callStateClass,lcData->callStateFromIntId,(jint)state), message ? env->NewStringUTF(message) : NULL); } + static void notify_presence_recv (LinphoneCore *lc, LinphoneFriend *my_friend) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->notifyPresenceReceivedId + ,lcData->core + ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend)); + } + static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *my_friend, const char* url) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->newSubscriptionRequestId + ,lcData->core + ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend) + ,url ? env->NewStringUTF(url) : NULL); + } + static void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->textReceivedId + ,lcData->core + ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room) + ,env->NewObject(lcData->addressClass,lcData->addressCtrId,(jlong)from) + ,message ? env->NewStringUTF(message) : NULL); + } }; @@ -221,8 +303,8 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* ,jstring jfactoryConfig ,jobject juserdata){ - const char* userConfig = env->GetStringUTFChars(juserConfig, NULL); - const char* factoryConfig = env->GetStringUTFChars(jfactoryConfig, NULL); + const char* userConfig = juserConfig?env->GetStringUTFChars(juserConfig, NULL):NULL; + const char* factoryConfig = jfactoryConfig?env->GetStringUTFChars(jfactoryConfig, NULL):NULL; LinphoneCoreData* ldata = new LinphoneCoreData(env,thiz,jlistener,juserdata); #ifdef ANDROID ms_andsnd_set_jvm(jvm); @@ -240,8 +322,8 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* //clear existing proxy config linphone_core_clear_proxy_config((LinphoneCore*) nativePtr); - env->ReleaseStringUTFChars(juserConfig, userConfig); - env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig); + if (userConfig) env->ReleaseStringUTFChars(juserConfig, userConfig); + if (factoryConfig) env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig); return nativePtr; } extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env @@ -456,7 +538,48 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCurrentCall(JNIEnv* ) { return (jlong)linphone_core_get_current_call((LinphoneCore*)lc); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addFriend(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jlong aFriend + ) { + linphone_core_add_friend((LinphoneCore*)lc,(LinphoneFriend*)aFriend); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPresenceInfo(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jint minute_away + ,jstring jalternative_contact + ,jint status) { + const char* alternative_contact = env->GetStringUTFChars(jalternative_contact, NULL); + linphone_core_set_presence_info((LinphoneCore*)lc,minute_away,alternative_contact,(LinphoneOnlineStatus)status); + env->ReleaseStringUTFChars(jalternative_contact, alternative_contact); +} +extern "C" long Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jto) { + + const char* to = env->GetStringUTFChars(jto, NULL); + LinphoneChatRoom* lResult = linphone_core_create_chat_room((LinphoneCore*)lc,to); + env->ReleaseStringUTFChars(jto, to); + return (long)lResult; +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableVideo(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jboolean vcap_enabled + ,jboolean display_enabled) { + linphone_core_enable_video((LinphoneCore*)lc, vcap_enabled,display_enabled); + +} +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isVideoEnabled(JNIEnv* env + ,jobject thiz + ,jlong lc) { + return linphone_core_video_enabled((LinphoneCore*)lc); +} //ProxyConfig @@ -741,6 +864,90 @@ extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getState( JNIEnv* env return (jint)linphone_call_get_state((LinphoneCall*)ptr); } +//LinphoneFriend +extern "C" long Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env + ,jobject thiz + ,jstring jFriendUri) { + LinphoneFriend* lResult; + if (jFriendUri) { + const char* friendUri = env->GetStringUTFChars(jFriendUri, NULL); + lResult= linphone_friend_new_with_addr(friendUri); + env->ReleaseStringUTFChars(jFriendUri, friendUri); + } else { + lResult = linphone_friend_new(); + } + return (long)lResult; +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jlong linphoneAddress) { + linphone_friend_set_addr((LinphoneFriend*)ptr,(LinphoneAddress*)linphoneAddress); +} +extern "C" long Java_org_linphone_core_LinphoneFriendImpl_getAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (long)linphone_friend_get_address((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setIncSubscribePolicy(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jint policy) { + linphone_friend_set_inc_subscribe_policy((LinphoneFriend*)ptr,(LinphoneSubscribePolicy)policy); +} +extern "C" jint Java_org_linphone_core_LinphoneFriendImpl_getIncSubscribePolicy(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_get_inc_subscribe_policy((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_enableSubscribes(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jboolean value) { + linphone_friend_enable_subscribes((LinphoneFriend*)ptr,value); +} +extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_isSubscribesEnabled(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_subscribes_enabled((LinphoneFriend*)ptr); +} +extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_getStatus(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_get_status((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_edit(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_edit((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_done(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + linphone_friend_done((LinphoneFriend*)ptr); +} +//LinphoneChatRoom +extern "C" long Java_org_linphone_core_LinphoneChatRoomImpl_getPeerAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (long) linphone_chat_room_get_peer_address((LinphoneChatRoom*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jstring jmessage) { + const char* message = env->GetStringUTFChars(jmessage, NULL); + linphone_chat_room_send_message((LinphoneChatRoom*)ptr,message); + env->ReleaseStringUTFChars(jmessage, message); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jobject obj) { + linphone_core_set_native_video_window_id((LinphoneCore*)lc,(unsigned long)obj); + ms_message("linphone_core_set_native_video_window_id() called !!!!!!!"); +} diff --git a/coreapi/misc.c b/coreapi/misc.c index ced4f3396..1dc3b7e70 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -754,14 +754,10 @@ static int get_local_ip_for_with_connect(int type, const char *dest, char *resul } int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ - if (dest==NULL) { - if (type==AF_INET) - dest="87.98.157.38"; /*a public IP address*/ - else dest="2a00:1450:8002::68"; - } strcpy(result,type==AF_INET ? "127.0.0.1" : "::1"); #ifdef HAVE_GETIFADDRS - { + if (dest==NULL) { + /*we use getifaddrs for lookup of default interface */ int found_ifs; found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE); @@ -774,5 +770,33 @@ int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ } #endif /*else use connect to find the best local ip address */ + if (type==AF_INET) + dest="87.98.157.38"; /*a public IP address*/ + else dest="2a00:1450:8002::68"; return get_local_ip_for_with_connect(type,dest,result); } + +#ifndef WIN32 +#include + + + + +void _linphone_core_configure_resolver(){ +/*bionic declares _res but does not define nor export it !!*/ +#ifdef ANDROID + /*timeout and attempts are the same as retrans and retry, but are android specific names.*/ + setenv("RES_OPTIONS","timeout:1 attempts:2 retrans:1 retry:2",1); +#else + res_init(); + _res.retrans=1; /*retransmit every second*/ + _res.retry=2; /*only two times per DNS server*/ +#endif +} + +#else + +void _linphone_core_configure_resolver(){ +} + +#endif diff --git a/coreapi/private.h b/coreapi/private.h index 9bee983b6..969c337e9 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -57,6 +57,7 @@ struct _LinphoneCallParams{ + LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */ bool_t has_video; bool_t pad[3]; }; @@ -76,6 +77,7 @@ struct _LinphoneCall time_t start_time; /*time at which the call was initiated*/ time_t media_start_time; /*time at which it was accepted, media streams established*/ LinphoneCallState state; + LinphoneReason reason; int refcnt; void * user_pointer; int audio_port; @@ -196,9 +198,9 @@ void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float void linphone_core_stop_waiting(LinphoneCore *lc); int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); -void linphone_core_start_pending_refered_calls(LinphoneCore *lc); +void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call); extern SalCallbacks linphone_sal_callbacks; -void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg,LinphoneError error); +void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error); struct _LinphoneProxyConfig { @@ -223,7 +225,7 @@ struct _LinphoneProxyConfig bool_t dial_escape_plus; void* user_data; time_t deletion_date; - LinphoneError error; + LinphoneReason error; }; struct _LinphoneAuthInfo @@ -438,6 +440,7 @@ SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call, bool_t with_video, bool_t only_one_codec); #define linphone_core_ready(lc) ((lc)->state!=LinphoneGlobalStartup) +void _linphone_core_configure_resolver(); #define HOLD_OFF (0) #define HOLD_ON (1) diff --git a/coreapi/proxy.c b/coreapi/proxy.c index aa901cb8d..b77ad9422 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -841,10 +841,12 @@ LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyCon } return NULL; } -LinphoneError linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg) { + +LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg) { return cfg->error; } -void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg,LinphoneError error) { + +void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg,LinphoneReason error) { cfg->error = error; } diff --git a/coreapi/sal.h b/coreapi/sal.h index 0fcd6e5a8..b0b3c7574 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -282,8 +282,11 @@ int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optio int sal_call_hold(SalOp *h, bool_t holdon); int sal_call_update(SalOp *h); SalMediaDescription * sal_call_get_final_media_description(SalOp *h); -int sal_refer(SalOp *h, const char *refer_to); -int sal_refer_accept(SalOp *h); +int sal_call_refer(SalOp *h, const char *refer_to); +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h); +int sal_call_accept_refer(SalOp *h); +/*informs this call is consecutive to an incoming refer */ +int sal_call_set_referer(SalOp *h, SalOp *refered_call); /* returns the SalOp of a call that should be replaced by h, if any */ SalOp *sal_call_get_replaces(SalOp *h); int sal_call_send_dtmf(SalOp *h, char dtmf); diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index c61ec96ab..dfa4744cb 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -162,6 +162,7 @@ SalOp * sal_op_new(Sal *sal){ op->reinvite=FALSE; op->call_id=NULL; op->replaces=NULL; + op->referred_by=NULL; op->masquerade_via=FALSE; op->auto_answer_asked=FALSE; return op; @@ -205,6 +206,9 @@ void sal_op_release(SalOp *op){ if (op->replaces){ ms_free(op->replaces); } + if (op->referred_by){ + ms_free(op->referred_by); + } __sal_op_free(op); } @@ -494,6 +498,12 @@ int sal_call(SalOp *h, const char *from, const char *to){ h->sdp_offering=TRUE; set_sdp_from_desc(invite,h->base.local_media); }else h->sdp_offering=FALSE; + if (h->replaces){ + osip_message_set_header(invite,"Replaces",h->replaces); + if (h->referred_by) + osip_message_set_header(invite,"Referred-By",h->referred_by); + } + eXosip_lock(); err=eXosip_call_send_initial_invite(invite); eXosip_unlock(); @@ -610,6 +620,14 @@ SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ return h->result; } +int sal_call_set_referer(SalOp *h, SalOp *refered_call){ + if (refered_call->replaces) + h->replaces=ms_strdup(refered_call->replaces); + if (refered_call->referred_by) + h->referred_by=ms_strdup(refered_call->referred_by); + return 0; +} + int sal_ping(SalOp *op, const char *from, const char *to){ osip_message_t *options=NULL; @@ -628,7 +646,7 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return -1; } -int sal_refer_accept(SalOp *op){ +int sal_call_accept_refer(SalOp *op){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -648,7 +666,7 @@ int sal_refer_accept(SalOp *op){ return err; } -int sal_refer(SalOp *h, const char *refer_to){ +int sal_call_refer(SalOp *h, const char *refer_to){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -659,6 +677,24 @@ int sal_refer(SalOp *h, const char *refer_to){ return err; } +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h){ + osip_message_t *msg=NULL; + char referto[256]={0}; + int err=0; + eXosip_lock(); + if (eXosip_call_get_referto(other_call_h->did,referto,sizeof(referto)-1)!=0){ + ms_error("eXosip_call_get_referto() failed for did=%i",other_call_h->did); + eXosip_unlock(); + return -1; + } + eXosip_call_build_refer(h->did,referto, &msg); + osip_message_set_header(msg,"Referred-By",h->base.from); + if (msg) err=eXosip_call_send_request(h->did, msg); + else err=-1; + eXosip_unlock(); + return err; +} + SalOp *sal_call_get_replaces(SalOp *h){ if (h->replaces!=NULL){ int cid; @@ -1240,6 +1276,59 @@ static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){ } } +static void fill_options_answer(osip_message_t *options){ + osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); + osip_message_set_accept(options,"application/sdp"); +} + +static void process_refer(Sal *sal, SalOp *op, eXosip_event_t *ev){ + osip_header_t *h=NULL; + osip_message_t *ans=NULL; + ms_message("Receiving REFER request !"); + osip_message_header_get_byname(ev->request,"Refer-To",0,&h); + + if (h){ + osip_from_t *from=NULL; + char *tmp; + osip_from_init(&from); + + if (osip_from_parse(from,h->hvalue)==0){ + if (op ){ + osip_uri_header_t *uh=NULL; + osip_header_t *referred_by=NULL; + osip_uri_header_get_byname(&from->url->url_headers,(char*)"Replaces",&uh); + if (uh!=NULL && uh->gvalue && uh->gvalue[0]!='\0'){ + ms_message("Found replaces in Refer-To"); + if (op->replaces){ + ms_free(op->replaces); + } + op->replaces=ms_strdup(uh->gvalue); + } + osip_message_header_get_byname(ev->request,"Referred-By",0,&referred_by); + if (referred_by && referred_by->hvalue && referred_by->hvalue[0]!='\0'){ + if (op->referred_by) + ms_free(op->referred_by); + op->referred_by=ms_strdup(referred_by->hvalue); + } + } + osip_uri_header_freelist(&from->url->url_headers); + osip_from_to_str(from,&tmp); + sal->callbacks.refer_received(sal,op,tmp); + osip_free(tmp); + osip_from_free(from); + } + eXosip_lock(); + eXosip_call_build_answer(ev->tid,202,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,202,ans); + eXosip_unlock(); + } + else + { + ms_warning("cannot do anything with the refer without destination\n"); + } +} + static void call_message_new(Sal *sal, eXosip_event_t *ev){ osip_message_t *ans=NULL; if (ev->request){ @@ -1268,8 +1357,7 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); } - } - if(MSG_IS_MESSAGE(ev->request)){ + }else if(MSG_IS_MESSAGE(ev->request)){ /* SIP messages could be received into call */ text_received(sal, ev); eXosip_lock(); @@ -1277,27 +1365,12 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ if (ans) eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); - } - if(MSG_IS_REFER(ev->request)){ - osip_header_t *h=NULL; + }else if(MSG_IS_REFER(ev->request)){ SalOp *op=find_op(sal,ev); ms_message("Receiving REFER request !"); - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_lock(); - eXosip_call_build_answer(ev->tid,202,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,202,ans); - eXosip_unlock(); - if (h){ - sal->callbacks.refer_received(sal,op,h->hvalue); - } - else - { - ms_warning("cannot do anything with the refer without destination\n"); - } - } - if(MSG_IS_NOTIFY(ev->request)){ + process_refer(sal,op,ev); + }else if(MSG_IS_NOTIFY(ev->request)){ osip_header_t *h=NULL; char *from=NULL; SalOp *op=find_op(sal,ev); @@ -1314,6 +1387,14 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); osip_free(from); + }else if (MSG_IS_OPTIONS(ev->request)){ + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&ans); + if (ans){ + fill_options_answer(ans); + eXosip_call_send_answer(ev->tid,200,ans); + } + eXosip_unlock(); } }else ms_warning("call_message_new: No request ?"); } @@ -1362,6 +1443,8 @@ static void text_received(Sal *sal, eXosip_event_t *ev){ osip_free(from); } + + static void other_request(Sal *sal, eXosip_event_t *ev){ ms_message("in other_request"); if (ev->request==NULL) return; @@ -1371,8 +1454,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strcmp(ev->request->sip_method,"OPTIONS")==0){ osip_message_t *options=NULL; eXosip_options_build_answer(ev->tid,200,&options); - osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); - osip_message_set_accept(options,"application/sdp"); + fill_options_answer(options); eXosip_options_send_answer(ev->tid,200,options); }else if (strcmp(ev->request->sip_method,"WAKEUP")==0 && comes_from_local_if(ev->request)) { @@ -1382,12 +1464,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){ ms_message("Receiving REFER request !"); if (comes_from_local_if(ev->request)) { - osip_header_t *h=NULL; - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_message_send_answer(ev->tid,200,NULL); - if (h){ - sal->callbacks.refer_received(sal,NULL,h->hvalue); - } + process_refer(sal,NULL,ev); }else ms_warning("Ignored REFER not coming from this local loopback interface."); }else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){ inc_update(sal,ev); diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index bfb3d10f2..10c8b46b7 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -57,6 +57,7 @@ struct SalOp{ osip_call_id_t *call_id; /*used for out of calls transaction in order to retrieve the operation when receiving a response*/ char *replaces; + char *referred_by; bool_t supports_session_timers; bool_t sdp_offering; bool_t reinvite; diff --git a/coreapi/sal_eXosip2_presence.c b/coreapi/sal_eXosip2_presence.c index 1e7409f19..154c8d07e 100644 --- a/coreapi/sal_eXosip2_presence.c +++ b/coreapi/sal_eXosip2_presence.c @@ -20,6 +20,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal_eXosip2.h" +typedef enum { + PIDF = 0, + RFCxxxx = 1, + MSOLDPRES = 2 +} presence_type_t; + +/* + * REVISIT: this static variable forces every dialog to use the same presence description type depending + * on what is received on a single dialog... + */ +static presence_type_t presence_style = PIDF; SalOp * sal_find_out_subscribe(Sal *sal, int sid){ const MSList *elem; @@ -175,268 +186,372 @@ int sal_subscribe_decline(SalOp *op){ return 0; } -static void add_presence_body(osip_message_t *notify, SalPresenceStatus online_status) -{ - char buf[1000]; -#ifdef SUPPORT_MSN - int atom_id = 1000; -#endif - char *contact_info; +static void mk_presence_body (const SalPresenceStatus online_status, const char *contact_info, + char *buf, size_t buflen, presence_type_t ptype) { + switch (ptype) { + case RFCxxxx: { + /* definition from http://msdn.microsoft.com/en-us/library/cc246202%28PROT.10%29.aspx */ + int atom_id = 1000; - osip_from_t *from=NULL; - from=osip_message_get_from(notify); - osip_uri_to_str(from->url,&contact_info); - -#ifdef SUPPORT_MSN - - if (online_status==SalPresenceOnline) - { - sprintf(buf, "\n\ -\n\ + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceBusy) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ -\n\ -", contact_info, atom_id, contact_info); +\n", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceBerightback) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ +
\n\ +\n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceAway) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ +
\n\ +\n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceOnthephone) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceOuttolunch) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ +\n\ +\n\ +
\n\ +\n\ +", contact_info, atom_id, contact_info); + + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + } + break; + } + case MSOLDPRES: { + /* Couldn't find schema http://schemas.microsoft.com/2002/09/sip/presence + * so messages format has been taken from Communigate that can send notify + * requests with this schema + */ + int atom_id = 1000; + + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ \n\ \n\
\n\
\n\
", contact_info, atom_id, contact_info); - } - else - { - sprintf(buf, "\n\ -\n\ + } + else + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ -\n\ +
\n\ +\n\ +\n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } + } + break; + } + default: { /* use pidf+xml as default format, rfc4479, rfc4480, rfc3863 */ - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/xpidf+xml"); -#else + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +Out to lunch \n\ +\n\ +", +contact_info, contact_info); + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +closed\n\ +%s\n\ +\n\ +\n", contact_info, contact_info); + } + break; + } + } // switch - if (online_status==SalPresenceOnline) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ -%s\n\ -online\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceBusy) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - busy\n\ -\n\ -\n\ -%s\n\ -busy\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceBerightback) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - in-transit\n\ -\n\ -\n\ -%s\n\ -be right back\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceAway) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - away\n\ -\n\ -\n\ -%s\n\ -away\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceOnthephone) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - on-the-phone\n\ -\n\ -\n\ -%s\n\ -on the phone\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceOuttolunch) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - meal\n\ -\n\ -\n\ -%s\n\ -out to lunch\n\ -\n\ -", - contact_info, contact_info); - } - else - { - /* */ - sprintf(buf, "\n\ -\n%s", - contact_info, -"\n\ -\n\ -closed\n\ -\n\ - permanent-absence\n\ -\n\ -\n\ -\n\ -\n\n"); - } - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/pidf+xml"); +} + +static void add_presence_body(osip_message_t *notify, SalPresenceStatus online_status) +{ + char buf[1000]; + char *contact_info; + + osip_from_t *from=NULL; + from=osip_message_get_from(notify); + osip_uri_to_str(from->url,&contact_info); + + mk_presence_body (online_status, contact_info, buf, sizeof (buf), presence_style); + + osip_message_set_body(notify, buf, strlen(buf)); + osip_message_set_content_type(notify, + presence_style ? "application/xpidf+xml" : "application/pidf+xml"); -#endif osip_free(contact_info); } @@ -483,137 +598,10 @@ int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus p int i; char buf[1024]; - if (presence_mode==SalPresenceOnline) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - %s\n\ - online\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceBusy - ||presence_mode==SalPresenceDonotdisturb) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - busy\n\ - \n\ - \n\ - %s\n\ - busy\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceBerightback) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - in-transit\n\ - \n\ - \n\ - %s\n\ - be right back\n\ - \n\ - ", - from,from); - } - else if (presence_mode==SalPresenceAway - ||presence_mode==SalPresenceMoved) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - away\n\ - \n\ - \n\ - %s\n\ - away\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceOnthephone) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - on-the-phone\n\ - \n\ - \n\ - %s\n\ - on the phone\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceOuttolunch) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - meal\n\ - \n\ - \n\ - %s\n\ - out to lunch\n\ - \n\ - ", - from, from); - } - else{ - /* offline */ - snprintf(buf, sizeof(buf), "\n\ - \n%s", - from, - "\n\ - \n\ - closed\n\ - \n\ - permanent-absence\n\ - \n\ - \n\ - \n\ - \n\n"); - } + mk_presence_body (presence_mode, from, buf, sizeof (buf), presence_style); - i = eXosip_build_publish(&pub,from, to, NULL, "presence", "1800", "application/pidf+xml", buf); + i = eXosip_build_publish(&pub,from, to, NULL, "presence", "300", + presence_style ? "application/xpidf+xml" : "application/pidf+xml", buf); if (i<0){ ms_warning("Failed to build publish request."); return -1; @@ -627,6 +615,7 @@ int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus p ms_message("Failed to send publish request."); return -1; } + sal_add_other(sal_op_get_sal(op),op,pub); return 0; } @@ -698,7 +687,8 @@ void sal_exosip_notify_recv(Sal *sal, eXosip_event_t *ev){ }else if (strstr(body->body,"berightback")!=NULL || strstr(body->body,"in-transit")!=NULL ){ estatus=SalPresenceBerightback; - }else if (strstr(body->body,"away")!=NULL){ + }else if (strstr(body->body,"away")!=NULL + || strstr(body->body,"idle")){ estatus=SalPresenceAway; }else if (strstr(body->body,"onthephone")!=NULL || strstr(body->body,"on-the-phone")!=NULL){ @@ -721,6 +711,15 @@ void sal_exosip_notify_recv(Sal *sal, eXosip_event_t *ev){ ms_message("And outgoing subscription terminated by remote."); } sal->callbacks.notify_presence(op,op->sid!=-1 ? SalSubscribeActive : SalSubscribeTerminated, estatus,NULL); + + /* try to detect presence message style used by server, + * and switch our presence messages to servers style */ + if (strstr (body->body, "//IETF//DTD RFCxxxx XPIDF 1.0//EN") != NULL) { + presence_style = RFCxxxx; + } else if (strstr(body->body,"http://schemas.microsoft.com/2002/09/sip/presence")!=NULL) { + presence_style = MSOLDPRES; + } + osip_free(tmp); } diff --git a/java/common/org/linphone/core/LinphoneAddress.java b/java/common/org/linphone/core/LinphoneAddress.java index 16c688470..b2a2c9380 100644 --- a/java/common/org/linphone/core/LinphoneAddress.java +++ b/java/common/org/linphone/core/LinphoneAddress.java @@ -17,7 +17,16 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; - +/** + * Object that represents a SIP address. + * The LinphoneAddress is an opaque object to represents SIP addresses, ie the content of SIP's 'from' and 'to' headers. + * A SIP address is made of display name, username, domain name, port, and various uri headers (such as tags). + * It looks like 'Alice '. The LinphoneAddress has methods to extract and manipulate all parts of the address. + * When some part of the address (for example the username) is empty, the accessor methods return null. + *
Can be instanciated using both {@link LinphoneCoreFactory#createLinphoneAddress(String, String, String)} or {@link LinphoneCoreFactory#createLinphoneAddress(String)} + * @author jehanmonnier + * + */ public interface LinphoneAddress { /** * Human display name @@ -58,6 +67,9 @@ public interface LinphoneAddress { */ public String asStringUriOnly(); - /*must return the same thing as asString()*/ + /** + * same as {@link #asString()} + * + * */ public String toString(); } diff --git a/java/common/org/linphone/core/LinphoneAuthInfo.java b/java/common/org/linphone/core/LinphoneAuthInfo.java index 8ae3aeea5..6590dafeb 100644 --- a/java/common/org/linphone/core/LinphoneAuthInfo.java +++ b/java/common/org/linphone/core/LinphoneAuthInfo.java @@ -17,13 +17,51 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; - +/** + * Object holding authentication information. + * In most case, authentication information consists of a username and password. Sometimes, a userid is required by proxy, and realm can be useful to discriminate different SIP domains. + *
This object is instanciated using {@link LinphoneCoreFactory#createAuthInfo(String, String, String)}. + *
+ *Once created and filled, a LinphoneAuthInfo must be added to the LinphoneCore in order to become known and used automatically when needed. + *Use {@link LinphoneCore#addAuthInfo(LinphoneAuthInfo)} for that purpose. + *
+ *The LinphoneCore object can take the initiative to request authentication information when needed to the application + *through the {@link LinphoneCoreListener#authInfoRequested(LinphoneCore, String, String)} listener. + *
+ *The application can respond to this information request later using {@link LinphoneCore#addAuthInfo(LinphoneAuthInfo)}. + *This will unblock all pending authentication transactions and retry them with authentication headers. + * + */ public interface LinphoneAuthInfo { + /** + * get user name + * @return username + */ String getUsername(); - String getPassword(); - String getRealm(); + /** + * Sets the username. + * @param username + */ void setUsername(String username); + /** + * get password + * @return password + */ + String getPassword(); + /** + * sets password + * @param password + */ void setPassword(String password); + /** + * get realm + * @return + */ + String getRealm(); + /** + * set realm + * @param realm + */ void setRealm(String realm); } diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index 03baca9df..65923706c 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -20,26 +20,78 @@ package org.linphone.core; import java.util.Vector; - +/** + * Object representing a Call. calls are created using {@link LinphoneCore#invite(LinphoneAddress)} or paased to the application by listener {@link LinphoneCoreListener#callState(LinphoneCore, LinphoneCall, State, String)} + * + */ public interface LinphoneCall { + /** + * Linphone call states + * + */ static class State { static private Vector values = new Vector(); private final int mValue; private final String mStringValue; + /** + * Idle + */ public final static State Idle = new State(0,"Idle"); + /** + * Incoming call received. + */ public final static State IncomingReceived = new State(1,"IncomingReceived"); + /** + * Outgoing call initialiazed. + */ public final static State OutgoingInit = new State(2,"OutgoingInit"); + /** + * Outgoing call in progress. + */ public final static State OutgoingProgress = new State(3,"OutgoingProgress"); + /** + * Outgoing call ringing. + */ public final static State OutgoingRinging = new State(4,"OutgoingRinging"); + /** + * Outgoing call early media + */ public final static State OutgoingEarlyMedia = new State(5,"OutgoingEarlyMedia"); + /** + * Connected + */ public final static State Connected = new State(6,"Connected"); + /** + * Streams running + */ public final static State StreamsRunning = new State(7,"StreamsRunning"); + /** + * Paussing + */ public final static State Pausing = new State(8,"Pausing"); + /** + * Paused + */ public final static State Paused = new State(9,"Paused"); + /** + * Resuming + */ public final static State Resuming = new State(10,"Resuming"); + /** + * Refered + */ public final static State Refered = new State(11,"Refered"); + /** + * Error + */ public final static State Error = new State(12,"Error"); + /** + * Call end + */ public final static State CallEnd = new State(13,"CallEnd"); + /** + * Paused by remote + */ public final static State PausedByRemote = new State(14,"PausedByRemote"); private State(int value,String stringValue) { mValue = value; @@ -69,10 +121,14 @@ public interface LinphoneCall { * **/ public LinphoneAddress getRemoteAddress(); - + /** + * get direction of the call (incoming or outgoing). + * @return CallDirection + */ public CallDirection getDirection(); /** - * Returns the call log associated to this call. + * get the call log associated to this call. + * @Return LinphoneCallLog **/ public LinphoneCallLog getCallLog(); diff --git a/java/common/org/linphone/core/LinphoneCallLog.java b/java/common/org/linphone/core/LinphoneCallLog.java index 08cdb8034..6c4923872 100644 --- a/java/common/org/linphone/core/LinphoneCallLog.java +++ b/java/common/org/linphone/core/LinphoneCallLog.java @@ -17,13 +17,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; - +/** + * Call data records object + * + */ public interface LinphoneCallLog { - + /** + * Originator of the call as a LinphoneAddress object. + * @return LinphoneAddress + */ public LinphoneAddress getFrom(); - + /** + * Destination of the call as a LinphoneAddress object. + * @return + */ public LinphoneAddress getTo (); - + /** + * The direction of the call + * @return CallDirection + */ public CallDirection getDirection(); } diff --git a/java/common/org/linphone/core/LinphoneChatRoom.java b/java/common/org/linphone/core/LinphoneChatRoom.java new file mode 100644 index 000000000..a8ef5e215 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneChatRoom.java @@ -0,0 +1,39 @@ +/* +LinphoneChatRoom.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; +/** + * + * A chat room is the place where text messages are exchanged. +Can be created by linphone_core_create_chat_room(). + * + */ +public interface LinphoneChatRoom { + /** + * get peer address associated to this LinphoneChatRoom + * + * @return LinphoneAddress peer address + */ + LinphoneAddress getPeerAddress(); + /** + * send a message to peer member of this chat room. + * @param message to be sent + */ + void sendMessage(String message); + +} diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index c09ff9814..e9471201d 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -21,17 +21,31 @@ package org.linphone.core; import java.util.Vector; - +/** + * Linphone core main object created by method {@link LinphoneCoreFactory#createLinphoneCore(LinphoneCoreListener, String, String, Object)}. + * + */ public interface LinphoneCore { - /* + /** * linphone core states */ static public class GlobalState { static private Vector values = new Vector(); - + /** + * Off + */ static public GlobalState GlobalOff = new GlobalState(0,"GlobalOff"); + /** + * Startup + */ static public GlobalState GlobalStartup = new GlobalState(1,"GlobalStartup"); + /** + * On + */ static public GlobalState GlobalOn = new GlobalState(2,"GlobalOn"); + /** + * Shutdown + */ static public GlobalState GlobalShutdown = new GlobalState(3,"GlobalShutdown"); private final int mValue; @@ -54,12 +68,31 @@ public interface LinphoneCore { return mStringValue; } } + /** + * Describes proxy registration states. + * + */ static public class RegistrationState { static private Vector values = new Vector(); + /** + * None + */ static public RegistrationState RegistrationNone = new RegistrationState(0,"RegistrationNone"); + /** + * In Progress + */ static public RegistrationState RegistrationProgress = new RegistrationState(1,"RegistrationProgress"); + /** + * Ok + */ static public RegistrationState RegistrationOk = new RegistrationState(2,"RegistrationOk"); + /** + * Cleared + */ static public RegistrationState RegistrationCleared = new RegistrationState(3,"RegistrationCleared"); + /** + * Failed + */ static public RegistrationState RegistrationFailed = new RegistrationState(4,"RegistrationFailed"); private final int mValue; private final String mStringValue; @@ -81,8 +114,18 @@ public interface LinphoneCore { return mStringValue; } } + /** + * Signaling transports + * + */ static public class Transport { + /** + * UDP transport + */ public final static Transport udp =new Transport("udp"); + /** + * TCP transport + */ public final static Transport tcp =new Transport("tcp"); private final String mStringValue; @@ -94,15 +137,26 @@ public interface LinphoneCore { } } /** - * clear all added proxy config + * clear all added proxy configs */ public void clearProxyConfigs(); - + /** + * Add a proxy configuration. This will start registration on the proxy, if registration is enabled. + * @param proxyCfg + * @throws LinphoneCoreException + */ public void addProxyConfig(LinphoneProxyConfig proxyCfg) throws LinphoneCoreException; - + /** + * Sets the default proxy. + *
+ * This default proxy must be part of the list of already entered {@link LinphoneProxyConfig}. + * Toggling it as default will make LinphoneCore use the identity associated with the proxy configuration in all incoming and outgoing calls. + * @param proxyCfg + */ public void setDefaultProxyConfig(LinphoneProxyConfig proxyCfg); /** + * get he default proxy configuration, that is the one used to determine the current identity. * @return null if no default proxy config */ public LinphoneProxyConfig getDefaultProxyConfig() ; @@ -111,7 +165,11 @@ public interface LinphoneCore { * clear all the added auth info */ void clearAuthInfos(); - + /** + * Adds authentication information to the LinphoneCore. + *
This information will be used during all SIP transacations that require authentication. + * @param info + */ void addAuthInfo(LinphoneAuthInfo info); /** @@ -123,13 +181,22 @@ public interface LinphoneCore { public LinphoneAddress interpretUrl(String destination) throws LinphoneCoreException; /** - * Starts a call given a destination. Internally calls interpretUrl() then invite(LinphoneAddress). + * Starts a call given a destination. Internally calls {@link #interpretUrl(String)} then {@link #invite(LinphoneAddress)}. * @param uri */ public LinphoneCall invite(String destination)throws LinphoneCoreException; - + /** + * Initiates an outgoing call given a destination LinphoneAddress + *
The LinphoneAddress can be constructed directly using linphone_address_new(), or created by linphone_core_interpret_url(). The application doesn't own a reference to the returned LinphoneCall object. Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * @param to the destination of the call (sip address). + * @return LinphoneCall + * @throws LinphoneCoreException + */ public LinphoneCall invite(LinphoneAddress to)throws LinphoneCoreException; - + /** + * Terminates a call. + * @param aCall to be terminated + */ public void terminateCall(LinphoneCall aCall); /** * Returns The LinphoneCall the current call if one is in call @@ -152,6 +219,17 @@ public interface LinphoneCore { * @return Returns true if in incoming call is pending, ie waiting for being answered or declined. */ public boolean isInComingInvitePending(); + /** + * Main loop function. It is crucial that your application call it periodically. + * + * #iterate() performs various backgrounds tasks: + *
  • receiving of SIP messages + *
  • handles timers and timeout + *
  • performs registration to proxies + *
  • authentication retries The application MUST call this function from periodically, in its main loop. + *
    Be careful that this function must be call from the same thread as other liblinphone methods. In not the case make sure all liblinphone calls are serialized with a mutex. + + */ public void iterate(); /** * Accept an incoming call. @@ -232,28 +310,86 @@ public interface LinphoneCore { public void stopDtmf(); /** - * + * remove all call logs */ public void clearCallLogs(); - - /*** * get payload type from mime type an clock rate * * return null if not found */ public PayloadType findPayloadType(String mime,int clockRate); - + /** + * not implemented yet + * @param pt + * @param enable + * @throws LinphoneCoreException + */ public void enablePayloadType(PayloadType pt, boolean enable) throws LinphoneCoreException; - + /** + * Enables or disable echo cancellation. + * @param enable + */ public void enableEchoCancellation(boolean enable); - + /** + * get EC status + * @return true if echo cancellation is enabled. + */ public boolean isEchoCancellationEnabled(); - + /** + * not implemented yet + * @param aTransport + */ public void setSignalingTransport(Transport aTransport); - + /** + * not implemented + * @param value + */ public void enableSpeaker(boolean value); - + /** + * not implemented + * @return + */ public boolean isSpeakerEnabled(); + /** + * add a friend to the current buddy list, if subscription attribute is set, a SIP SUBSCRIBE message is sent. + * @param lf LinphoenFriend to add + * @throws LinphoneCoreException + */ + void addFriend(LinphoneFriend lf) throws LinphoneCoreException; + /** + * Set my presence status + * @param minute_away how long in away + * @param status sip uri used to redirect call in state LinphoneStatusMoved + */ + void setPresenceInfo(int minute_away,String alternative_contact, OnlineStatus status); + /** + * Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org + * @param to destination address for messages + * + * @return {@link LinphoneChatRoom} where messaging can take place. + */ + LinphoneChatRoom createChatRoom(String to); + + public void setVideoWindow(Object w); + public void setPreviewWindow(Object w); + /** + * Enables video globally. + * + * + * This function does not have any effect during calls. It just indicates #LinphoneCore to + * initiate future calls with video or not. The two boolean parameters indicate in which + * direction video is enabled. Setting both to false disables video entirely. + * + * @param vcap_enabled indicates whether video capture is enabled + * @param display_enabled indicates whether video display should be shown + * + **/ + void enableVideo(boolean vcap_enabled, boolean display_enabled); + /** + * Returns TRUE if video is enabled, FALSE otherwise. + * + ***/ + boolean isVideoEnabled(); } diff --git a/java/common/org/linphone/core/LinphoneCoreFactory.java b/java/common/org/linphone/core/LinphoneCoreFactory.java index b3e2952fb..6629c3dc3 100644 --- a/java/common/org/linphone/core/LinphoneCoreFactory.java +++ b/java/common/org/linphone/core/LinphoneCoreFactory.java @@ -21,6 +21,7 @@ package org.linphone.core; + abstract public class LinphoneCoreFactory { private static String factoryName = "org.linphone.core.LinphoneCoreFactoryImpl"; @@ -49,9 +50,21 @@ abstract public class LinphoneCoreFactory { abstract public LinphoneAuthInfo createAuthInfo(String username,String password, String realm); abstract public LinphoneCore createLinphoneCore(LinphoneCoreListener listener, String userConfig,String factoryConfig,Object userdata) throws LinphoneCoreException; - + abstract public LinphoneCore createLinphoneCore(LinphoneCoreListener listener) throws LinphoneCoreException; + + /** + * Constructs a LinphoneAddress object + * @param username + * @param domain + * @param displayName + * @return + */ abstract public LinphoneAddress createLinphoneAddress(String username,String domain,String displayName); - + /** + * Constructs a LinphoneAddress object by parsing the user supplied address, given as a string. + * @param address should be like sip:joe@sip.linphone.org + * @return + */ abstract public LinphoneAddress createLinphoneAddress(String address); abstract public LinphoneProxyConfig createProxyConfig(String identity, String proxy,String route,boolean enableRegister) throws LinphoneCoreException; @@ -62,4 +75,17 @@ abstract public class LinphoneCoreFactory { abstract public void setDebugMode(boolean enable); abstract public void setLogHandler(LinphoneLogHandler handler); + /** + * Create a LinphoneFriend, similar to {@link #createLinphoneFriend()} + {@link LinphoneFriend#setAddress(LinphoneAddress)} + * @param friendUri a buddy address, must be a sip uri like sip:joe@sip.linphone.org + * @return a new LinphoneFriend with address initialized + */ + abstract public LinphoneFriend createLinphoneFriend(String friendUri); + /** + * Create a new LinphoneFriend + * @return + */ + abstract public LinphoneFriend createLinphoneFriend(); + + } diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index c37e5d7c7..daf256662 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -19,15 +19,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone.core; - +/** + * + *This interface holds all callbacks that the application should implement. None is mandatory. + */ public interface LinphoneCoreListener { /**< Notifies the application that it should show up * @return */ public void show(LinphoneCore lc); - /**< Notify calls terminated by far end - * @return */ - public void byeReceived(LinphoneCore lc,String from); /**< Ask the application some authentication information * @return */ public void authInfoRequested(LinphoneCore lc,String realm,String username); @@ -55,5 +55,28 @@ public interface LinphoneCoreListener { * Registration state notification * */ public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String smessage); + /** + * Reports that a new subscription request has been received and wait for a decision. + *Status on this subscription request is notified by changing policy for this friend + *@param lc LinphoneCore + *@param lf LinphoneFriend corresponding to the subscriber + *@param url of the subscriber + * + */ + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); + /** + * Report status change for a friend previously added to LinphoneCore. + * @param lc LinphoneCore + * @param lf updated LinphoneFriend + */ + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); + /** + * invoked when a new text message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param from LinphoneAddress from + * @param message incoming message + */ + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message); } diff --git a/java/common/org/linphone/core/LinphoneFriend.java b/java/common/org/linphone/core/LinphoneFriend.java new file mode 100644 index 000000000..f9ba9b758 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneFriend.java @@ -0,0 +1,122 @@ +/* +LinphoneFriend.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +import java.util.Vector; + + + +/** + * Represents a buddy, all presence actions like subscription and status change notification are performed on this object + * + * + */ +public interface LinphoneFriend { + /** + * Enum controlling behavior for incoming subscription request. + * Use by {@link LinphoneFriend#setIncSubscribePolicy()} + * + */ + static class SubscribePolicy { + static private Vector values = new Vector(); + protected final int mValue; + private final String mStringValue; + /** + * Does not automatically accept an incoming subscription request. + * This policy implies that a decision has to be taken for each incoming subscription request notified by {@link LinphoneCoreListener#newSubscriptionRequest(LinphoneCore, LinphoneFriend, String)} + */ + public final static SubscribePolicy SPWait = new SubscribePolicy(0,"SPWait"); + /** + * Rejects incoming subscription request. + */ + public final static SubscribePolicy SPDeny = new SubscribePolicy(1,"SPDeny"); + /** + * Automatically accepts a subscription request. + */ + public final static SubscribePolicy SPAccept = new SubscribePolicy(2,"SPAccept"); + private SubscribePolicy(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static SubscribePolicy fromInt(int value) { + + for (int i=0; i Because friend configuration must be consistent, applications MUST call {@link #edit()} before doing any attempts to modify friend configuration (such as address or subscription policy and so on). + *Once the modifications are done, then the application must call {@link #done()} to commit the changes. + */ + void edit(); + /** + * Commits modification made to the friend configuration. + */ + void done(); + /** + * Human readable representation of this friend + * @return + */ + String toString(); + + +} diff --git a/java/common/org/linphone/core/LinphoneLogHandler.java b/java/common/org/linphone/core/LinphoneLogHandler.java index d1330a400..9465dccc7 100644 --- a/java/common/org/linphone/core/LinphoneLogHandler.java +++ b/java/common/org/linphone/core/LinphoneLogHandler.java @@ -17,7 +17,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; - +/** + * Interface to implement for handling liblinphone log. + *
    use {@link LinphoneCoreFactory#setLogHandler(LinphoneLogHandler)} + * + */ public interface LinphoneLogHandler { public static final int Fatal=1<<4; public static final int Error=1<<3|Fatal; @@ -25,5 +29,13 @@ public interface LinphoneLogHandler { public static final int Info=1<<1|Warn; public static final int Debug=1|Info; + /** + * Method invoked for each traces + * @param loggerName + * @param level + * @param levelString + * @param msg + * @param e + */ public void log(String loggerName, int level, String levelString, String msg, Throwable e); } diff --git a/java/common/org/linphone/core/LinphoneProxyConfig.java b/java/common/org/linphone/core/LinphoneProxyConfig.java index 236bded39..729fd249d 100644 --- a/java/common/org/linphone/core/LinphoneProxyConfig.java +++ b/java/common/org/linphone/core/LinphoneProxyConfig.java @@ -17,34 +17,62 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; - +/** + * The LinphoneProxyConfig object represents a proxy configuration to be used by the LinphoneCore object. Its fields must not be used directly in favour of the accessors methods. + * Once created and filled properly the LinphoneProxyConfig can be given to LinphoneCore with {@link LinphoneCore#addProxyConfig(LinphoneProxyConfig)}. This will automatically triggers the registration, if enabled. + *
    The proxy configuration are persistent to restarts because they are saved in the configuration file. As a consequence, after {@link LinphoneCoreFactory#createLinphoneCore(LinphoneCoreListener, String, String, Object)} there might already be a default proxy that can be examined with {@link LinphoneCore#getDefaultProxyConfig()} . + * + */ public interface LinphoneProxyConfig { /** - * Unregister proxy config a enable edition + *Starts editing a proxy configuration. + *Because proxy configuration must be consistent, applications MUST call {@link #edit()} before doing any attempts to modify proxy configuration (such as identity, proxy address and so on). + *Once the modifications are done, then the application must call {@link #done()} to commit the changes. */ public void edit(); /** - * Validate proxy config changes. Start registration in case + * Commits modification made to the proxy configuration. */ public void done(); /** - * sip user made by sip:username@domain + * Sets the user identity as a SIP address. + * @param identy This identity is normally formed with display name, username and domain, such as: Alice The REGISTER messages will have from and to set to this identity. */ public void setIdentity(String identity) throws LinphoneCoreException; /** - * Set proxy uri, like sip:linphone.org:5060 + *get the SIP identity that belongs to this proxy configuration. + * + * @return The SIP identity is a SIP address (Display Name ) + */ + public String getIdentity(); + /** + *Sets the proxy address + * Examples of valid sip proxy address are: + *
  • IP address: sip:87.98.157.38 + *
  • IP address with port: sip:87.98.157.38:5062 + *
  • hostnames : sip:sip.example.net * @param proxyUri * @throws LinphoneCoreException */ public void setProxy(String proxyUri) throws LinphoneCoreException; + /** + * get the proxy's SIP address. + * + */ + public String getProxy(); /** * Enable register for this proxy config. * Register message is issued after call to {@link #done()} * @param value * @throws LinphoneCoreException - */ + */ public void enableRegister(boolean value) throws LinphoneCoreException; + /** + * @return true if registration to the proxy is enabled. + */ + public boolean registerEnabled(); + /** * normalize a human readable phone number into a basic string. 888-444-222 becomes 888444222 * @param number @@ -52,7 +80,7 @@ public interface LinphoneProxyConfig { */ public String normalizePhoneNumber(String number); /** - * usefull function to automatically add internationnal prefix to e164 phone numbers + * Useful function to automatically add international prefix to e164 phone numbers * @param prefix */ public void setDialPrefix(String prefix); @@ -64,15 +92,25 @@ public interface LinphoneProxyConfig { public void setDialEscapePlus(boolean value); /** - * rget domain host name or ip + * get domain host name or ip * @return may be null */ public String getDomain(); - public String getIdentity(); - public String getProxy(); - public boolean registerEnabled(); + /** + * + * @return a boolean indicating that the user is successfully registered on the proxy. + */ public boolean isRegistered(); + /** + * Sets a SIP route. When a route is set, all outgoing calls will go to the route's destination if this proxy is the default one (see {@link LinphoneCore#getDefaultProxyConfig()} ). + * @param routeUri ex sip:git.linphone.org + * @throws LinphoneCoreException + */ public void setRoute(String routeUri) throws LinphoneCoreException; + /** + * + * @return the route set for this proxy configuration. + */ public String getRoute(); } diff --git a/java/common/org/linphone/core/OnlineStatus.java b/java/common/org/linphone/core/OnlineStatus.java new file mode 100644 index 000000000..36aca1909 --- /dev/null +++ b/java/common/org/linphone/core/OnlineStatus.java @@ -0,0 +1,96 @@ +/* +OnlineStatus.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +import java.util.Vector; + + +/** + * Enum describing remote friend status + * + */ +public class OnlineStatus { + + static private Vector values = new Vector(); + /** + * Offline + */ + static public OnlineStatus Offline = new OnlineStatus(0,"Offline"); + /** + * Online + */ + static public OnlineStatus Online = new OnlineStatus(1,"Online"); + /** + * Busy + */ + static public OnlineStatus Busy = new OnlineStatus(2,"Busy"); + /** + * Be Right Back + */ + static public OnlineStatus BeRightBack = new OnlineStatus(3,"BeRightBack"); + /** + * Away + */ + static public OnlineStatus Away = new OnlineStatus(4,"Away"); + /** + * On The Phone + */ + static public OnlineStatus OnThePhone = new OnlineStatus(5,"OnThePhone"); + /** + * Out To Lunch + */ + static public OnlineStatus OutToLunch = new OnlineStatus(6,"OutToLunch "); + /** + * Do Not Disturb + */ + static public OnlineStatus DoNotDisturb = new OnlineStatus(7,"DoNotDisturb"); + /** + * Moved in this sate, call can be redirected if an alternate contact address has been set using function {@link LinphoneCore#setPresenceInfo(int, String, OnlineStatus)} + */ + static public OnlineStatus StatusMoved = new OnlineStatus(8,"StatusMoved"); + /** + * Using another messaging service + */ + static public OnlineStatus StatusAltService = new OnlineStatus(9,"StatusAltService"); + /** + * Pending + */ + static public OnlineStatus Pending = new OnlineStatus(10,"Pending"); + + protected final int mValue; + private final String mStringValue; + + private OnlineStatus(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static OnlineStatus fromInt(int value) { + + for (int i=0; i + + + + + + +Liblinphone is a high level library for bringing SIP video call functionnality into an application. It aims at making easy the integration of the SIP video calls into any applications. All variants of linphone are directly based on it: + +
  • linphone (GUI interface) +
  • linphonec (console interface) +
    Liblinphone is GPL (see COPYING file). Please understand the licencing details before using it! + +
    For any use of this library beyond the rights granted to you by the GPL license, please contact Belledonne Communications (contact@belledonne-communications.com) + + + + +

    Package Specification

    + +LibLinphone package is organized in submodules. + + + + +

    Related Documentation

    + +For overviews, tutorials, examples, guides, and tool documentation, please see: + + + +

    +Managing proxies +

    +User registration is controled by {@link org.linphone.core.LinphoneProxyConfig } settings. +
    Each {@link org.linphone.core.LinphoneProxyConfig } object can be configured with registration informations +like {@link org.linphone.core.LinphoneProxyConfig#setProxy proxy address } , {@link org.linphone.core.LinphoneProxyConfig#setIdentity user id}, and so on. +
    A created proxy config using {@link org.linphone.core.LinphoneCoreFactory#createProxyConfig }, once configured, must be added to {@link org.linphone.core.LinphoneCore} using function {@link org.linphone.core.LinphoneCore#addProxyConfig }. +
    It is recommended to set a default {@link org.linphone.core.LinphoneProxyConfig proxy config } using function {@link org.linphone.core.LinphoneCore#setDefaultProxyConfig }. Once done, if {@link org.linphone.core.LinphoneProxyConfig a proxy config } has been configured with attribute {@link org.linphone.core.LinphoneProxyConfig#enableRegister enable register } , next call to {@link org.linphone.core.LinphoneCore#iterate } triggers a SIP register. +
    Registration status is reported by {@link org.linphone.core.LinphoneCoreListener#registrationState registration listener}. +
    +
    This pseudo code demonstrates basic registration operations: +
    +
    +
    +	
    +	LinphoneProxyConfig proxy_cfg;
    +	/*create proxy config*/
    +	proxy_cfg = LinphoneCoreFactory.instance().createProxyConfig();
    +	/*parse identity*/
    +	LinphoneAddress from = LinphoneCoreFactory.instance().createAddress("sip:toto@sip.titi.com");
    +	LinphoneAuthInfo info;
    +	if (password!=NULL){
    + 		info=LinphoneCoreFactory.instance().createAuthInfo(from.getUsername(),null,"secret",null,null); /*create authentication structure from identity*/
    +		lc.addAuthInfo(info); /*add authentication info to LinphoneCore*/
    +	}	
    +	// configure proxy entries
    +	proxy_cfg.setIdenty(identity); /*set identity with user name and domain*/
    +	String server_addr = from.getDomain(); /*extract domain address from identity*/
    +	proxy_cfg.setProxy(server_addr); /* we assume domain = proxy server address*/
    +	proxy_cfg.enableRegister(true); /*activate registration for this proxy config*/
    +	
    +	lc.addProxyConfig(proxy_cfg); /*add proxy config to linphone core*/
    +	lc.setDefaultProxyconfig(proxy_cfg); /*set to default proxy*/ 
    +
    +
    +
    + {@link org.linphone.core.LinphoneCoreListener#registrationState Registration state listener} : +
    +
    + void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String message){
    +		System.out.println(New registration state ["+cstate+"] for user id ["+cfg.getUserName()+"] at proxy ["+cfg.getProxy()+"]";
    +}
    +
    + + +
    Authentication: +
    Most of the time, registration requires {@link org.linphone.core.LinphoneAuthInfo authentication } to succed. {@link org.linphone.core.LinphoneAuthInfo} info must be either added to {@link org.linphone.core.LinphoneCore } using method {@link org.linphone.core.LinphoneCore#addAuthInfo } before {@link org.linphone.core.LinphoneProxyConfig} is added to Linphone core, or on demand from listener {@link org.linphone.core.LinphoneCoreListener#authInfoRequested(LinphoneCore, String, String)} . +
    +
    Unregistration: +
    Unregistration or any changes to {@link org.linphone.core.LinphoneProxyConfig} must be first started by a call to function {@link org.linphone.core.LinphoneProxyConfig#edit } and validated by function {@link org.linphone.core.LinphoneProxyConfig#done } +
    This pseudo code shows how to unregister a user associated to a{@link org.linphone.core.LinphoneProxyConfig} +
    +
    + 	LinphoneProxyConfig proxy_cfg;
    + 	lc.setDefaultProxyConfig(proxy_cfg); /* get default proxy config*/
    +	proxy_cfg.edit(); /*start editing proxy configuration*/
    +	proxy_cfg.enableRegister(false); /*de-activate registration for this proxy config*/
    +	proxy_cfg.done(); /*initiate REGISTER with expire = 0*/
    +
    + +
    +
    +

    +Managing Buddies and buddy list and presence +

    +
    +Buddies and buddy list +
    Each buddy is represented by a {@link org.linphone.core.LinphoneFriend } object created by function {@link org.linphone.core.LinphoneCoreFactory#createLinphoneFriend()}. +Buddy configuration parameters like {@link org.linphone.core.LinphoneFriend#setAddress(LinphoneAddress) sip uri} or {@link org.linphone.core.LinphoneFriend#setIncSubscribePolicy(LinphoneFriend.SubscribePolicy) status publication} are configurable for each buddy. +
    Here under a typical buddy creation: +
    +
    +
    +	LinphoneFriend my_friend=LinphoneFactory.instance().createFriend("sip:joe@sip.linphone.org"); /*creates friend object for buddy joe*/
    +	my_friend.enableSubscribes(true); /*configure this friend to emit SUBSCRIBE message after being added to LinphoneCore*/
    +	my_friend.setIncSubscribePolicy(LinphoneFriend.SubscribePolicy.Accept); /* accept Incoming subscription request for this friend*/
    +
    +
    +{@link LinphoneFriend friends } status changes are reported by {@link org.linphone.core.LinphoneCoreListener#notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf)} . +
    +
    + void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf){
    +	LinphoneAddress friend_address = lf.getAddress();
    +	System.out.println("New state ["+lf.getStatus()+"] for user id ["+friend_address+"] ");
    +}
    +
    +
    + +
    Once created a buddy can be added to the buddy list using function {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) } . Added friends will be notified about {@link org.linphone.core.LinphoneCore#setPresenceInfo(int minute_away,String alternative_contact, OnlineStatus status) local status changes } +
    +Any subsequente modifications to {@link org.linphone.core.LinphoneFriend} must be first started by a call to function to {@link org.linphone.core.LinphoneFriend#edit()} and validated by function {@link org.linphone.core.LinphoneFriend#done()} +
    +
    +	my_friend.edit(); /* start editing friend */
    +	my_friend.enableSubscribes(true); /*disable subscription for this friend*/
    +	my_friend.done(); /*commit changes triggering an UNSUBSCRIBE message*/
    +
    +
    + + Publishing presence status +
    Local presence status can be changed using function {@link org.linphone.core.LinphoneCore#setPresenceInfo }.New status is propagated to all friends {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) previously added } to LinphoneCore. +
    +
    +Handling incoming subscription request +
    New incoming subscription requests are process according to{@link org.linphone.core.LinphoneFriend#setIncSubscribePolicy(LinphoneFriend.SubscribePolicy) the incoming subscription policy state} for subscription initiated by {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) members of the buddy list. } +
    For incoming request coming from an unknown buddy, the call back {@link org.linphone.core.LinphoneCoreListener#newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)} + +

    +Chat room and Messaging +

    + Exchanging text messages +
    Messages are sent using {@link org.linphone.core.LinphoneChatRoom} object. First step is to create a {@link org.linphone.core.LinphoneCore#createChatRoom chat room } +from a peer sip uri. +
    +
    +	LinphoneChatRoom chat_room = lc.createChatRoom("sip:joe@sip.linphone.org");
    +
    + +
    Once created, messages are sent using function {@link org.linphone.core.LinphoneChatRoom#sendMessage } . +
    +
    +	chat_room.sendMessage("Hello world"); /*sending message*/
    +
    + +
    Incoming message are received from {@link org.linphone.core.LinphoneCoreListener#textReceived a listener } +
    +
    +	void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message) {
    +		System.out.println("Message ["+message+"] received from ["+from+"] ");
    +	}
    +
    +
    +
    +
    \ No newline at end of file
    diff --git a/m4/exosip.m4 b/m4/exosip.m4
    index 725def2bb..c25d8ad96 100644
    --- a/m4/exosip.m4
    +++ b/m4/exosip.m4
    @@ -6,7 +6,7 @@ AC_REQUIRE([LP_CHECK_OSIP2])
     
     case $target_os in
     	*darwin*)
    -		OSIP_LIBS="$OSIP_LIBS  -framework CoreFoundation -framework CoreServices "
    +		OSIP_LIBS="$OSIP_LIBS  -framework CoreFoundation "
     	;;
     esac
     
    diff --git a/mediastreamer2 b/mediastreamer2
    index 93ef451fd..7ec60743d 160000
    --- a/mediastreamer2
    +++ b/mediastreamer2
    @@ -1 +1 @@
    -Subproject commit 93ef451fd6da31fd70fd59e6a6ca59cc26a3b287
    +Subproject commit 7ec60743dfffefc49cc83e47f0eff0ec7928d83a
    diff --git a/oRTP b/oRTP
    index 461dd13a0..930fac6f5 160000
    --- a/oRTP
    +++ b/oRTP
    @@ -1 +1 @@
    -Subproject commit 461dd13a0aad2a075a075bf618e68443475f7a24
    +Subproject commit 930fac6f59b13cdd6cbb5f370911a65f98bad7de