Commit be865213 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Rayo plugin - Implementing Rayo IQHandlers

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13785 b35dd754-fafc-0310-a699-88a17e54d16e
parent d989eac3
......@@ -64,6 +64,14 @@
{
cls: 'mute',
label: 'Mute Call'
},
{
cls: 'redirect',
label: 'Redirect Call'
},
{
cls: 'private',
label: 'Private Call'
}],
timer: true
},
......@@ -263,7 +271,12 @@
};
self.hold = function () {
if (self.call) {
self.call.leave();
self.call.hold();
}
};
self.redirect = function () {
if (self.call) {
self.call.redirect(prompt("Please enter new destination:","sip:xxxx@domain.com"));
}
};
self.mute = function () {
......@@ -271,6 +284,11 @@
self.call.mute(true);
}
};
self.private = function () {
if (self.call) {
self.call.private();
}
};
self.unmute = function () {
if (self.call) {
self.call.mute(false);
......
......@@ -38,6 +38,11 @@
onHide: function () {
//console.log('removed it');
},
onHangup: function (number) {
window.candybar.call.hangup();
},
onCall: function (number) {
if (window.dialer.getCallLabel() == "Call") {
......@@ -62,6 +67,12 @@
} else if (window.dialer.getCallLabel() == "Unmute") {
window.candybar.call.mute(false);
} else if (window.dialer.getCallLabel() == "Private") {
window.candybar.call.private();
} else if (window.dialer.getCallLabel() == "Public") {
window.candybar.call.private();
} else {
console.error('bad call state');
}
......@@ -259,12 +270,36 @@
},
offMute: function(callId) {
//console.log('offMute ' + callId);
console.log('offMute ' + callId);
window.candybar.setState('active');
window.dialer.setCallLabel('Hangup');
},
onPrivate: function(callId) {
//console.log('onPrivate ' + callId);
window.candybar.call.privateCall = true;
window.dialer.setCallLabel('Public');
},
offPrivate: function(callId) {
//console.log('offPrivate ' + callId);
window.candybar.call.privateCall = false;
window.dialer.setCallLabel('Hangup');
},
onRedirect: function(callId) {
//console.log('onRedirect ' + callId);
window.candybar.endGently();
window.candybar.call = null;
window.dialer.setCallLabel('Call');
stopTone();
},
onError: function(e) {
console.error(e);
......
......@@ -54,13 +54,13 @@ Strophe.addConnectionPlugin('rayo',
//console.log("hangup " + callId);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("hangup", {xmlns: Strophe.NS.RAYO_CORE});
var iq = $iq({to: callId + "@" + this._connection.domain, from: this._connection.jid, type: "get"}).c("hangup", {xmlns: Strophe.NS.RAYO_CORE});
//console.log(iq.toString());
that._connection.sendIQ(iq, function()
{
this._onhook();
that._onhook();
}, function(error) {
......@@ -109,12 +109,97 @@ Strophe.addConnectionPlugin('rayo',
this._onhook();
},
hold: function(callId)
{
//console.log("hold " + callId);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("hold", {xmlns: Strophe.NS.RAYO_HANDSET});
//console.log(iq.toString());
that._connection.sendIQ(iq, function()
{
that._onhook();
}, function(error) {
//console.log(error);
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("hold failure " + errorcode);
});
});
},
redirect: function(to, mixer, headers)
{
//console.log("redirect " + to + " " + mixer);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("redirect", {xmlns: Strophe.NS.RAYO_CORE, to: to});
if (headers)
{
var hdrs = Object.getOwnPropertyNames(headers)
for (var i=0; i< hdrs.length; i++)
{
var name = hdrs[i];
var value = headers[name];
if (value) iq.c("header", {name: name, value: value}).up();
}
}
console.log(iq.toString());
that._connection.sendIQ(iq, function(response)
{
$('ref', response).each(function()
{
callId = $(this).attr('id');
if (that._isOffhook()) that._onhook();
if (that.callbacks && that.callbacks.onRedirect) that.callbacks.onRedirect(callId);
});
}, function(error) {
console.log(error);
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("redirect failure " + errorcode);
});
});
},
private: function(callId, flag)
{
//console.log('Rayo plugin private ' + callId + " " + flag);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "private" : "public", {xmlns: Strophe.NS.RAYO_HANDSET});
that._connection.sendIQ(iq, null, function(error)
{
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("private/public failure " + errorcode);
});
});
},
mute: function(callId, flag)
{
//console.log('Rayo plugin mute ' + callId + " " + flag);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "mute" : "unmute", {xmlns: Strophe.NS.RAYO_CORE});
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "mute" : "unmute", {xmlns: Strophe.NS.RAYO_HANDSET});
that._connection.sendIQ(iq, null, function(error)
{
......@@ -127,21 +212,23 @@ Strophe.addConnectionPlugin('rayo',
},
answer: function(callId, mixer, headers)
answer: function(callId, mixer, headers, callFrom)
{
//console.log('Rayo plugin accept ' + callId + " " + mixer);
//console.log(headers)
var that = this;
if (this._isOffhook()) this._onhook();
if (!headers) headers = {};
headers.call_id = callId;
//console.log(headers)
this._offhook(mixer, headers, function()
{
var iq = $iq({to: callId + "@rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("answer", {xmlns: Strophe.NS.RAYO_CORE});
if (headers)
{
var hdrs = Object.getOwnPropertyNames(headers)
for (var i=0; i< hdrs.length; i++)
......@@ -151,7 +238,10 @@ Strophe.addConnectionPlugin('rayo',
if (value) iq.c("header", {name: name, value: value}).up();
}
}
iq.c("header", {name: "caller_id", value: callFrom}).up();
iq.c("header", {name: "mixer_name", value: mixer}).up();
//console.log(iq.toString());
......@@ -180,7 +270,7 @@ Strophe.addConnectionPlugin('rayo',
this._offhook(mixer, headers, function()
{
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("dial", {xmlns: Strophe.NS.RAYO_CORE, to: to, from: from});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("dial", {xmlns: Strophe.NS.RAYO_CORE, to: to, from: from});
if (headers)
{
......@@ -208,14 +298,18 @@ Strophe.addConnectionPlugin('rayo',
that.callbacks.onAccept(
{
digit: function(tone) {that.digit(callId, tone);},
redirect: function(to) {that.redirect(to, mixer, headers);},
hangup: function() {that.hangup(callId);},
hold: function() {that.hold(callId);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
from: from,
to: to,
id: callId
id: callId,
privateCall: false
});
}
});
......@@ -256,7 +350,7 @@ Strophe.addConnectionPlugin('rayo',
var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
//console.log(iq.toString())
......@@ -352,8 +446,9 @@ Strophe.addConnectionPlugin('rayo',
var stereo = (headers && headers.stereo_pan) ? headers.stereo_pan : (that.callbacks.stereo_pan ? that.callbacks.stereo_pan : "0");
var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
var group = (headers && headers.group_name) ? headers.group_name : "";
var callid = (headers && headers.call_id) ? headers.call_id : "";
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, cryptoSuite: that.cryptoSuite, localCrypto: that.localCrypto, remoteCrypto: that.remoteCrypto, codec: codec, stereo: stereo, mixer: mixer, group: group});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, cryptoSuite: that.cryptoSuite, localCrypto: that.localCrypto, remoteCrypto: that.remoteCrypto, codec: codec, stereo: stereo, mixer: mixer, group: group, callid: callid});
//console.log(iq.toString())
......@@ -384,7 +479,7 @@ Strophe.addConnectionPlugin('rayo',
//console.log('Rayo plugin onhook ' + this.handsetId);
that = this;
var server = this.handsetId + "@rayo." + this._connection.domain;
var server = this.handsetId + "@" + this._connection.domain;
this._connection.sendIQ($iq({to: server, from: this._connection.jid, type: "get"}).c('onhook', {xmlns: Strophe.NS.RAYO_HANDSET}), function(response)
{
......@@ -441,15 +536,19 @@ Strophe.addConnectionPlugin('rayo',
var call = {
digit: function(tone) {that.digit(callId, tone);},
redirect: function(to) {that.redirect(to, mixer, headers);},
hangup: function() {that.hangup(callId);},
answer: function() {that.answer(callId, mixer, headers);},
hold: function() {that.hold(callId);},
answer: function() {that.answer(callId, mixer, headers, callFrom);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
from: callFrom,
to: callTo,
id: callId
id: callId,
privateCall: false
}
if (that.callbacks && that.callbacks.onOffer) that.callbacks.onOffer(call, headers);
......@@ -466,6 +565,9 @@ Strophe.addConnectionPlugin('rayo',
if (value) iq.c("header", {name: name, value: value}).up();
}
iq.c("header", {name: "caller_id", value: callFrom}).up();
iq.c("header", {name: "mixer_name", value: mixer}).up();
//console.log(iq.toString());
that._connection.sendIQ(iq, null, function(error)
......@@ -543,7 +645,7 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onHold) that.callbacks.onHold(callId);
......@@ -554,7 +656,7 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onMute) that.callbacks.onMute(callId);
......@@ -565,15 +667,39 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.offMute) that.callbacks.offMute(callId);
}
});
$(presence).find('private').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onPrivate) that.callbacks.onPrivate(callId);
}
});
$(presence).find('public').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.offPrivate) that.callbacks.offPrivate(callId);
}
});
$(presence).find('ringing').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
{
var callId = Strophe.getNodeFromJid(from);
......@@ -612,13 +738,15 @@ Strophe.addConnectionPlugin('rayo',
var call = {
digit: function(tone) {that.digit(callId, tone);},
hangup: function() {that.hangup(callId);},
answer: function() {that.answer(callId, mixer, headers);},
hold: function() {that.hold(callId);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
id: callId,
from: Strophe.getNodeFromJid(jid)
from: Strophe.getNodeFromJid(jid),
privateCall: false
}
if (that.callbacks && that.callbacks.onHook) that.callbacks.onHook();
if (that.callbacks && that.callbacks.onBusy) that.callbacks.onBusy(call, headers);
......
......@@ -32,6 +32,7 @@ public class Handset extends BaseVerb {
public String mixer = null;
public String group = null;
public String sipuri = null;
public String callId = null;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo, String mixer)
{
......
......@@ -23,10 +23,7 @@ import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import com.rayo.core.verb.OnHookCommand;
import com.rayo.core.verb.OffHookCommand;
import com.rayo.core.verb.Handset;
import com.rayo.core.verb.SayCompleteEvent;
import com.rayo.core.verb.*;
import com.rayo.core.verb.SayCompleteEvent.Reason;
public class HandsetProvider extends BaseProvider {
......@@ -39,6 +36,11 @@ public class HandsetProvider extends BaseProvider {
private static final QName ONHOOK_QNAME = new QName("onhook", NAMESPACE);
private static final QName OFFHOOK_QNAME = new QName("offhook", NAMESPACE);
private static final QName PRIVATE_QNAME = new QName("private", NAMESPACE);
private static final QName PUBLIC_QNAME = new QName("public", NAMESPACE);
private static final QName MUTE_QNAME = new QName("mute", NAMESPACE);
private static final QName UNMUTE_QNAME = new QName("unmute", NAMESPACE);
private static final QName HOLD_QNAME = new QName("hold", NAMESPACE);
@Override
protected Object processElement(Element element) throws Exception
......@@ -49,14 +51,54 @@ public class HandsetProvider extends BaseProvider {
} else if (OFFHOOK_QNAME.equals(element.getQName())) {
return buildOffHookCommand(element);
} else if (PRIVATE_QNAME.equals(element.getQName())) {
return buildPrivateCommand(element);
} else if (PUBLIC_QNAME.equals(element.getQName())) {
return buildPublicCommand(element);
} else if (MUTE_QNAME.equals(element.getQName())) {
return buildMuteCommand(element);
} else if (UNMUTE_QNAME.equals(element.getQName())) {
return buildUnmuteCommand(element);
} else if (HOLD_QNAME.equals(element.getQName())) {
return buildHoldCommand(element);
} else if (element.getNamespace().equals(RAYO_COMPONENT_NAMESPACE)) {
return buildCompleteCommand(element);
}
return null;
}
private Object buildCompleteCommand(Element element) {
private Object buildPrivateCommand(Element element)
{
return new PrivateCommand();
}
private Object buildPublicCommand(Element element)
{
return new PublicCommand();
}
private Object buildMuteCommand(Element element)
{
return new MuteCommand();
}
private Object buildUnmuteCommand(Element element)
{
return new UnmuteCommand();
}
private Object buildHoldCommand(Element element)
{
return new HoldCommand();
}
private Object buildCompleteCommand(Element element)
{
Element reasonElement = (Element)element.elements().get(0);
String reasonValue = reasonElement.getName().toUpperCase();
Reason reason = Reason.valueOf(reasonValue);
......@@ -87,6 +129,7 @@ public class HandsetProvider extends BaseProvider {
}
handset.group = element.attributeValue("group");
handset.callId = element.attributeValue("callid");
OffHookCommand command = new OffHookCommand();
command.setHandset(handset);
......@@ -112,6 +155,24 @@ public class HandsetProvider extends BaseProvider {
} else if (object instanceof SayCompleteEvent) {
createHandsetCompleteEvent((SayCompleteEvent) object, document);
} else if (object instanceof OnHoldEvent) {
createOnHoldEvent((OnHoldEvent) object, document);
} else if (object instanceof OffHoldEvent) {
createOffHoldEvent((OffHoldEvent) object, document);
} else if (object instanceof MutedEvent) {
createMutedEvent((MutedEvent) object, document);
} else if (object instanceof UnmutedEvent) {
createUnmutedEvent((UnmutedEvent) object, document);
} else if (object instanceof PrivateEvent) {
createPrivateEvent((PrivateEvent) object, document);
} else if (object instanceof PublicEvent) {
createPublicEvent((PublicEvent) object, document);
}
}
......@@ -128,11 +189,44 @@ public class HandsetProvider extends BaseProvider {
}
private void createOnHookCommand(OnHookCommand command, Document document) throws Exception {
private void createOnHookCommand(OnHookCommand command, Document document) throws Exception
{
document.addElement(new QName("onhook", NAMESPACE));
}
private void createHandsetCompleteEvent(SayCompleteEvent event, Document document) throws Exception {
private void createHandsetCompleteEvent(SayCompleteEvent event, Document document) throws Exception
{
addCompleteElement(document, event, COMPLETE_NAMESPACE);
}
private void createOnHoldEvent(OnHoldEvent onHold, Document document)
{
document.addElement(new QName("onhold", NAMESPACE));
}
private void createOffHoldEvent(OffHoldEvent offHold, Document document)
{
document.addElement(new QName("offhold", NAMESPACE));
}
private void createMutedEvent(MutedEvent muted, Document document)
{
document.addElement(new QName("onmute", NAMESPACE));
}
private void createUnmutedEvent(UnmutedEvent unmuted, Document document)
{
document.addElement(new QName("offmute", NAMESPACE));
}
private void createPrivateEvent(PrivateEvent event, Document document)
{
document.addElement(new QName("private", NAMESPACE));
}
private void createPublicEvent(PublicEvent event, Document document)
{
document.addElement(new QName("public", NAMESPACE));
}
}
......@@ -63,6 +63,10 @@ public class ConferenceManager {
private boolean isFirstMember = true;
private boolean privateCall = false;
private String groupName = null;
/*
* If useSingleSender is true, a single
* conferenceSender will be used for all conferences.
......@@ -623,7 +627,8 @@ public class ConferenceManager {
* audio treatment.
* @return true if this is the first member, false otherwise
*/
public boolean isFirstMember() {
public boolean isFirstMember()
{
synchronized (memberList) {
if (isFirstMember == false) {
return false;
......@@ -635,6 +640,26 @@ public class ConferenceManager {
}
}
public boolean isPrivateCall()
{
return privateCall;
}
public void setPrivateCall(boolean privateCall)
{
this.privateCall = privateCall;
}
public String getGroupName()
{
return groupName;
}
public void setGroupName(String groupName)
{
this.groupName = groupName;
}
/**
* Add a new member to the conference
*
......
......@@ -26,9 +26,13 @@ import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.openfire.muc.spi.*;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.http.HttpBindManager;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.handler.IQHandler;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.util.JiveGlobals;
......@@ -36,6 +40,9 @@ import org.xmpp.packet.JID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.component.AbstractComponent;
import org.xmpp.jnodes.*;
......@@ -61,11 +68,14 @@ import org.voicebridge.*;
public class RayoComponent extends AbstractComponent
implements TreatmentDoneListener,
CallEventListener,
DirectCallEventListener {
CallEventListener {
private static final Logger Log = LoggerFactory.getLogger(RayoComponent.class);
private static final String RAYO_CORE = "urn:xmpp:rayo:1";
private static final String RAYO_SAY = "urn:xmpp:tropo:say:1";
private static final String RAYO_HANDSET = "urn:xmpp:rayo:handset:1";
private static final String HOST = "host";
private static final String LOCAL_PORT = "localport";
private static final String REMOTE_PORT = "remoteport";
......@@ -81,20 +91,34 @@ public class RayoComponent extends AbstractComponent
private SayProvider sayProvider = null;
private HandsetProvider handsetProvider = null;
public RayoComponent(final RayoPlugin plugin)
{
self = this;
this.plugin = plugin;
}
public void doStart()
{
Log.info("RayoComponent initialize " + jid);
XMPPServer server = XMPPServer.getInstance();
server.getIQDiscoInfoHandler().addServerFeature(RAYO_CORE);
rayoProvider = new RayoProvider();
rayoProvider.setValidator(new Validator());
server.getIQDiscoInfoHandler().addServerFeature(RAYO_SAY);
sayProvider = new SayProvider();
sayProvider.setValidator(new Validator());
server.getIQDiscoInfoHandler().addServerFeature(RAYO_HANDSET);
handsetProvider = new HandsetProvider();
handsetProvider.setValidator(new Validator());
Plugin fastpath = XMPPServer.getInstance().getPluginManager().getPlugin("fastpath");
createIQHandlers();
Plugin fastpath = server.getPluginManager().getPlugin("fastpath");
if (fastpath != null)
{
......@@ -102,6 +126,19 @@ public class RayoComponent extends AbstractComponent
}
}
public void doStop()
{
Log.info("RayoComponent shutdown ");
XMPPServer server = XMPPServer.getInstance();
server.getIQDiscoInfoHandler().removeServerFeature(RAYO_CORE);
server.getIQDiscoInfoHandler().removeServerFeature(RAYO_SAY);
server.getIQDiscoInfoHandler().removeServerFeature(RAYO_HANDSET);
destroyIQHandlers();
}
public String getName() {
return "rayo";
}
......@@ -112,7 +149,7 @@ public class RayoComponent extends AbstractComponent
@Override
protected String[] discoInfoFeatureNamespaces() {
return new String[]{"urn:xmpp:rayo:1"};
return new String[]{RAYO_CORE};
}
@Override
......@@ -128,7 +165,7 @@ public class RayoComponent extends AbstractComponent
try {
if ("urn:xmpp:rayo:handset:1".equals(namespace)) {
if (RAYO_HANDSET.equals(namespace)) {
IQ reply = null;
Object object = handsetProvider.fromXML(element);
......@@ -140,11 +177,26 @@ public class RayoComponent extends AbstractComponent
} else if (object instanceof OffHookCommand) {
OffHookCommand command = (OffHookCommand) object;
reply = handleOnOffHookCommand(command, iq);
} else if (object instanceof MuteCommand) {
reply = handleMuteCommand((MuteCommand) object, iq);
} else if (object instanceof UnmuteCommand) {
reply = handleMuteCommand((UnmuteCommand) object, iq);
} else if (object instanceof HoldCommand) {
reply = handleHoldCommand((HoldCommand) object, iq);
} else if (object instanceof PrivateCommand) {
reply = handlePrivateCommand(object, iq);
} else if (object instanceof PublicCommand) {
reply = handlePrivateCommand(object, iq);
}
return reply;
}
if ("urn:xmpp:tropo:say:1".equals(namespace)) {
if (RAYO_SAY.equals(namespace)) {
IQ reply = null;
Object object = sayProvider.fromXML(element);
......@@ -161,26 +213,12 @@ public class RayoComponent extends AbstractComponent
return reply;
}
if ("urn:xmpp:rayo:1".equals(namespace)) {
if (RAYO_CORE.equals(namespace)) {
IQ reply = null;
Object object = rayoProvider.fromXML(element);
if (object instanceof ConnectCommand) {
} else if (object instanceof HoldCommand) {
// implemented as onhook on client
} else if (object instanceof UnholdCommand) {
// implemented as offhook on client
} else if (object instanceof MuteCommand) {
reply = handleMuteCommand((MuteCommand) object, iq);
} else if (object instanceof UnmuteCommand) {
reply = handleMuteCommand((UnmuteCommand) object, iq);
} else if (object instanceof JoinCommand) {
if (object instanceof JoinCommand) {
reply = handleJoinCommand((JoinCommand) object, iq);
} else if (object instanceof UnjoinCommand) {
......@@ -199,6 +237,13 @@ public class RayoComponent extends AbstractComponent
// implemented as hangup on client
} else if (object instanceof RedirectCommand) {
RedirectCommand redirect = (RedirectCommand) object;
DialCommand dial = new DialCommand();
dial.setTo(redirect.getTo());
dial.setFrom(new URI("xmpp:" + iq.getFrom()));
dial.setHeaders(redirect.getHeaders());
reply = handleDialCommand((DialCommand) dial, iq);
} else if (object instanceof DialCommand) {
reply = handleDialCommand((DialCommand) object, iq);
......@@ -225,6 +270,26 @@ public class RayoComponent extends AbstractComponent
}
}
private IQ handleHoldCommand(Object object, IQ iq)
{
Log.info("RayoComponent handleHoldCommand");
IQ reply = IQ.createResultIQ(iq);
String callId = iq.getTo().getNode(); // far party
CallHandler handler = CallHandler.findCall(callId);
if (handler != null)
{
handler.getCallParticipant().setHeld(true);
} else {
reply.setError(PacketError.Condition.item_not_found);
}
return reply;
}
private IQ handleMuteCommand(Object object, IQ iq)
{
......@@ -233,7 +298,7 @@ public class RayoComponent extends AbstractComponent
boolean muted = object instanceof MuteCommand;
IQ reply = IQ.createResultIQ(iq);
String callId = JID.escapeNode(iq.getFrom().toString());
String callId = JID.escapeNode(iq.getFrom().toString()); // handset
CallHandler handler = CallHandler.findCall(callId);
......@@ -266,10 +331,72 @@ public class RayoComponent extends AbstractComponent
if (muted)
{
MutedEvent event = new MutedEvent();
presence.getElement().add(toXML(event));
presence.getElement().add(handsetProvider.toXML(event));
} else {
UnmutedEvent event = new UnmutedEvent();
presence.getElement().add(toXML(event));
presence.getElement().add(handsetProvider.toXML(event));
}
sendPacket(presence);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
reply.setError(PacketError.Condition.item_not_found);
}
return reply;
}
private IQ handlePrivateCommand(Object object, IQ iq)
{
Log.info("RayoComponent handlePrivateCommand");
boolean privateCall = object instanceof PrivateCommand;
IQ reply = IQ.createResultIQ(iq);
String callId = JID.escapeNode(iq.getFrom().toString()); // handset
CallHandler handler = CallHandler.findCall(callId);
if (handler != null)
{
try {
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(handler.getCallParticipant().getConferenceId());
conferenceManager.setPrivateCall(privateCall);
ArrayList memberList = conferenceManager.getMemberList();
synchronized (memberList)
{
for (int i = 0; i < memberList.size(); i++)
{
ConferenceMember member = (ConferenceMember) memberList.get(i);
CallHandler callHandler = member.getCallHandler();
CallParticipant cp = callHandler.getCallParticipant();
String target = cp.getCallOwner();
Log.info( "RayoComponent handlePrivateCommand route event to " + target);
if (target != null)
{
Presence presence = new Presence();
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(target);
if (privateCall)
{
PrivateEvent event = new PrivateEvent();
presence.getElement().add(handsetProvider.toXML(event));
} else {
PublicEvent event = new PublicEvent();
presence.getElement().add(handsetProvider.toXML(event));
}
sendPacket(presence);
......@@ -293,15 +420,15 @@ public class RayoComponent extends AbstractComponent
Log.info("RayoComponent handleOnOffHookCommand");
IQ reply = IQ.createResultIQ(iq);
String callId = JID.escapeNode(iq.getFrom().toString());
String handsetId = JID.escapeNode(iq.getFrom().toString());
if (object instanceof OnHookCommand)
{
CallHandler handler = CallHandler.findCall(callId);
CallHandler handler = CallHandler.findCall(handsetId);
if (handler != null)
{
handleOnOffHook(callId, object, plugin.getRelayChannel(callId));
handleOnOffHook(handsetId, object, plugin.getRelayChannel(handsetId), reply);
} else {
reply.setError(PacketError.Condition.item_not_found);
......@@ -317,7 +444,7 @@ public class RayoComponent extends AbstractComponent
if (channel != null)
{
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(HOST, LocalIPResolver.getLocalIP());
childElement.addAttribute(LOCAL_PORT, Integer.toString(channel.getPortA()));
......@@ -327,7 +454,7 @@ public class RayoComponent extends AbstractComponent
Log.debug("Created WebRTC handset channel {}:{}, {}:{}, {}:{}", new Object[]{HOST, LocalIPResolver.getLocalIP(), LOCAL_PORT, Integer.toString(channel.getPortA()), REMOTE_PORT, Integer.toString(channel.getPortB())});
handleOnOffHook(callId, object, channel);
handleOnOffHook(handsetId, object, channel, reply);
} else {
reply.setError(PacketError.Condition.internal_server_error);
......@@ -335,14 +462,14 @@ public class RayoComponent extends AbstractComponent
} else { // SIP handset
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(ID, callId);
childElement.addAttribute(URI, "handset:" + callId + "@rayo." + getDomain() + "/" + iq.getFrom().getNode());
childElement.addAttribute(ID, handsetId);
childElement.addAttribute(URI, "handset:" + handsetId + "@rayo." + getDomain() + "/" + iq.getFrom().getNode());
Log.info("Created SIP handset channel " + handset.sipuri);
handleOnOffHook(callId, object, null);
handleOnOffHook(handsetId, object, null, reply);
}
}
......@@ -350,14 +477,14 @@ public class RayoComponent extends AbstractComponent
}
private void handleOnOffHook(String callId, Object object, RelayChannel channel)
private void handleOnOffHook(String handsetId, Object object, RelayChannel channel, IQ reply)
{
final boolean flag = object instanceof OnHookCommand;
Log.info("RayoComponent handleOnOffHook " + flag);
try {
CallHandler handler = CallHandler.findCall(callId);
CallHandler handler = CallHandler.findCall(handsetId);
if (handler != null)
{
......@@ -375,21 +502,34 @@ public class RayoComponent extends AbstractComponent
mediaPreference = "PCM/48000/2";
CallParticipant cp = new CallParticipant();
cp.setCallId(callId);
cp.setCallId(handsetId);
cp.setConferenceId(handset.mixer);
cp.setDisplayName("rayo-handset-" + System.currentTimeMillis());
cp.setName(cp.getDisplayName());
cp.setVoiceDetection(true);
cp.setCallOwner(JID.unescapeNode(callId));
cp.setCallOwner(JID.unescapeNode(handsetId));
String label = (new JID(cp.getCallOwner())).getNode();
if (handset.group != null && ! "".equals(handset.group))
{
// set for new or existing conference
label = handset.group;
}
ConferenceManager cm = ConferenceManager.getConference(handset.mixer, mediaPreference, label, false);
ConferenceManager.getConference(handset.mixer, mediaPreference, handset.group, false);
ConferenceManager.setDisplayName(handset.mixer, handset.group);
if (handset.callId != null && "".equals(handset.callId) == false)
{
cm.setCallId(handset.callId); // set answering far party call id for mixer
}
if (handset.group != null && ! "".equals(handset.group))
{
cm.setGroupName(handset.group);
}
if (cm.isPrivateCall() == false || cm.getMemberList().size() < 2)
{
if (channel == null)
{
cp.setMediaPreference("PCMU/8000/1");
......@@ -410,6 +550,11 @@ public class RayoComponent extends AbstractComponent
{
channel.setCallHandler(callHandler);
}
} else {
reply.setError(PacketError.Condition.not_allowed);
}
}
} catch (Exception e) {
......@@ -432,7 +577,7 @@ public class RayoComponent extends AbstractComponent
try {
callHandler.playTreatmentToCall(treatmentId, this);
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(URI, (String) "xmpp:" + entityId + "@rayo." + getDomain() + "/" + treatmentId);
} catch (Exception e1) {
......@@ -447,7 +592,7 @@ public class RayoComponent extends AbstractComponent
try {
conferenceManager.addTreatment(treatmentId);
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(URI, (String) "xmpp:" + entityId + "@rayo." + getDomain() + "/" + treatmentId);
} catch (Exception e2) {
......@@ -499,23 +644,32 @@ public class RayoComponent extends AbstractComponent
private IQ handleAcceptCommand(AcceptCommand command, IQ iq)
{
String callId = iq.getTo().getNode();
Log.info("RayoComponent handleAcceptCommand " + iq.getFrom() + " " + callId);
Map<String, String> headers = command.getHeaders();
String callId = iq.getTo().getNode(); // destination JID escaped
String callerId = headers.get("caller_id"); // source JID
String mixer = headers.get("mixer_name");
Log.info("RayoComponent handleAcceptCommand " + callerId + " " + callId + " " + mixer);
IQ reply = IQ.createResultIQ(iq);
JID callJID = getJID(callId);
if (callJID != null) // only for XMPP calls
{
Map<String, String> headers = command.getHeaders();
if (mixer != null)
{
headers.put("call_protocol", "XMPP");
callerId = callerId.substring(5); // remove xmpp: prefix
Presence presence = new Presence();
presence.setFrom(iq.getTo());
presence.setTo(callJID);
presence.setTo(callerId);
presence.getElement().add(rayoProvider.toXML(new RingingEvent(null, headers)));
sendPacket(presence);
} else reply.setError(PacketError.Condition.item_not_found);
}
return reply;
......@@ -523,29 +677,39 @@ public class RayoComponent extends AbstractComponent
private IQ handleAnswerCommand(AnswerCommand command, IQ iq)
{
String callId = iq.getTo().getNode();
Log.info("RayoComponent AnswerCommand " + iq.getFrom() + " " + callId);
Map<String, String> headers = command.getHeaders();
IQ reply = IQ.createResultIQ(iq);
String callId = iq.getTo().getNode(); // destination JID escaped
String callerId = headers.get("caller_id"); // source JID
Log.info("RayoComponent AnswerCommand " + callerId + " " + callId);
if (callerId != null)
{
JID callJID = getJID(callId);
Map<String, String> headers = command.getHeaders();
CallHandler callHandler = null;
CallHandler handsetHandler = null;
if (callJID != null) // XMPP call
{
callerId = callerId.substring(5); // remove xmpp: prefix
headers.put("call_protocol", "XMPP");
headers.put("call_owner", callJID.toString());
headers.put("call_owner", callerId);
headers.put("call_action", "join");
try {
Presence presence1 = new Presence(); //to caller
presence1.setFrom(iq.getTo());
presence1.setTo(callJID);
presence1.setTo(callerId);
presence1.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers)));
sendPacket(presence1);
callHandler = CallHandler.findCall(callId);
handsetHandler = CallHandler.findCall(JID.escapeNode(callerId));
} catch (Exception e) {
reply.setError(PacketError.Condition.item_not_found);
......@@ -555,26 +719,23 @@ public class RayoComponent extends AbstractComponent
} else {
callHandler = CallHandler.findCall(callId); // SIP call;
handsetHandler = CallHandler.findCall(JID.escapeNode(iq.getFrom().toString()));
}
if (callHandler != null)
if (callHandler != null && handsetHandler != null)
{
Log.info("RayoComponent handleAnswerCommand found call handler " + callHandler);
CallHandler handsetHandler = CallHandler.findCall(JID.escapeNode(iq.getFrom().toString()));
if (handsetHandler != null)
{
Log.info("RayoComponent handleAnswerCommand found handset handler " + handsetHandler);
try {
CallParticipant cp = callHandler.getCallParticipant();
CallParticipant hp = handsetHandler.getCallParticipant();
hp.setFarParty(cp);
Log.info("RayoComponent handleAnswerCommand found call handlers " + cp.getCallId() + " " + hp.getCallId());
try {
long start = System.currentTimeMillis();
cp.setStartTimestamp(start);
cp.setHandset(hp);
hp.setFarParty(cp);
hp.setStartTimestamp(start);
cp.setStartTimestamp(System.currentTimeMillis());
cp.setHeaders(headers);
String recording = cp.getConferenceId() + "-" + cp.getStartTimestamp() + ".au";
......@@ -585,18 +746,24 @@ public class RayoComponent extends AbstractComponent
if (callJID != null)
{
source = callJID.getNode();
source = (new JID(callerId)).getNode();
Config.createCallRecord(source, recording, "xmpp:" + iq.getFrom(), cp.getStartTimestamp(), 0, "dialed") ;
Config.createCallRecord(destination, recording, "xmpp:" + callJID, cp.getStartTimestamp(), 0, "received") ;
Config.createCallRecord(destination, recording, "xmpp:" + callerId, cp.getStartTimestamp(), 0, "received");
sendMessage(new JID(callerId), iq.getFrom(), "Call started", recording);
} else { // incoming SIP
Config.createCallRecord(destination, recording, "sip:" + cp.getPhoneNumber(), cp.getStartTimestamp(), 0, "received") ;
sendMessage(iq.getFrom(), new JID(cp.getCallId() + "@" + getDomain()), "Call started", recording);
}
} catch (ParseException e1) {
reply.setError(PacketError.Condition.internal_server_error);
}
} else reply.setError(PacketError.Condition.item_not_found);
} else reply.setError(PacketError.Condition.item_not_found);
......@@ -720,20 +887,6 @@ public class RayoComponent extends AbstractComponent
}
private IQ handleDialCommand(DialCommand command, IQ iq)
{
Log.info("RayoComponent handleDialCommand " + iq.getFrom());
Map<String, String> headers = command.getHeaders();
String bridged = headers.get("voicebridge");
if (bridged == null)
return handleHandsetDialCommand(command, iq);
else
return handleBridgedDialCommand(command, iq);
}
private IQ handleHandsetDialCommand(DialCommand command, IQ iq)
{
Log.info("RayoComponent handleHandsetDialCommand " + iq.getFrom());
......@@ -809,11 +962,9 @@ public class RayoComponent extends AbstractComponent
headers.put("mixer_name", hp.getConferenceId());
ConferenceManager.setCallId(hp.getConferenceId(), source);
if (findUser(destination.getNode()) != null)
{
routeXMPPCall(reply, destination, source, calledName, headers);
routeXMPPCall(reply, destination, source, calledName, headers, hp.getConferenceId());
} else {
int count = 0;
......@@ -829,7 +980,7 @@ public class RayoComponent extends AbstractComponent
for (ClientSession session : sessions)
{
routeXMPPCall(reply, session.getAddress(), source, calledName, headers);
routeXMPPCall(reply, session.getAddress(), source, calledName, headers, hp.getConferenceId());
count++;
}
}
......@@ -847,7 +998,7 @@ public class RayoComponent extends AbstractComponent
{
if (iq.getFrom().toBareJID().equals(role.getUserAddress().toBareJID()) == false)
{
routeXMPPCall(reply, role.getUserAddress(), source, calledName, headers);
routeXMPPCall(reply, role.getUserAddress(), source, calledName, headers, hp.getConferenceId());
count++;
}
}
......@@ -880,10 +1031,12 @@ public class RayoComponent extends AbstractComponent
return reply;
}
private void routeXMPPCall(IQ reply, JID destination, String source, String calledName, Map<String, String> headers)
private void routeXMPPCall(IQ reply, JID destination, String source, String calledName, Map<String, String> headers, String mixer)
{
String callId = JID.escapeNode(destination.toString());
Presence presence = new Presence();
presence.setFrom(source + "@rayo." + getDomain());
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(destination);
OfferEvent offer = new OfferEvent(null);
......@@ -905,144 +1058,15 @@ public class RayoComponent extends AbstractComponent
offer.setHeaders(headers);
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(URI, (String) "xmpp:" + presence.getFrom());
childElement.addAttribute(ID, (String) callId);
presence.getElement().add(rayoProvider.toXML(offer));
sendPacket(presence);
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
childElement.addAttribute(URI, (String) "xmpp:" + destination);
childElement.addAttribute(ID, (String) JID.escapeNode(destination.toString()));
}
private IQ handleBridgedDialCommand(DialCommand command, IQ iq)
{
Log.info("RayoComponent handleBridgedDialCommand " + iq.getFrom());
IQ reply = IQ.createResultIQ(iq);
Map<String, String> headers = command.getHeaders();
String from = command.getFrom().toString();
String to = command.getTo().toString();
boolean fromPhone = from.indexOf("sip:") == 0 || from.indexOf("tel:") == 0;
boolean toPhone = to.indexOf("sip:") == 0 || to.indexOf("tel:") == 0;
boolean toHandset = to.indexOf("handset:") == 0;
boolean fromHandset = from.indexOf("handset:") == 0;
String mixer = null;
String callerId = headers.get("caller_id");
String calledId = headers.get("called_id");
String callerName = headers.get("caller_name");
String calledName = headers.get("called_name");
JoinCommand join = command.getJoin();
if (join != null && join.getTo() != null)
{
if (join.getType() == JoinDestinationType.CALL) {
// TODO join.getTo()
} else {
mixer = join.getTo();
}
}
if (callerId == null) callerId = "rayo-caller-" + System.currentTimeMillis();
if (calledId == null) calledId = "rayo-called-" + System.currentTimeMillis();
CallParticipant cp = new CallParticipant();
cp.setVoiceDetection(true);
cp.setCallOwner(iq.getFrom().toString());
if (fromPhone && toPhone) // Phone to Phone
{
cp.setMediaPreference("PCMU/8000/1");
cp.setProtocol("SIP");
cp.setCallId(calledId);
cp.setPhoneNumber(to.substring(4));
cp.setName(calledName == null ? cp.getPhoneNumber() : calledName);
cp.setSecondPartyCallId(callerId);
cp.setSecondPartyNumber(from.substring(4));
cp.setSecondPartyName(callerName == null ? cp.getSecondPartyNumber() : callerName);
if (mixer == null) // Direct call, migrate to TwoParty later with Join to mixer
{
DirectCallHandler callHandler = new DirectCallHandler(cp, this);
callHandler.start();
} else { // TwoParty, use a mixer
if (mixer == null) mixer = cp.getPhoneNumber();
cp.setConferenceId(mixer);
TwoPartyCallHandler callHandler = new TwoPartyCallHandler(this, cp);
callHandler.start();
}
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
childElement.addAttribute(URI, (String) "xmpp:" + calledId + "@rayo." + getDomain());
} else if (fromPhone && !toPhone) { // Phone to PC
cp.setMediaPreference("PCMU/8000/1");
cp.setProtocol("SIP");
cp.setCallId(callerId);
cp.setPhoneNumber(from.substring(4));
cp.setName(callerName == null ? cp.getPhoneNumber() : callerName);
if (mixer == null) mixer = cp.getPhoneNumber();
cp.setConferenceId(mixer);
if (toHandset) // (no offer)
{
//doPhoneAndPcCall(new JID(to.substring(8)), cp, reply, mixer);
} else { // (offer)
}
} else if (!fromPhone && toPhone) { // PC to Phone
cp.setMediaPreference("PCMU/8000/1");
cp.setProtocol("SIP");
cp.setCallId(calledId);
cp.setPhoneNumber(to.substring(4));
cp.setName(calledName == null ? cp.getPhoneNumber() : calledName);
if (mixer == null) mixer = cp.getPhoneNumber();
cp.setConferenceId(mixer);
if (fromHandset) // (no offer)
{
//doPhoneAndPcCall(new JID(from.substring(8)), cp, reply, mixer);
} else { // (offer)
}
} else if (!fromPhone && !toPhone) { // PC to PC
if (fromHandset && toHandset) // (intercom)
{
} else { // (private wire)
if (toHandset) // offer from
{
} else { // offer to
}
}
}
return reply;
}
private IQ doPhoneAndPcCall(String handsetId, CallParticipant cp, IQ reply)
{
Log.info("RayoComponent doPhoneAndPcCall " + handsetId);
......@@ -1061,7 +1085,7 @@ public class RayoComponent extends AbstractComponent
outgoingCallHandler.start();
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
final Element childElement = reply.setChildElement("ref", RAYO_CORE);
childElement.addAttribute(URI, (String) "xmpp:" + cp.getCallId() + "@rayo." + getDomain());
childElement.addAttribute(ID, (String) cp.getCallId());
......@@ -1084,20 +1108,23 @@ public class RayoComponent extends AbstractComponent
hp.setFarParty(cp);
cp.setHandset(hp);
long start = System.currentTimeMillis();
cp.setStartTimestamp(start);
hp.setStartTimestamp(start);
String mixer = hp.getConferenceId();
ConferenceManager conferenceManager = ConferenceManager.getConference(mixer,
hp.getMediaPreference(),
cp.getName(), false);
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(mixer);
cp.setConferenceId(mixer);
cp.setCallId(mixer);
conferenceManager.setCallId(mixer);
cp.setStartTimestamp(System.currentTimeMillis());
String recording = mixer + "-" + cp.getStartTimestamp() + ".au";
conferenceManager.recordConference(true, recording, "au");
Config.createCallRecord(cp.getDisplayName(), recording, cp.getPhoneNumber(), cp.getStartTimestamp(), 0, "dialed") ;
sendMessage(new JID(cp.getCallOwner()), new JID(cp.getCallId() + "@" + getDomain()), "Call started", recording);
} catch (ParseException e1) {
reply.setError(PacketError.Condition.internal_server_error);
}
......@@ -1208,6 +1235,9 @@ public class RayoComponent extends AbstractComponent
{
ConferenceMember member = (ConferenceMember) memberList.get(i);
CallHandler callHandler = member.getCallHandler();
if (callHandler != null)
{
CallParticipant cp = callHandler.getCallParticipant();
String target = cp.getCallOwner();
......@@ -1235,6 +1265,7 @@ public class RayoComponent extends AbstractComponent
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
......@@ -1243,6 +1274,8 @@ public class RayoComponent extends AbstractComponent
private void finishCallRecord(CallParticipant cp)
{
Log.info( "RayoComponent finishCallRecord " + cp.getStartTimestamp());
if (cp.getStartTimestamp() > 0)
{
cp.setEndTimestamp(System.currentTimeMillis());
......@@ -1251,55 +1284,51 @@ public class RayoComponent extends AbstractComponent
try {
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(cp.getConferenceId());
conferenceManager.recordConference(false, null, null);
} catch (Exception e) {}
cp.setStartTimestamp(0);
}
}
String target = cp.getCallOwner();
JID destination = getJID(conferenceManager.getCallId());
public void initiated(String name, String callId)
if (destination == null)
{
Log.info( "RayoComponent initiated " + name + " " + callId);
Presence presence = new Presence();
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(new JID(name));
presence.getElement().add(rayoProvider.toXML(new RingingEvent(null, null)));
sendPacket(presence);
destination = new JID(conferenceManager.getCallId() + "@" + getDomain());
}
public void established(String name, String callId)
if (target == null)
{
Log.info( "RayoComponent established " + name + " " + callId);
Presence presence = new Presence();
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(new JID(name));
presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, null)));
sendPacket(presence);
if (cp.getHandset() != null)
{
target = cp.getHandset().getCallOwner();
}
}
public void failed(String name, String callId)
if (target != null)
{
Log.info( "RayoComponent failed " + name + " " + callId);
try {
sendMessage(new JID(target), destination, "Call ended", null);
} catch (Exception e) {
e.printStackTrace();
}
}
Presence presence = new Presence();
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(new JID(name));
presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("ERROR"), null)));
//sendPacket(presence);
} catch (Exception e) {}
cp.setStartTimestamp(0);
}
}
public void terminated(String name, String callId)
private void sendMessage(JID from, JID to, String body, String fileName)
{
Log.info( "RayoComponent terminated " + name + " " + callId);
Log.info( "RayoComponent sendMessage " + from + " " + to + " " + body + " " + fileName);
Presence presence = new Presence();
presence.setFrom(callId + "@rayo." + getDomain());
presence.setTo(new JID(name));
presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("HANGUP"), null)));
//sendPacket(presence);
int port = HttpBindManager.getInstance().getHttpBindUnsecurePort();
Message packet = new Message();
packet.setTo(to);
packet.setFrom(from);
packet.setType(Message.Type.chat);
packet.setThread("http://" + getDomain() + ":" + port + "/rayo/recordings/" + fileName);
packet.setBody(body);
sendPacket(packet);
}
public void sendPacket(Packet packet)
......@@ -1325,7 +1354,7 @@ public class RayoComponent extends AbstractComponent
ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(conferenceEvent.getConferenceId());
String groupName = conferenceManager.getDisplayName();
String groupName = conferenceManager.getGroupName();
String callId = conferenceManager.getCallId();
if (callId == null) callId = conferenceEvent.getConferenceId(); // special case of SIP incoming
......@@ -1343,11 +1372,9 @@ public class RayoComponent extends AbstractComponent
{
ArrayList memberList = conferenceManager.getMemberList();
if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT))
if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT) && callId.equals(conferenceEvent.getCallId()))
{
CallParticipant hp = callParticipant.getHandset();
if (hp != null) // far party left
if (farParty != null && farParty.getCallParticipant().isHeld() == false) // far party left
{
synchronized (memberList)
{
......@@ -1419,6 +1446,8 @@ public class RayoComponent extends AbstractComponent
if (memberCount > 2) // conferencing state
{
Log.info( "RayoComponent routeJoinEvent conferenced state " + memberCount);
if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT))
{
UnjoinedEvent event = new UnjoinedEvent(null, conferenceEvent.getConferenceId(), JoinDestinationType.MIXER);
......@@ -1435,38 +1464,63 @@ public class RayoComponent extends AbstractComponent
if (memberCount == 2) // caller with callee only
{
Log.info( "RayoComponent routeJoinEvent answered state " + callId + " " + conferenceEvent.getCallId());
if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT)) // previously conferenced
{
Log.info( "RayoComponent routeJoinEvent someone left ");
if (callId.equals(conferenceEvent.getCallId()) == false) // handset leaving
{
Log.info( "RayoComponent routeJoinEvent handset leaving ");
presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers)));
sendPacket(presence);
} else {
Log.info( "RayoComponent routeJoinEvent far party leaving ");
}
} else {
Log.info( "RayoComponent routeJoinEvent someone joined ");
if (callId.equals(conferenceEvent.getCallId())) // far party joined
{
Log.info( "RayoComponent routeJoinEvent far party joined ");
presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers)));
sendPacket(presence);
} else { // handset joined
Log.info( "RayoComponent routeJoinEvent handset joined ");
if (farParty != null)
{
CallParticipant fp = farParty.getCallParticipant();
if (fp.isHeld())
{
Log.info( "RayoComponent routeJoinEvent on hold ");
fp.setHeld(false);
presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers)));
sendPacket(presence);
} else {
Log.info( "RayoComponent routeJoinEvent not held " + fp.getProtocol() + " " + fp);
if ("WebRtc".equals(fp.getProtocol()) == false)
{
Log.info( "RayoComponent routeJoinEvent handset joing sip call");
presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers)));
sendPacket(presence);
}
}
}
}
sendPacket(presence);
}
} else if (memberCount == 1) { // callee or caller
......@@ -1478,20 +1532,36 @@ public class RayoComponent extends AbstractComponent
if (farParty != null)
{
CallParticipant fp = farParty.getCallParticipant();
fp.setHeld(true);
presence.getElement().add(toXML(new OnHoldEvent()));
if (fp.isHeld())
{
presence.getElement().add(handsetProvider.toXML(new OnHoldEvent()));
sendPacket(presence);
}
}
} else { // far party leaving
if (callParticipant.isHeld())
{
presence.getElement().add(handsetProvider.toXML(new OnHoldEvent()));
sendPacket(presence);
} else {
finishCallRecord(callParticipant);
presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("HANGUP"), headers)));
sendPacket(presence);
}
}
}
} else { // nobody left, call ended
} else { // nobody left, call ended, signal last handset
presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("HANGUP"), headers)));
sendPacket(presence);
finishCallRecord(callParticipant);
//finishCallRecord(callParticipant);
}
}
}
......@@ -1531,7 +1601,7 @@ public class RayoComponent extends AbstractComponent
cp.setCallId(callId);
cp.setMediaPreference("PCMU/8000/1");
cp.setConferenceId(callId);
cp.setConferenceDisplayName(cp.getToPhoneNumber());
ConferenceManager conferenceManager = ConferenceManager.getConference(callId, cp.getMediaPreference(), cp.getToPhoneNumber(), false);
conferenceManager.setCallId(callId);
......@@ -1551,6 +1621,8 @@ public class RayoComponent extends AbstractComponent
try {
Group group = GroupManager.getInstance().getGroup(cp.getToPhoneNumber());
conferenceManager.setGroupName(cp.getToPhoneNumber());
for (JID memberJID : group.getMembers())
{
Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode());
......@@ -1614,55 +1686,61 @@ public class RayoComponent extends AbstractComponent
}
}
private Element toXML(Object object)
{
try {
Document document = DocumentHelper.createDocument();
generateDocument(object, document);
return document.getRootElement();
private IQHandler onHookIQHandler = null;
private IQHandler offHookIQHandler = null;
private IQHandler dialIQHandler = null;
private IQHandler hangupIQHandler = null;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private void generateDocument(Object object, Document document) throws Exception
private void createIQHandlers()
{
if (object instanceof OnHoldEvent) {
createOnHoldEvent((OnHoldEvent) object, document);
XMPPServer server = XMPPServer.getInstance();
} else if (object instanceof OffHoldEvent) {
createOffHoldEvent((OffHoldEvent) object, document);
onHookIQHandler = new OnHookIQHandler(); server.getIQRouter().addHandler(onHookIQHandler);
offHookIQHandler = new OffHookIQHandler(); server.getIQRouter().addHandler(offHookIQHandler);
dialIQHandler = new DialIQHandler(); server.getIQRouter().addHandler(dialIQHandler);
hangupIQHandler = new HangupIQHandler(); server.getIQRouter().addHandler(hangupIQHandler);
}
} else if (object instanceof MutedEvent) {
createMutedEvent((MutedEvent) object, document);
private void destroyIQHandlers()
{
XMPPServer server = XMPPServer.getInstance();
} else if (object instanceof UnmutedEvent) {
createUnmutedEvent((UnmutedEvent) object, document);
}
if (onHookIQHandler != null) {server.getIQRouter().removeHandler(onHookIQHandler); onHookIQHandler = null;}
if (offHookIQHandler != null) {server.getIQRouter().removeHandler(offHookIQHandler); offHookIQHandler = null;}
if (dialIQHandler != null) {server.getIQRouter().removeHandler(dialIQHandler); dialIQHandler = null;}
if (hangupIQHandler != null) {server.getIQRouter().removeHandler(hangupIQHandler); hangupIQHandler = null;}
}
private Document createOnHoldEvent(OnHoldEvent onHold, Document document)
private class OnHookIQHandler extends IQHandler
{
Element root = document.addElement(new QName("onhold", new Namespace("", "urn:xmpp:rayo:1")));
return document;
public OnHookIQHandler() { super("Rayo: XEP 0327 - Onhook");}
@Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) {return null;} }
@Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("onhook", RAYO_HANDSET); }
}
private Document createOffHoldEvent(OffHoldEvent offHold, Document document)
private class OffHookIQHandler extends IQHandler
{
Element root = document.addElement(new QName("offhold", new Namespace("", "urn:xmpp:rayo:1")));
return document;
public OffHookIQHandler() { super("Rayo: XEP 0327 - Offhook");}
@Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}}
@Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("offhook", RAYO_HANDSET); }
}
private Document createMutedEvent(MutedEvent muted, Document document)
private class DialIQHandler extends IQHandler
{
Element root = document.addElement(new QName("onmute", new Namespace("", "urn:xmpp:rayo:1")));
return document;
public DialIQHandler() { super("Rayo: XEP 0327 - Dial");}
@Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}}
@Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("dial", RAYO_CORE); }
}
private Document createUnmutedEvent(UnmutedEvent unmuted, Document document)
private class HangupIQHandler extends IQHandler
{
Element root = document.addElement(new QName("offmute", new Namespace("", "urn:xmpp:rayo:1")));
return document;
public HangupIQHandler() { super("Rayo: XEP 0327 - Hangup");}
@Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}}
@Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("hangup", RAYO_CORE); }
}
}
......@@ -104,6 +104,8 @@ public class RayoPlugin implements Plugin, SessionEventListener {
Log.error("Could NOT load " + component.getName());
}
setup();
component.doStart();
}
private void setup() {
......@@ -191,6 +193,7 @@ public class RayoPlugin implements Plugin, SessionEventListener {
}
closeAllChannels();
executor.shutdownNow();
component.doStop();
}
public boolean hasPublicIP() {
......
......@@ -54,13 +54,13 @@ Strophe.addConnectionPlugin('rayo',
//console.log("hangup " + callId);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("hangup", {xmlns: Strophe.NS.RAYO_CORE});
var iq = $iq({to: callId + "@" + this._connection.domain, from: this._connection.jid, type: "get"}).c("hangup", {xmlns: Strophe.NS.RAYO_CORE});
//console.log(iq.toString());
that._connection.sendIQ(iq, function()
{
this._onhook();
that._onhook();
}, function(error) {
......@@ -109,12 +109,97 @@ Strophe.addConnectionPlugin('rayo',
this._onhook();
},
hold: function(callId)
{
//console.log("hold " + callId);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("hold", {xmlns: Strophe.NS.RAYO_HANDSET});
//console.log(iq.toString());
that._connection.sendIQ(iq, function()
{
that._onhook();
}, function(error) {
//console.log(error);
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("hold failure " + errorcode);
});
});
},
redirect: function(to, mixer, headers)
{
//console.log("redirect " + to + " " + mixer);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c("redirect", {xmlns: Strophe.NS.RAYO_CORE, to: to});
if (headers)
{
var hdrs = Object.getOwnPropertyNames(headers)
for (var i=0; i< hdrs.length; i++)
{
var name = hdrs[i];
var value = headers[name];
if (value) iq.c("header", {name: name, value: value}).up();
}
}
console.log(iq.toString());
that._connection.sendIQ(iq, function(response)
{
$('ref', response).each(function()
{
callId = $(this).attr('id');
if (that._isOffhook()) that._onhook();
if (that.callbacks && that.callbacks.onRedirect) that.callbacks.onRedirect(callId);
});
}, function(error) {
console.log(error);
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("redirect failure " + errorcode);
});
});
},
private: function(callId, flag)
{
//console.log('Rayo plugin private ' + callId + " " + flag);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "private" : "public", {xmlns: Strophe.NS.RAYO_HANDSET});
that._connection.sendIQ(iq, null, function(error)
{
$('error', error).each(function()
{
var errorcode = $(this).attr('code');
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("private/public failure " + errorcode);
});
});
},
mute: function(callId, flag)
{
//console.log('Rayo plugin mute ' + callId + " " + flag);
var that = this;
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "mute" : "unmute", {xmlns: Strophe.NS.RAYO_CORE});
var iq = $iq({to: callId + "@rayo." + this._connection.domain, from: this._connection.jid, type: "get"}).c( flag ? "mute" : "unmute", {xmlns: Strophe.NS.RAYO_HANDSET});
that._connection.sendIQ(iq, null, function(error)
{
......@@ -127,21 +212,23 @@ Strophe.addConnectionPlugin('rayo',
},
answer: function(callId, mixer, headers)
answer: function(callId, mixer, headers, callFrom)
{
//console.log('Rayo plugin accept ' + callId + " " + mixer);
//console.log(headers)
var that = this;
if (this._isOffhook()) this._onhook();
if (!headers) headers = {};
headers.call_id = callId;
//console.log(headers)
this._offhook(mixer, headers, function()
{
var iq = $iq({to: callId + "@rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("answer", {xmlns: Strophe.NS.RAYO_CORE});
if (headers)
{
var hdrs = Object.getOwnPropertyNames(headers)
for (var i=0; i< hdrs.length; i++)
......@@ -151,7 +238,10 @@ Strophe.addConnectionPlugin('rayo',
if (value) iq.c("header", {name: name, value: value}).up();
}
}
iq.c("header", {name: "caller_id", value: callFrom}).up();
iq.c("header", {name: "mixer_name", value: mixer}).up();
//console.log(iq.toString());
......@@ -180,7 +270,7 @@ Strophe.addConnectionPlugin('rayo',
this._offhook(mixer, headers, function()
{
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("dial", {xmlns: Strophe.NS.RAYO_CORE, to: to, from: from});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("dial", {xmlns: Strophe.NS.RAYO_CORE, to: to, from: from});
if (headers)
{
......@@ -208,14 +298,18 @@ Strophe.addConnectionPlugin('rayo',
that.callbacks.onAccept(
{
digit: function(tone) {that.digit(callId, tone);},
redirect: function(to) {that.redirect(to, mixer, headers);},
hangup: function() {that.hangup(callId);},
hold: function() {that.hold(callId);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
from: from,
to: to,
id: callId
id: callId,
privateCall: false
});
}
});
......@@ -256,7 +350,7 @@ Strophe.addConnectionPlugin('rayo',
var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
//console.log(iq.toString())
......@@ -352,8 +446,9 @@ Strophe.addConnectionPlugin('rayo',
var stereo = (headers && headers.stereo_pan) ? headers.stereo_pan : (that.callbacks.stereo_pan ? that.callbacks.stereo_pan : "0");
var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
var group = (headers && headers.group_name) ? headers.group_name : "";
var callid = (headers && headers.call_id) ? headers.call_id : "";
var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, cryptoSuite: that.cryptoSuite, localCrypto: that.localCrypto, remoteCrypto: that.remoteCrypto, codec: codec, stereo: stereo, mixer: mixer, group: group});
var iq = $iq({to: that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, cryptoSuite: that.cryptoSuite, localCrypto: that.localCrypto, remoteCrypto: that.remoteCrypto, codec: codec, stereo: stereo, mixer: mixer, group: group, callid: callid});
//console.log(iq.toString())
......@@ -384,7 +479,7 @@ Strophe.addConnectionPlugin('rayo',
//console.log('Rayo plugin onhook ' + this.handsetId);
that = this;
var server = this.handsetId + "@rayo." + this._connection.domain;
var server = this.handsetId + "@" + this._connection.domain;
this._connection.sendIQ($iq({to: server, from: this._connection.jid, type: "get"}).c('onhook', {xmlns: Strophe.NS.RAYO_HANDSET}), function(response)
{
......@@ -441,15 +536,19 @@ Strophe.addConnectionPlugin('rayo',
var call = {
digit: function(tone) {that.digit(callId, tone);},
redirect: function(to) {that.redirect(to, mixer, headers);},
hangup: function() {that.hangup(callId);},
answer: function() {that.answer(callId, mixer, headers);},
hold: function() {that.hold(callId);},
answer: function() {that.answer(callId, mixer, headers, callFrom);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
from: callFrom,
to: callTo,
id: callId
id: callId,
privateCall: false
}
if (that.callbacks && that.callbacks.onOffer) that.callbacks.onOffer(call, headers);
......@@ -466,6 +565,9 @@ Strophe.addConnectionPlugin('rayo',
if (value) iq.c("header", {name: name, value: value}).up();
}
iq.c("header", {name: "caller_id", value: callFrom}).up();
iq.c("header", {name: "mixer_name", value: mixer}).up();
//console.log(iq.toString());
that._connection.sendIQ(iq, null, function(error)
......@@ -543,7 +645,7 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onHold) that.callbacks.onHold(callId);
......@@ -554,7 +656,7 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onMute) that.callbacks.onMute(callId);
......@@ -565,15 +667,39 @@ Strophe.addConnectionPlugin('rayo',
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.offMute) that.callbacks.offMute(callId);
}
});
$(presence).find('private').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.onPrivate) that.callbacks.onPrivate(callId);
}
});
$(presence).find('public').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_HANDSET)
{
var callId = Strophe.getNodeFromJid(from);
if (that.callbacks && that.callbacks.offPrivate) that.callbacks.offPrivate(callId);
}
});
$(presence).find('ringing').each(function()
{
//console.log(presence);
if ($(this).attr('xmlns') == Strophe.NS.RAYO_CORE)
{
var callId = Strophe.getNodeFromJid(from);
......@@ -612,13 +738,15 @@ Strophe.addConnectionPlugin('rayo',
var call = {
digit: function(tone) {that.digit(callId, tone);},
hangup: function() {that.hangup(callId);},
answer: function() {that.answer(callId, mixer, headers);},
hold: function() {that.hold(callId);},
join: function() {that.join(mixer, headers);},
leave: function() {that.leave(mixer);},
mute: function(flag) {that.mute(callId, flag);},
private: function() {that.private(callId, !this.privateCall);},
id: callId,
from: Strophe.getNodeFromJid(jid)
from: Strophe.getNodeFromJid(jid),
privateCall: false
}
if (that.callbacks && that.callbacks.onHook) that.callbacks.onHook();
if (that.callbacks && that.callbacks.onBusy) that.callbacks.onBusy(call, headers);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment