/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ifsoft.rayo; import org.dom4j.*; import org.jivesoftware.openfire.container.Plugin; import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.SessionManager; 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; 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.*; import org.xmpp.jnodes.nio.LocalIPResolver; import org.xmpp.packet.*; import java.util.*; import java.util.concurrent.*; import java.text.ParseException; import java.net.*; import com.rayo.core.*; import com.rayo.core.verb.*; import com.rayo.core.validation.*; import com.rayo.core.xml.providers.*; import com.sun.voip.server.*; import com.sun.voip.*; import org.voicebridge.*; import com.jcumulus.server.rtmfp.ServerPipelineFactory; import com.jcumulus.server.rtmfp.Sessions; import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory; import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory; import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; public class RayoComponent extends AbstractComponent implements TreatmentDoneListener, 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_RECORD = "urn:xmpp:rayo:record: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"; private static final String ID = "id"; private static final String URI = "uri"; private static final String defaultIncomingConferenceId = "IncomingCallsConference"; public static RayoComponent self; private final RayoPlugin plugin; private RayoProvider rayoProvider = null; private RecordProvider recordProvider = null; private SayProvider sayProvider = null; private HandsetProvider handsetProvider = null; private static ConnectionlessBootstrap bootstrap = null; public static Channel channel = null; private static Sessions sessions; 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_RECORD); recordProvider = new RecordProvider(); recordProvider.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()); createIQHandlers(); try{ Log.info("Starting jCumulus....."); sessions = new Sessions(); ExecutorService executorservice = Executors.newCachedThreadPool(); NioDatagramChannelFactory niodatagramchannelfactory = new NioDatagramChannelFactory(executorservice); bootstrap = new ConnectionlessBootstrap(niodatagramchannelfactory); OrderedMemoryAwareThreadPoolExecutor orderedmemoryawarethreadpoolexecutor = new OrderedMemoryAwareThreadPoolExecutor(10, 0x100000L, 0x40000000L, 100L, TimeUnit.MILLISECONDS, Executors.defaultThreadFactory()); bootstrap.setPipelineFactory(new ServerPipelineFactory(sessions, orderedmemoryawarethreadpoolexecutor)); bootstrap.setOption("reuseAddress", Boolean.valueOf(true)); bootstrap.setOption("sendBufferSize", Integer.valueOf(1215)); bootstrap.setOption("receiveBufferSize", Integer.valueOf(2048)); bootstrap.setOption("receiveBufferSizePredictorFactory", new FixedReceiveBufferSizePredictorFactory(2048)); InetSocketAddress inetsocketaddress = new InetSocketAddress(JiveGlobals.getIntProperty("voicebridge.rtmfp.port", 1935)); Log.info("Listening on " + inetsocketaddress.getPort() + " port"); channel = bootstrap.bind(inetsocketaddress); } catch (Exception e) { Log.error("jCumulus startup failure"); e.printStackTrace(); } } public void doStop() { Log.info("RayoComponent shutdown "); XMPPServer server = XMPPServer.getInstance(); server.getIQDiscoInfoHandler().removeServerFeature(RAYO_CORE); server.getIQDiscoInfoHandler().removeServerFeature(RAYO_RECORD); server.getIQDiscoInfoHandler().removeServerFeature(RAYO_SAY); server.getIQDiscoInfoHandler().removeServerFeature(RAYO_HANDSET); destroyIQHandlers(); Log.info("jCumulus stopping..."); channel.close(); bootstrap.releaseExternalResources(); } public String getName() { return "rayo"; } public String getDescription() { return "XEP-0327: Rayo"; } @Override protected String[] discoInfoFeatureNamespaces() { return new String[]{RAYO_CORE}; } @Override protected String discoInfoIdentityCategoryType() { return "rayo"; } @Override synchronized protected IQ handleIQGet(IQ iq) throws Exception { Log.info("RayoComponent handleIQGet \n" + iq.toString()); final Element element = iq.getChildElement(); final String namespace = element.getNamespaceURI(); try { if (RAYO_HANDSET.equals(namespace)) { IQ reply = null; Object object = handsetProvider.fromXML(element); if (object instanceof OnHookCommand) { OnHookCommand command = (OnHookCommand) object; reply = handleOnOffHookCommand(command, iq); } 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); } else if (object instanceof CreateSpeakerCommand) { reply = handleCreateSpeakerCommand(object, iq); } else if (object instanceof DestroySpeakerCommand) { reply = handleDestroySpeakerCommand(object, iq); } else if (object instanceof PutOnSpeakerCommand) { reply = handleOnOffSpeakerCommand(object, iq, true); } else if (object instanceof TakeOffSpeakerCommand) { reply = handleOnOffSpeakerCommand(object, iq, false); } else if (object instanceof TalkCommand) { reply = handleOnOffTalkCommand(object, iq, false); } else if (object instanceof UntalkCommand) { reply = handleOnOffTalkCommand(object, iq, true); } return reply; } if (RAYO_RECORD.equals(namespace)) { IQ reply = null; Object object = recordProvider.fromXML(element); if (object instanceof Record) { reply = handleRecord((Record) object, iq); } else if (object instanceof PauseCommand) { reply = handlePauseRecordCommand(true, iq); } else if (object instanceof ResumeCommand) { reply = handlePauseRecordCommand(false, iq); } return reply; } if (RAYO_SAY.equals(namespace)) { IQ reply = null; Object object = sayProvider.fromXML(element); if (object instanceof Say) { reply = handleSay((Say) object, iq); } else if (object instanceof PauseCommand) { reply = handlePauseSayCommand(true, iq); } else if (object instanceof ResumeCommand) { reply = handlePauseSayCommand(false, iq); } return reply; } if (RAYO_CORE.equals(namespace)) { IQ reply = null; Object object = rayoProvider.fromXML(element); if (object instanceof JoinCommand) { reply = handleJoinCommand((JoinCommand) object, iq); } else if (object instanceof UnjoinCommand) { reply = handleUnjoinCommand((UnjoinCommand) object, iq); } else if (object instanceof AcceptCommand) { reply = handleAcceptCommand((AcceptCommand) object, iq); } else if (object instanceof AnswerCommand) { reply = handleAnswerCommand((AnswerCommand) object, iq); } else if (object instanceof HangupCommand) { reply = handleHangupCommand(iq); } else if (object instanceof RejectCommand) { // 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, true); } else if (object instanceof DialCommand) { reply = handleDialCommand((DialCommand) object, iq, false); } else if (object instanceof StopCommand) { } else if (object instanceof DtmfCommand) { reply = handleDtmfCommand((DtmfCommand) object, iq); } else if (object instanceof DestroyMixerCommand) { } return reply; } return null; // feature not implemented. } catch (Exception e) { e.printStackTrace(); final IQ reply = IQ.createResultIQ(iq); reply.setError(PacketError.Condition.internal_server_error); return reply; } } 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) { Log.info("RayoComponent handleMuteCommand"); boolean muted = object instanceof MuteCommand; IQ reply = IQ.createResultIQ(iq); String callId = JID.escapeNode(iq.getFrom().toString()); // handset CallHandler handler = CallHandler.findCall(callId); if (handler != null) { handler.setMuted(muted); try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(handler.getCallParticipant().getConferenceId()); 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 handleMuteCommand route event to " + target); if (target != null) { Presence presence = new Presence(); presence.setFrom(callId + "@" + getDomain()); presence.setTo(target); if (muted) { MutedEvent event = new MutedEvent(); presence.getElement().add(handsetProvider.toXML(event)); } else { UnmutedEvent event = new UnmutedEvent(); 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 + "@" + 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); } } } } catch (Exception e) { e.printStackTrace(); } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleOnOffHookCommand(Object object, IQ iq) { Log.info("RayoComponent handleOnOffHookCommand"); IQ reply = IQ.createResultIQ(iq); String handsetId = JID.escapeNode(iq.getFrom().toString()); if (object instanceof OnHookCommand) { CallHandler handler = CallHandler.findCall(handsetId); if (handler != null) { handleOnOffHook(handsetId, object, plugin.getRelayChannel(handsetId), reply); } else { reply.setError(PacketError.Condition.item_not_found); } } else { final Handset handset = ((OffHookCommand) object).getHandset(); if (handset.sipuri == null) // webrtc handset { final RelayChannel channel = plugin.createRelayChannel(iq.getFrom(), handset); if (channel != null) { final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(HOST, LocalIPResolver.getLocalIP()); childElement.addAttribute(LOCAL_PORT, Integer.toString(channel.getPortA())); childElement.addAttribute(REMOTE_PORT, Integer.toString(channel.getPortB())); childElement.addAttribute(ID, channel.getAttachment()); childElement.addAttribute(URI, "xmpp:" + channel.getAttachment() + "@" + getDomain() + "/webrtc"); Log.debug("Created WebRTC handset channel {}:{}, {}:{}, {}:{}", new Object[]{HOST, LocalIPResolver.getLocalIP(), LOCAL_PORT, Integer.toString(channel.getPortA()), REMOTE_PORT, Integer.toString(channel.getPortB())}); handleOnOffHook(handsetId, object, channel, reply); } else { reply.setError(PacketError.Condition.internal_server_error); } } else { // SIP handset final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, handsetId); childElement.addAttribute(URI, handset.sipuri); Log.info("Created SIP handset channel " + handset.sipuri); handleOnOffHook(handsetId, object, null, reply); } } return reply; } private IQ handleOnOffTalkCommand(Object object, IQ iq, boolean mute) { Log.info("RayoComponent handleOnOffTalkCommand"); IQ reply = IQ.createResultIQ(iq); String callId = iq.getTo().getNode(); String speakerId = JID.escapeNode(iq.getFrom().toBareJID() + "/speaker"); String bridgeCallId = "call-" + callId + speakerId; String bridgeSpeakerId = "spkr-" + speakerId + callId; CallHandler callHandler = CallHandler.findCall(bridgeCallId); CallHandler callHandler2 = CallHandler.findCall(bridgeSpeakerId); if (callHandler != null) { CallHandler spkrHandler = CallHandler.findCall(speakerId); if (spkrHandler != null) { MemberReceiver memberReceiver = spkrHandler.getMemberReceiver(); MemberSender memberSender = callHandler.getMemberSender(); if (!mute) { memberReceiver.setChannel(new SpeakerChannel(callHandler.getMemberReceiver())); } else { memberReceiver.setChannel(null); } CallParticipant cp = spkrHandler.getCallParticipant(); cp.setMuted(mute); // mic on/off } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleOnOffSpeakerCommand(Object object, IQ iq, boolean flag) { Log.info("RayoComponent handleOnOffSpeakerCommand"); IQ reply = IQ.createResultIQ(iq); String callId = iq.getTo().getNode(); String speakerId = JID.escapeNode(iq.getFrom().toBareJID() + "/speaker"); CallHandler callHandler = CallHandler.findCall(callId); CallHandler spkrHandler = CallHandler.findCall(speakerId); if (callHandler != null) { if (spkrHandler != null) { Log.info("RayoComponent handleOnOffSpeakerCommand, found call " + callId); bridgeMixers(spkrHandler, callHandler, flag, iq.getFrom()); } } else { // not call, we use callId for mixer name ConferenceManager cm = ConferenceManager.getConference(callId, "PCMU/8000/1", callId, false); if (spkrHandler != null) { Log.info("RayoComponent handleOnOffSpeakerCommand, found conference " + callId); CallParticipant sp = spkrHandler.getCallParticipant(); String spkrMixer = sp.getConferenceId(); String callMixer = callId; bridgeMixers(spkrMixer, speakerId, callMixer, callId, flag, iq.getFrom()); } } return reply; } private void bridgeMixers(CallHandler spkrHandler, CallHandler callHandler, boolean flag, JID from) { Log.info("RayoComponent bridgeMixers"); CallParticipant sp = spkrHandler.getCallParticipant(); CallParticipant cp = callHandler.getCallParticipant(); String callMixer = cp.getConferenceId(); String spkrMixer = sp.getConferenceId(); bridgeMixers(spkrMixer, sp.getCallId(), callMixer, cp.getCallId(), flag, from); } synchronized private void bridgeMixers(String spkrMixer, String speakerId, String callMixer, String callId, boolean flag, JID from) { Log.info("RayoComponent bridgeMixers " + speakerId + " " + callMixer + " " + callId + " " + flag); String bridgeSpeakerId = "spkr-" + speakerId + callId; String bridgeCallId = "call-" + callId + speakerId; CallHandler bridge1 = CallHandler.findCall(bridgeSpeakerId); if (bridge1 != null) bridge1.cancelRequest("Speaker terminated"); CallHandler bridge2 = CallHandler.findCall(bridgeCallId); if (bridge2 != null) bridge2.cancelRequest("Speaker terminated"); if (flag) { synchronized (this) { CallParticipant bp1 = new CallParticipant(); bp1.setCallId(bridgeSpeakerId); bp1.setConferenceId(spkrMixer); bp1.setPhoneNumber(speakerId); bp1.setDisplayName("SPKR"); bp1.setVoiceDetection(false); bp1.setProtocol("Speaker"); bridge1 = new OutgoingCallHandler(this, bp1); CallParticipant bp2 = new CallParticipant(); bp2.setCallId(bridgeCallId); bp2.setConferenceId(callMixer); bp2.setPhoneNumber(speakerId); bp2.setDisplayName("CALL"); bp2.setVoiceDetection(true); bp2.setCallOwner(from.toString()); bp2.setProtocol("Speaker"); bp2.setOtherCall(bridge1); bridge2 = new OutgoingCallHandler(this, bp2); bridge1.start(); try { Thread.sleep(3000); } catch (Exception e) {} bridge2.start(); } } } private IQ handleDestroySpeakerCommand(Object object, IQ iq) { Log.info("RayoComponent handleDestroySpeakerCommand"); IQ reply = IQ.createResultIQ(iq); DestroySpeakerCommand speaker = (DestroySpeakerCommand) object; try { String speakerId = JID.escapeNode(iq.getFrom().toBareJID() + "/speaker"); CallHandler handler = CallHandler.findCall(speakerId); if (handler != null) { killSpeaker(handler); } } catch (Exception e) { e.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } return reply; } private void killSpeaker(CallHandler handler) { Log.info("RayoComponent killSpeaker"); try { handler.cancelRequest("Speaker is destroyed"); CallParticipant cp = handler.getCallParticipant(); String confId = cp.getConferenceId(); handler = null; ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(confId); ArrayList memberList = conferenceManager.getMemberList(); synchronized (memberList) { for (int i = 0; i < memberList.size(); i++) { CallHandler participant = ((ConferenceMember) memberList.get(i)).getCallHandler(); if (participant != null) { participant.cancelRequest("Speaker is destroyed"); participant = null; } } } } catch (Exception e) { e.printStackTrace(); } } private IQ handleCreateSpeakerCommand(Object object, IQ iq) { Log.info("RayoComponent handleCreateSpeakerCommand"); IQ reply = IQ.createResultIQ(iq); CreateSpeakerCommand speaker = (CreateSpeakerCommand) object; try { String speakerId = JID.escapeNode(iq.getFrom().toBareJID() + "/speaker"); String label = iq.getFrom().getNode(); CallHandler handler = CallHandler.findCall(speakerId); if (handler != null) { //killSpeaker(handler); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, speakerId); childElement.addAttribute(URI, "xmpp:" + iq.getFrom().toBareJID() + "/speaker"); return reply; } String mediaPreference = "PCMU/8000/1"; if (speaker.codec == null || "OPUS".equals(speaker.codec)) mediaPreference = "PCM/48000/2"; CallParticipant cp = new CallParticipant(); cp.setCallId(speakerId); cp.setConferenceId(speaker.mixer); cp.setDisplayName("rayo-speaker-" + System.currentTimeMillis()); cp.setName(cp.getDisplayName()); cp.setVoiceDetection(true); cp.setCallOwner(iq.getFrom().toString()); cp.setPhoneNumber(speaker.sipuri); cp.setAutoAnswer(true); cp.setProtocol("SIP"); cp.setMuted(true); // set mic off ConferenceManager cm = ConferenceManager.getConference(speaker.mixer, mediaPreference, label, false); OutgoingCallHandler callHandler = new OutgoingCallHandler(this, cp); callHandler.start(); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, speakerId); childElement.addAttribute(URI, "xmpp:" + iq.getFrom().toBareJID() + "/speaker"); } catch (Exception e) { e.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } return reply; } 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(handsetId); if (handler != null) { handler.cancelRequest("Reseting handset to " + (flag ? "on" : "off") + "hook"); handler = null; } if (!flag) // offhook { Handset handset = ((OffHookCommand) object).getHandset(); String mediaPreference = "PCMU/8000/1"; if (handset.codec == null || "OPUS".equals(handset.codec)) mediaPreference = "PCM/48000/2"; CallParticipant cp = new CallParticipant(); cp.setCallId(handsetId); cp.setConferenceId(handset.mixer); cp.setDisplayName("rayo-handset-" + System.currentTimeMillis()); cp.setName(cp.getDisplayName()); cp.setVoiceDetection(true); cp.setCallOwner(JID.unescapeNode(handsetId)); String label = (new JID(cp.getCallOwner())).getNode(); if (handset.group != null && ! "".equals(handset.group)) { label = handset.group; } ConferenceManager cm = ConferenceManager.getConference(handset.mixer, mediaPreference, label, false); 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) { if (handset.sipuri.indexOf("sip:") == 0) { cp.setPhoneNumber(handset.sipuri); cp.setAutoAnswer(true); cp.setProtocol("SIP"); } else if (handset.sipuri.indexOf("rtmfp:") == 0) { String[] tokens = handset.sipuri.split(":"); if (tokens.length == 3) { cp.setProtocol("Rtmfp"); cp.setRtmfpSendStream(tokens[1]); cp.setRtmfpRecieveStream(tokens[2]); cp.setAutoAnswer(true); } else { reply.setError(PacketError.Condition.not_allowed); return; } } else { reply.setError(PacketError.Condition.not_allowed); return; } } else { cp.setMediaPreference(mediaPreference); cp.setChannel(channel); cp.setProtocol("WebRtc"); } OutgoingCallHandler callHandler = new OutgoingCallHandler(this, cp); callHandler.start(); if (channel != null) { channel.setCallHandler(callHandler); } } else { reply.setError(PacketError.Condition.not_allowed); } } } catch (Exception e) { e.printStackTrace(); } } private IQ handleRecord(Record command, IQ iq) { Log.info("RayoComponent handleRecord " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); final String callId = JID.escapeNode(iq.getFrom().toString()); final String uri = command.getTo().toString(); CallHandler callHandler = CallHandler.findCall(callId); if (callHandler != null) { try { final String fileName = uri.substring(5); // expecting file: prefix callHandler.getCallParticipant().setRecordDirectory(System.getProperty("com.sun.voip.server.Bridge.soundsDirectory", ".")); callHandler.getMemberReceiver().setRecordFromMember(true, fileName, "au"); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, fileName); childElement.addAttribute(URI, (String) uri); } catch (Exception e1) { e1.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handlePauseRecordCommand(boolean flag, IQ iq) { Log.info("RayoComponent handlePauseRecordCommand " + iq.getFrom() + " " + iq.getTo()); IQ reply = IQ.createResultIQ(iq); final String callId = JID.escapeNode(iq.getFrom().toString()); CallHandler callHandler = CallHandler.findCall(callId); if (callHandler != null) { try { CallParticipant cp = callHandler.getCallParticipant(); String fileName = cp.getFromRecordingFile(); cp.setRecordDirectory(System.getProperty("com.sun.voip.server.Bridge.soundsDirectory", ".")); callHandler.getMemberReceiver().setRecordFromMember(flag, fileName, "au"); } catch (Exception e1) { e1.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleSay(Say command, IQ iq) { Log.info("RayoComponent handleSay " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); final String entityId = iq.getTo().getNode(); final String treatmentId = command.getPrompt().getText(); CallHandler callHandler = CallHandler.findCall(entityId); if (callHandler != null) { try { callHandler.playTreatmentToCall(treatmentId, this); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, treatmentId); childElement.addAttribute(URI, (String) "xmpp:" + entityId + "@" + getDomain() + "/" + treatmentId); } catch (Exception e1) { e1.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } } else { // not call, lets try mixer try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(entityId); try { conferenceManager.addTreatment(treatmentId); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(ID, treatmentId); childElement.addAttribute(URI, (String) "xmpp:" + entityId + "@" + getDomain() + "/" + treatmentId); } catch (Exception e2) { e2.printStackTrace(); reply.setError(PacketError.Condition.not_allowed); } } catch (ParseException e1) { reply.setError(PacketError.Condition.item_not_found); } } return reply; } private IQ handlePauseSayCommand(boolean flag, IQ iq) { Log.info("RayoComponent handlePauseSayCommand " + iq.getFrom() + " " + iq.getTo()); IQ reply = IQ.createResultIQ(iq); final JID entityId = getJID(iq.getTo().getNode()); if (entityId != null) { final String treatmentId = entityId.getResource(); final String callId = entityId.getNode(); CallHandler callHandler = CallHandler.findCall(callId); if (callHandler != null) { callHandler.getMember().pauseTreatment(treatmentId, flag); } else { // not call, lets try mixer try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(callId); conferenceManager.getWGManager().pauseConferenceTreatment(treatmentId, flag); } catch (ParseException e1) { reply.setError(PacketError.Condition.item_not_found); } } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleAcceptCommand(AcceptCommand command, IQ iq) { 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 { 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(callerId); setRingingState(presence, ConferenceManager.isTransferCall(mixer), headers); sendPacket(presence); } else reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleAnswerCommand(AnswerCommand command, IQ iq) { 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); 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", callerId); headers.put("call_action", "join"); try { callHandler = CallHandler.findCall(callId); handsetHandler = CallHandler.findCall(JID.escapeNode(callerId)); if (handsetHandler != null) { CallParticipant hp = handsetHandler.getCallParticipant(); Presence presence1 = new Presence(); //to caller presence1.setFrom(iq.getTo()); presence1.setTo(callerId); setAnsweredState(presence1, ConferenceManager.isTransferCall(hp.getConferenceId()), headers); sendPacket(presence1); } } catch (Exception e) { reply.setError(PacketError.Condition.item_not_found); e.printStackTrace(); } } else { callHandler = CallHandler.findCall(callId); // SIP call; handsetHandler = CallHandler.findCall(JID.escapeNode(iq.getFrom().toString())); } if (callHandler != null && handsetHandler != null) { CallParticipant cp = callHandler.getCallParticipant(); CallParticipant hp = handsetHandler.getCallParticipant(); 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.setHeaders(headers); String recording = cp.getConferenceId() + "-" + cp.getStartTimestamp() + ".au"; ConferenceManager.recordConference(cp.getConferenceId(), true, recording, "au"); String destination = iq.getFrom().getNode(); String source = cp.getName(); if (callJID != null) { source = (new JID(callerId)).getNode(); Config.createCallRecord(source, recording, "xmpp:" + iq.getFrom(), cp.getStartTimestamp(), 0, "dialed") ; Config.createCallRecord(destination, recording, "xmpp:" + callerId, cp.getStartTimestamp(), 0, "received"); sendMessage(new JID(callerId), iq.getFrom(), "Call started", recording, "chat"); } 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, "chat"); } } 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); return reply; } private IQ handleHangupCommand(IQ iq) { String callId = iq.getTo().getNode(); Log.info("RayoComponent handleHangupCommand " + iq.getFrom() + " " + callId); IQ reply = IQ.createResultIQ(iq); CallHandler callHandler = CallHandler.findCall(callId); if (callHandler != null) { Log.info("RayoComponent handleHangupCommand found callhandler " + callId); CallParticipant cp = callHandler.getCallParticipant(); try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(cp.getConferenceId()); Log.info("RayoComponent handleHangupCommand one person left, cancel call " + conferenceManager.getMemberList().size()); if (conferenceManager.getMemberList().size() <= 2) { CallHandler.hangup(callId, "User requested call termination"); } } catch (Exception e) {} } else { //reply.setError(PacketError.Condition.item_not_found); } return reply; } private JID getJID(String jid) { if (jid != null) { jid = JID.unescapeNode(jid); if (jid.indexOf("@") == -1 || jid.indexOf("/") == -1) return null; try { return new JID(jid); } catch (Exception e) { return null; } } else return null; } private IQ handleDtmfCommand(DtmfCommand command, IQ iq) { Log.info("RayoComponent handleDtmfCommand " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); try { CallHandler callHandler = CallHandler.findCall(iq.getTo().getNode()); callHandler.dtmfKeys(command.getTones()); } catch (NoSuchElementException e) { reply.setError(PacketError.Condition.item_not_found); } return reply; } private IQ handleJoinCommand(JoinCommand command, IQ iq) { Log.info("RayoComponent handleJoinCommand " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); String mixer = null; if (command.getType() == JoinDestinationType.CALL) { // TODO join.getTo() } else { mixer = command.getTo(); } if (mixer != null) { try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(mixer); if (CallHandler.findCall("colibri-" + mixer) == null) // other participant than colibri { if (conferenceManager.getMemberList().size() == 1) // handset already in call { String recording = mixer + "-" + System.currentTimeMillis() + ".au"; conferenceManager.recordConference(true, recording, "au"); sendMucMessage(mixer, recording, iq.getFrom(), "started voice recording"); } } sendMucMessage(mixer, null, iq.getFrom(), iq.getFrom().getNode() + " joined voice conversation"); } catch (ParseException pe) { // colibri joining as first participant try { ConferenceManager conferenceManager = ConferenceManager.getConference(mixer, "PCM/48000/2", mixer, false); String recording = mixer + "-" + System.currentTimeMillis() + ".au"; conferenceManager.recordConference(true, recording, "au"); sendMucMessage(mixer, recording, iq.getFrom(), "started voice recording"); } catch (Exception e) { reply.setError(PacketError.Condition.item_not_found); } } catch (Exception e) { reply.setError(PacketError.Condition.item_not_found); } } else { reply.setError(PacketError.Condition.feature_not_implemented); } return reply; } private IQ handleUnjoinCommand(UnjoinCommand command, IQ iq) { Log.info("RayoComponent handleUnjoinCommand " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); String mixer = null; if (command.getType() == JoinDestinationType.CALL) { // TODO join.getFrom() } else { mixer = command.getFrom(); } if (mixer != null) { try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(mixer); if (conferenceManager.getMemberList().size() == 1) { conferenceManager.recordConference(false, null, null); sendMucMessage(mixer, null, iq.getFrom(), "stopped voice recording"); } sendMucMessage(mixer, null, iq.getFrom(), iq.getFrom().getNode() + " left voice conversation"); } catch (Exception e) { reply.setError(PacketError.Condition.item_not_found); } } else { reply.setError(PacketError.Condition.feature_not_implemented); } return reply; } private void attachVideobridge(String conferenceId, JID participant, String mediaPreference) { //if (XMPPServer.getInstance().getPluginManager().getPlugin("jitsivideobridge") != null) //{ Log.info("attachVideobridge Found Jitsi Videobridge, attaching.." + conferenceId); if (XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").hasChatRoom(conferenceId)) { MUCRoom room = XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").getChatRoom(conferenceId); if (room != null) { for (MUCRole role : room.getOccupants()) { if (participant.toBareJID().equals(role.getUserAddress().toBareJID())) { Log.info("attachVideobridge Found participant " + participant.toBareJID()); try { CallParticipant vp = new CallParticipant(); vp.setCallId("colibri-" + conferenceId); vp.setCallOwner(participant.toString()); vp.setProtocol("Videobridge"); vp.setPhoneNumber(participant.getNode()); vp.setMediaPreference(mediaPreference); vp.setConferenceId(conferenceId); OutgoingCallHandler videoBridgeHandler = new OutgoingCallHandler(null, vp); videoBridgeHandler.start(); } catch (Exception e) { e.printStackTrace(); } break; } } } } //} } private void detachVideobridge(String conferenceId) { try { Log.info("Jitsi Videobridge, detaching.." + conferenceId); CallHandler callHandler = CallHandler.findCall("colibri-" + conferenceId); if (callHandler != null) { CallHandler.hangup("colibri-" + conferenceId, "Detaching from Jitsi Videobridge"); } } catch (Exception e) { e.printStackTrace(); } } private boolean isMixerMuc(String mixer) { Log.info("RayoComponent isMixerMuc " + mixer); boolean isMuc = false; try { if (XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").hasChatRoom(mixer)) { isMuc = null != XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").getChatRoom(mixer); } } catch (Exception e) {} return isMuc; } private void sendMucMessage(String mixer, String recording, JID participant, String message) { if (isMixerMuc(mixer)) // not working properly. sending only to single person { sendMessage(new JID(mixer + "@conference." + getDomain()), participant, message, recording, "groupchat"); } } private IQ handleDialCommand(DialCommand command, IQ iq, boolean transferCall) { Log.info("RayoComponent handleHandsetDialCommand " + iq.getFrom()); IQ reply = IQ.createResultIQ(iq); Map<String, String> headers = command.getHeaders(); String from = command.getFrom().toString(); String to = command.getTo().toString(); boolean toPhone = to.indexOf("sip:") == 0 || to.indexOf("tel:") == 0; boolean toXmpp = to.indexOf("xmpp:") == 0; String callerName = headers.get("caller_name"); String calledName = headers.get("called_name"); String handsetId = iq.getFrom().toString(); JoinCommand join = command.getJoin(); if (join != null) { if (join.getType() == JoinDestinationType.CALL) { // TODO join.getTo() } else { } reply.setError(PacketError.Condition.feature_not_implemented); } else { if (callerName == null) { callerName = iq.getFrom().getNode(); headers.put("caller_name", callerName); } if (toPhone) { if (calledName == null) { calledName = to; headers.put("called_name", calledName); } CallParticipant cp = new CallParticipant(); cp.setVoiceDetection(true); cp.setCallOwner(handsetId); cp.setProtocol("SIP"); cp.setDisplayName(callerName); cp.setPhoneNumber(to); cp.setName(calledName); cp.setHeaders(headers); reply = doPhoneAndPcCall(JID.escapeNode(handsetId), cp, reply, transferCall); } else if (toXmpp){ headers.put("call_protocol", "XMPP"); JID destination = getJID(to.substring(5)); if (destination != null) { String source = JID.escapeNode(handsetId); CallHandler handsetHandler = CallHandler.findCall(source); if (handsetHandler != null) { CallParticipant hp = handsetHandler.getCallParticipant(); headers.put("mixer_name", hp.getConferenceId()); headers.put("codec_name", "PCM/48000/2".equals(hp.getMediaPreference()) ? "OPUS" : "PCMU"); try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(hp.getConferenceId()); conferenceManager.setTransferCall(transferCall); } catch (Exception e) {} if (findUser(destination.getNode()) != null) { routeXMPPCall(reply, destination, source, calledName, headers, hp.getConferenceId()); } else { int count = 0; try { Group group = GroupManager.getInstance().getGroup(destination.getNode()); for (JID memberJID : group.getMembers()) { if (iq.getFrom().toBareJID().equals(memberJID.toBareJID()) == false) { Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode()); for (ClientSession session : sessions) { routeXMPPCall(reply, session.getAddress(), source, calledName, headers, hp.getConferenceId()); count++; } } } } catch (GroupNotFoundException e) { if (XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").hasChatRoom(destination.getNode())) { MUCRoom room = XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").getChatRoom(destination.getNode()); if (room != null) { for (MUCRole role : room.getOccupants()) { if (iq.getFrom().toBareJID().equals(role.getUserAddress().toBareJID()) == false) { routeXMPPCall(reply, role.getUserAddress(), source, calledName, headers, hp.getConferenceId()); count++; } } } } else { reply.setError(PacketError.Condition.item_not_found); } } if (count == 0) { reply.setError(PacketError.Condition.item_not_found); } } } else { reply.setError(PacketError.Condition.item_not_found); } } else { reply.setError(PacketError.Condition.item_not_found); } } else { reply.setError(PacketError.Condition.feature_not_implemented); } } return reply; } 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(callId + "@" + getDomain()); presence.setTo(destination); OfferEvent offer = new OfferEvent(null); try { offer.setFrom(new URI("xmpp:" + JID.unescapeNode(source))); offer.setTo(new URI("xmpp:" + destination)); } catch (URISyntaxException e) { reply.setError(PacketError.Condition.feature_not_implemented); return; } if (calledName == null) { calledName = presence.getTo().getNode(); headers.put("called_name", calledName); } 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); } private IQ doPhoneAndPcCall(String handsetId, CallParticipant cp, IQ reply, boolean transferCall) { Log.info("RayoComponent doPhoneAndPcCall " + handsetId); CallHandler handsetHandler = CallHandler.findCall(handsetId); if (handsetHandler != null) { try { setMixer(handsetHandler, reply, cp, transferCall); OutgoingCallHandler outgoingCallHandler = new OutgoingCallHandler(this, cp); //outgoingCallHandler.setOtherCall(handsetHandler); //handsetHandler.setOtherCall(outgoingCallHandler); outgoingCallHandler.start(); final Element childElement = reply.setChildElement("ref", RAYO_CORE); childElement.addAttribute(URI, (String) "xmpp:" + cp.getCallId() + "@" + getDomain()); childElement.addAttribute(ID, (String) cp.getCallId()); } catch (Exception e) { e.printStackTrace(); reply.setError(PacketError.Condition.internal_server_error); } } else { reply.setError(PacketError.Condition.item_not_found); } return reply; } private void setMixer(CallHandler handsetHandler, IQ reply, CallParticipant cp, boolean transferCall) { CallParticipant hp = handsetHandler.getCallParticipant(); try { hp.setFarParty(cp); cp.setHandset(hp); long start = System.currentTimeMillis(); cp.setStartTimestamp(start); hp.setStartTimestamp(start); String mixer = hp.getConferenceId(); ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(mixer); cp.setConferenceId(mixer); cp.setCallId(mixer); cp.setMediaPreference(hp.getMediaPreference()); conferenceManager.setCallId(mixer); conferenceManager.setTransferCall(transferCall); 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, "chat"); } catch (ParseException e1) { reply.setError(PacketError.Condition.internal_server_error); } } @Override public String getDomain() { return XMPPServer.getInstance().getServerInfo().getXMPPDomain(); } public HandsetProvider getHandsetProvider() { return handsetProvider; } public void treatmentDoneNotification(TreatmentManager treatmentManager) { Log.info("RayoComponent treatmentDoneNotification " + treatmentManager.getId()); } public void callEventNotification(com.sun.voip.CallEvent callEvent) { Log.info("RayoComponent callEventNotification " + callEvent); JID from = getJID(callEvent.getCallInfo()); if (from != null) { String myEvent = com.sun.voip.CallEvent.getEventString(callEvent.getEvent()); String callState = callEvent.getCallState().toString(); try { CallHandler callHandler = CallHandler.findCall(callEvent.getCallId()); if (callHandler != null) { Log.info("RayoComponent callEventNotification found call handler " + callHandler); CallParticipant cp = callHandler.getCallParticipant(); CallParticipant hp = cp.getHandset(); if (cp != null) { Log.info("RayoComponent callEventNotification found call paticipant " + cp); Map<String, String> headers = cp.getHeaders(); headers.put("mixer_name", callEvent.getConferenceId()); headers.put("call_protocol", cp.getProtocol()); Presence presence = new Presence(); presence.setFrom(callEvent.getCallId() + "@" + getDomain()); presence.setTo(from); if ("001 STATE CHANGED".equals(myEvent)) { if ("100 INVITED".equals(callState)) { if (cp.isAutoAnswer() == false) // SIP handset, no ringing event { setRingingState(presence, ConferenceManager.isTransferCall(callEvent.getConferenceId()), headers); sendPacket(presence); } } else if ("200 ESTABLISHED".equals(callState)) { } else if ("299 ENDED".equals(callState)) { /* if (callEvent.getCallId().indexOf("2fspeaker") > -1 && callEvent.getInfo().indexOf("Reason='System shutdown'") == -1) { CallParticipant cp2 = new CallParticipant(); cp2.setCallId(cp.getCallId()); cp2.setConferenceId(cp.getConferenceId()); cp2.setDisplayName(cp.getDisplayName()); cp2.setName(cp.getDisplayName()); cp2.setCallOwner(cp.getCallOwner()); cp2.setPhoneNumber(cp.getPhoneNumber()); cp2.setVoiceDetection(true); cp2.setAutoAnswer(true); cp2.setProtocol("SIP"); cp2.setMuted(true); // set mic off OutgoingCallHandler callHandlerNew = new OutgoingCallHandler(this, cp2); callHandlerNew.start(); } */ } } else if ("250 STARTED SPEAKING".equals(myEvent)) { broadcastSpeaking(true, callEvent.getCallId(), callEvent.getConferenceId(), from); } else if ("259 STOPPED SPEAKING".equals(myEvent)) { broadcastSpeaking(false, callEvent.getCallId(), callEvent.getConferenceId(), from); } else if ("269 DTMF".equals(myEvent)) { presence.getElement().add(rayoProvider.toXML(new DtmfEvent(callEvent.getCallId(), callEvent.getDtmfKey()))); sendPacket(presence); } else if ("230 TREATMENT DONE".equals(myEvent)) { presence.setFrom(callEvent.getCallId() + "@" + getDomain() + "/" + callEvent.getTreatmentId()); SayCompleteEvent complete = new SayCompleteEvent(); complete.setReason(SayCompleteEvent.Reason.valueOf("SUCCESS")); presence.getElement().add(sayProvider.toXML(complete)); sendPacket(presence); } } } } catch (Exception e) { e.printStackTrace(); } } } private void broadcastSpeaking(Boolean startSpeaking, String callId, String conferenceId, JID from) { Log.info( "RayoComponent broadcastSpeaking " + startSpeaking + " " + callId + " " + conferenceId + " " + from); try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(conferenceId); ArrayList memberList = conferenceManager.getMemberList(); //sendMucMessage(conferenceId, null, from, from.getNode() + (startSpeaking ? " started" : " stopped") + " speaking"); synchronized (memberList) { for (int i = 0; i < memberList.size(); i++) { ConferenceMember member = (ConferenceMember) memberList.get(i); CallHandler callHandler = member.getCallHandler(); if (callHandler != null) { CallParticipant cp = callHandler.getCallParticipant(); String target = cp.getCallOwner(); Log.info( "RayoComponent broadcastSpeaking checking " + target); if (target != null && target.equals(from.toString()) == false) { Presence presence = new Presence(); presence.setFrom(conferenceId + "@" + getDomain()); presence.setTo(target); if (startSpeaking) { StartedSpeakingEvent speaker = new StartedSpeakingEvent(); speaker.setSpeakerId(JID.escapeNode(from.toString())); presence.getElement().add(rayoProvider.toXML(speaker)); } else { StoppedSpeakingEvent speaker = new StoppedSpeakingEvent(); speaker.setSpeakerId(JID.escapeNode(from.toString())); presence.getElement().add(rayoProvider.toXML(speaker)); } sendPacket(presence); } } } } } catch (Exception e) { e.printStackTrace(); } } private void finishCallRecord(CallParticipant cp) { Log.info( "RayoComponent finishCallRecord " + cp.getStartTimestamp()); if (cp.getStartTimestamp() > 0) { cp.setEndTimestamp(System.currentTimeMillis()); Config.updateCallRecord(cp.getStartTimestamp(), (int)((cp.getEndTimestamp() - cp.getStartTimestamp()) / 1000)); try { ConferenceManager conferenceManager = ConferenceManager.findConferenceManager(cp.getConferenceId()); conferenceManager.recordConference(false, null, null); String target = cp.getCallOwner(); JID destination = getJID(conferenceManager.getCallId()); if (destination == null) { destination = new JID(conferenceManager.getCallId() + "@" + getDomain()); } if (target == null) { if (cp.getHandset() != null) { target = cp.getHandset().getCallOwner(); } } if (target != null) { try { if (target.equals(destination.toString()) == false) { sendMessage(new JID(target), destination, "Call ended", null, "chat"); } } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) {} cp.setStartTimestamp(0); } } private void sendMessage(JID from, JID to, String body, String fileName, String type) { Log.info( "RayoComponent sendMessage " + from + " " + to + " " + body + " " + fileName); int port = HttpBindManager.getInstance().getHttpBindUnsecurePort(); Message packet = new Message(); packet.setTo(to); packet.setFrom(from); packet.setType("chat".equals(type) ? Message.Type.chat : Message.Type.groupchat); if (fileName != null) { String url = "http://" + getDomain() + ":" + port + "/rayo/recordings/" + fileName; packet.setThread(url); body = body + " " + url; } packet.setBody(body); sendPacket(packet); } public void sendPacket(Packet packet) { try { ComponentManagerFactory.getComponentManager().sendPacket(this, packet); } catch (Exception e) { Log.error("RayoComponent sendPacket " + e); e.printStackTrace(); } } public void notifyConferenceMonitors(ConferenceEvent conferenceEvent) { Log.info( "RayoComponent notifyConferenceMonitors " + conferenceEvent.toString()); if (defaultIncomingConferenceId.equals(conferenceEvent.getConferenceId())) return; ConferenceManager conferenceManager = null; try { if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT) || conferenceEvent.equals(ConferenceEvent.MEMBER_JOINED)) { Log.info("RayoComponent notifyConferenceMonitors looking for call " + conferenceEvent.getCallId() + " " + conferenceEvent.getMemberCount()); try { conferenceManager = ConferenceManager.findConferenceManager(conferenceEvent.getConferenceId()); } catch (Exception e) {} if (conferenceManager != null) { String groupName = conferenceManager.getGroupName(); String callId = conferenceManager.getCallId(); if (callId == null) callId = conferenceEvent.getConferenceId(); // special case of SIP incoming CallHandler farParty = CallHandler.findCall(callId); CallHandler callHandler = CallHandler.findCall(conferenceEvent.getCallId()); if (callHandler != null) { Log.info("RayoComponent notifyConferenceMonitors found call handler " + callHandler + " " + farParty); CallParticipant callParticipant = callHandler.getCallParticipant(); ArrayList memberList = conferenceManager.getMemberList(); if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT) && callId.equals(conferenceEvent.getCallId())) { if (farParty != null && farParty.getCallParticipant().isHeld() == false) // far party left { synchronized (memberList) { for (int i = 0; i < memberList.size(); i++) { CallHandler participant = ((ConferenceMember) memberList.get(i)).getCallHandler(); participant.cancelRequest("Far Party has left"); } } } } int memberCount = memberList.size(); /* When mixer is an muc, assume a conference call just sent join/unjoin When mixer is a group, assume a third party call, inform group members */ if (groupName == null) { if (isMixerMuc(conferenceEvent.getConferenceId())) { MUCRoom room = XMPPServer.getInstance().getMultiUserChatManager().getMultiUserChatService("conference").getChatRoom(conferenceEvent.getConferenceId()); Log.info("RayoComponent notifyConferenceMonitors routing to room occupants of " + conferenceEvent.getConferenceId()); for ( MUCRole role : room.getOccupants()) { String jid = role.getUserAddress().toString(); Log.info("RayoComponent notifyConferenceMonitors routing to room occupant " + jid); Presence presence = new Presence(); presence.setFrom(conferenceEvent.getCallId() + "@" + getDomain()); presence.setTo(jid); if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT)) { UnjoinedEvent event = new UnjoinedEvent(null, conferenceEvent.getConferenceId(), JoinDestinationType.MIXER); presence.getElement().add(rayoProvider.toXML(event)); } else { JoinedEvent event = new JoinedEvent(null, conferenceEvent.getConferenceId(), JoinDestinationType.MIXER); presence.getElement().add(rayoProvider.toXML(event)); } sendPacket(presence); } } else { Log.info("RayoComponent notifyConferenceMonitors routing to owner " + callParticipant.getCallOwner() + " " + memberCount); routeJoinEvent(callParticipant.getCallOwner(), callParticipant, conferenceEvent, memberCount, groupName, callId, farParty, conferenceManager); } } else { Group group = GroupManager.getInstance().getGroup(groupName); for (JID memberJID : group.getMembers()) { Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode()); for (ClientSession session : sessions) { routeJoinEvent(session.getAddress().toString(), callParticipant, conferenceEvent, memberCount, groupName, callId, farParty, conferenceManager); } } } if (memberCount == 0 && conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT)) { conferenceManager.recordConference(false, null, null); conferenceManager.endConference(conferenceEvent.getConferenceId()); CallParticipant heldCall = conferenceManager.getHeldCall(); if (heldCall != null) { JID target = getJID(heldCall.getCallId()); if (target != null) { Presence presence = new Presence(); presence.setFrom(callId + "@" + getDomain()); presence.setTo(target); presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("HANGUP"), callParticipant.getHeaders()))); sendPacket(presence); } } } else if (memberCount == 2) { conferenceManager.setTransferCall(false); // reset after informing on redirect } } } } } catch (Exception e) { Log.error( "RayoComponent Error in notifyConferenceMonitors " + e); e.printStackTrace(); } } private void routeJoinEvent(String callee, CallParticipant callParticipant, ConferenceEvent conferenceEvent, int memberCount, String groupName, String callId, CallHandler farParty, ConferenceManager conferenceManager) { Log.info( "RayoComponent routeJoinEvent " + callee + " " + callId + " " + groupName + " " + memberCount + " " + farParty); if (callee == null) return; Presence presence = new Presence(); presence.setFrom(callId + "@" + getDomain()); presence.setTo(callee); Map<String, String> headers = callParticipant.getHeaders(); headers.put("call_owner", callParticipant.getCallOwner()); headers.put("call_action", conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT) ? "leave" : "join"); headers.put("call_protocol", callParticipant.getProtocol()); headers.put("mixer_name", conferenceEvent.getConferenceId()); headers.put("group_name", groupName); 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); presence.getElement().add(rayoProvider.toXML(event)); } else { JoinedEvent event = new JoinedEvent(null, conferenceEvent.getConferenceId(), JoinDestinationType.MIXER); presence.getElement().add(rayoProvider.toXML(event)); } sendPacket(presence); } else { 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 "); setAnsweredState(presence, conferenceManager.isTransferCall(), 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 "); setAnsweredState(presence, conferenceManager.isTransferCall(), 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); conferenceManager.setHeldCall(null); setAnsweredState(presence, conferenceManager.isTransferCall(), 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"); setAnsweredState(presence, conferenceManager.isTransferCall(), headers); sendPacket(presence); } } } } } } else if (memberCount == 1) { // callee or caller if (conferenceEvent.equals(ConferenceEvent.MEMBER_LEFT)) { Log.info( "RayoComponent routeJoinEvent only one person left"); if (callId.equals(conferenceEvent.getCallId()) == false) // handset leaving { if (farParty != null) { Log.info( "RayoComponent routeJoinEvent handset leaving call " + farParty.getCallParticipant()); CallParticipant fp = farParty.getCallParticipant(); if (callParticipant.isAutoAnswer()) fp.setHeld(true); // sip phone as handset hangup if (fp.isHeld()) { Log.info( "RayoComponent routeJoinEvent call held with " + callParticipant); presence.getElement().add(handsetProvider.toXML(new OnHoldEvent())); sendPacket(presence); conferenceManager.setHeldCall(callParticipant); } } } else { // far party leaving Log.info( "RayoComponent routeJoinEvent far party leaving call " + callParticipant); if (callParticipant.isHeld()) { Log.info( "RayoComponent routeJoinEvent call held with " + farParty); presence.getElement().add(handsetProvider.toXML(new OnHoldEvent())); sendPacket(presence); conferenceManager.setHeldCall(farParty.getCallParticipant()); } else { finishCallRecord(callParticipant); presence.getElement().add(rayoProvider.toXML(new EndEvent(null, EndEvent.Reason.valueOf("HANGUP"), headers))); sendPacket(presence); } } } } 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); } } } private void setAnsweredState(Presence presence, boolean isTransfer, Map<String, String> headers) { if (isTransfer) { presence.getElement().add(handsetProvider.toXML(new TransferredEvent())); } else { presence.getElement().add(rayoProvider.toXML(new AnsweredEvent(null, headers))); } } private void setRingingState(Presence presence, boolean isTransfer, Map<String, String> headers) { if (isTransfer) { presence.getElement().add(handsetProvider.toXML(new TransferringEvent())); } else { presence.getElement().add(rayoProvider.toXML(new RingingEvent(null, headers))); } } private JID findUser(String username) { Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(); JID foundUser = null; for (ClientSession session : sessions) { try{ String userId = session.getAddress().getNode(); if (username.equals(userId)) { Log.info("Incoming SIP, findUser " + session.getAddress()); foundUser = session.getAddress(); break; } } catch (Exception e) { } } return foundUser; } public boolean routeIncomingSIP(CallParticipant cp) { boolean canRoute = false; Group group = null; JID foundUser = findUser(cp.getToPhoneNumber()); if (foundUser != null) canRoute = true; else { try { group = GroupManager.getInstance().getGroup(cp.getToPhoneNumber()); canRoute = true; } catch (GroupNotFoundException e) { } } Log.info("Incoming SIP, call route to entity " + cp.getToPhoneNumber() + " " + canRoute); if (canRoute) { String callId = "rayo-incoming-" + System.currentTimeMillis(); cp.setCallId(callId); cp.setConferenceId(callId); if (cp.getMediaPreference() == null) cp.setMediaPreference("PCMU/8000/1"); // regular phone ConferenceManager conferenceManager = ConferenceManager.getConference(callId, cp.getMediaPreference(), cp.getToPhoneNumber(), false); conferenceManager.setCallId(callId); Map<String, String> headers = cp.getHeaders(); headers.put("mixer_name", callId); headers.put("call_protocol", "SIP"); headers.put("codec_name", "PCM/48000/2".equals(cp.getMediaPreference()) ? "OPUS" : "PCMU"); headers.put("group_name", cp.getToPhoneNumber()); if (foundUser != null) // send this call to specific user { cp.setCallOwner(foundUser.toString()); routeSIPCall(foundUser, cp, callId, headers); } else { conferenceManager.setGroupName(cp.getToPhoneNumber()); for (JID memberJID : group.getMembers()) { Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(memberJID.getNode()); for (ClientSession session : sessions) { routeSIPCall(session.getAddress(), cp, callId, headers); } } } } return canRoute; } public void routeSIPCall(JID callee, CallParticipant cp, String callId, Map<String, String> headers) { Log.info("routeSIPCall to user " + callee); if (callee != null) // send this call to user { Presence presence = new Presence(); presence.setFrom(callId + "@" + getDomain()); presence.setTo(callee); OfferEvent offer = new OfferEvent(null); try { offer.setTo(new URI("xmpp:" + callee.toString())); offer.setFrom(new URI("sip:" + cp.getPhoneNumber())); } catch (URISyntaxException e) { Log.error("SIP phone nos not URI " + cp.getPhoneNumber() + " " + callee); } headers.put("called_name", callee.getNode()); headers.put("caller_name", cp.getName()); offer.setHeaders(headers); presence.getElement().add(rayoProvider.toXML(offer)); sendPacket(presence); } } private IQHandler onHookIQHandler = null; private IQHandler offHookIQHandler = null; private IQHandler privateIQHandler = null; private IQHandler publicIQHandler = null; private IQHandler muteIQHandler = null; private IQHandler unmuteIQHandler = null; private IQHandler holdIQHandler = null; private IQHandler sayIQHandler = null; private IQHandler pauseSayIQHandler = null; private IQHandler resumeSayIQHandler = null; private IQHandler recordIQHandler = null; private IQHandler pauseRecordIQHandler = null; private IQHandler resumeRecordIQHandler = null; private IQHandler acceptIQHandler = null; private IQHandler answerIQHandler = null; private IQHandler dialIQHandler = null; private IQHandler hangupIQHandler = null; private IQHandler redirectIQHandler = null; private IQHandler dtmfIQHandler = null; private void createIQHandlers() { XMPPServer server = XMPPServer.getInstance(); onHookIQHandler = new OnHookIQHandler(); server.getIQRouter().addHandler(onHookIQHandler); offHookIQHandler = new OffHookIQHandler(); server.getIQRouter().addHandler(offHookIQHandler); privateIQHandler = new PrivateIQHandler(); server.getIQRouter().addHandler(privateIQHandler); publicIQHandler = new PublicIQHandler(); server.getIQRouter().addHandler(publicIQHandler); muteIQHandler = new MuteIQHandler(); server.getIQRouter().addHandler(muteIQHandler); unmuteIQHandler = new UnmuteIQHandler(); server.getIQRouter().addHandler(unmuteIQHandler); holdIQHandler = new HoldIQHandler(); server.getIQRouter().addHandler(holdIQHandler); recordIQHandler = new RecordIQHandler(); server.getIQRouter().addHandler(recordIQHandler); pauseRecordIQHandler = new PauseRecordIQHandler(); server.getIQRouter().addHandler(pauseRecordIQHandler); resumeRecordIQHandler = new ResumeRecordIQHandler(); server.getIQRouter().addHandler(resumeRecordIQHandler); sayIQHandler = new SayIQHandler(); server.getIQRouter().addHandler(sayIQHandler); pauseSayIQHandler = new PauseSayIQHandler(); server.getIQRouter().addHandler(pauseSayIQHandler); resumeSayIQHandler = new ResumeSayIQHandler(); server.getIQRouter().addHandler(resumeSayIQHandler); acceptIQHandler = new AcceptIQHandler(); server.getIQRouter().addHandler(acceptIQHandler); answerIQHandler = new AnswerIQHandler(); server.getIQRouter().addHandler(answerIQHandler); dialIQHandler = new DialIQHandler(); server.getIQRouter().addHandler(dialIQHandler); hangupIQHandler = new HangupIQHandler(); server.getIQRouter().addHandler(hangupIQHandler); redirectIQHandler = new RedirectIQHandler(); server.getIQRouter().addHandler(redirectIQHandler); dtmfIQHandler = new DtmfIQHandler(); server.getIQRouter().addHandler(dtmfIQHandler); } private void destroyIQHandlers() { XMPPServer server = XMPPServer.getInstance(); if (onHookIQHandler != null) {server.getIQRouter().removeHandler(onHookIQHandler); onHookIQHandler = null;} if (offHookIQHandler != null) {server.getIQRouter().removeHandler(offHookIQHandler); offHookIQHandler = null;} if (privateIQHandler != null) {server.getIQRouter().removeHandler(privateIQHandler); privateIQHandler = null;} if (publicIQHandler != null) {server.getIQRouter().removeHandler(publicIQHandler); publicIQHandler = null;} if (muteIQHandler != null) {server.getIQRouter().removeHandler(muteIQHandler); muteIQHandler = null;} if (unmuteIQHandler != null) {server.getIQRouter().removeHandler(unmuteIQHandler); unmuteIQHandler = null;} if (holdIQHandler != null) {server.getIQRouter().removeHandler(holdIQHandler); holdIQHandler = null;} if (sayIQHandler != null) {server.getIQRouter().removeHandler(sayIQHandler); sayIQHandler = null;} if (pauseSayIQHandler != null) {server.getIQRouter().removeHandler(pauseSayIQHandler); pauseSayIQHandler = null;} if (resumeSayIQHandler != null) {server.getIQRouter().removeHandler(resumeSayIQHandler); resumeSayIQHandler = null;} if (acceptIQHandler != null) {server.getIQRouter().removeHandler(acceptIQHandler); acceptIQHandler = null;} if (answerIQHandler != null) {server.getIQRouter().removeHandler(answerIQHandler); answerIQHandler = null;} if (dialIQHandler != null) {server.getIQRouter().removeHandler(dialIQHandler); dialIQHandler = null;} if (hangupIQHandler != null) {server.getIQRouter().removeHandler(hangupIQHandler); hangupIQHandler = null;} if (redirectIQHandler != null) {server.getIQRouter().removeHandler(redirectIQHandler); redirectIQHandler = null;} if (dtmfIQHandler != null) {server.getIQRouter().removeHandler(dtmfIQHandler); dtmfIQHandler = null;} } private class OnHookIQHandler extends IQHandler { 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 class OffHookIQHandler extends IQHandler { 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 class PrivateIQHandler extends IQHandler { public PrivateIQHandler() { super("Rayo: XEP 0327 - Private");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("private", RAYO_HANDSET); } } private class PublicIQHandler extends IQHandler { public PublicIQHandler() { super("Rayo: XEP 0327 - Public");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("public", RAYO_HANDSET); } } private class MuteIQHandler extends IQHandler { public MuteIQHandler() { super("Rayo: XEP 0327 - Mute");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("mute", RAYO_HANDSET); } } private class UnmuteIQHandler extends IQHandler { public UnmuteIQHandler() { super("Rayo: XEP 0327 - Unmute");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("unmute", RAYO_HANDSET); } } private class HoldIQHandler extends IQHandler { public HoldIQHandler() { super("Rayo: XEP 0327 - Hold");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("hold", RAYO_HANDSET); } } private class RecordIQHandler extends IQHandler { public RecordIQHandler() { super("Rayo: XEP 0327 - Record");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("record", RAYO_RECORD); } } private class PauseRecordIQHandler extends IQHandler { public PauseRecordIQHandler() { super("Rayo: XEP 0327 - Pause Record");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("pause", RAYO_RECORD); } } private class ResumeRecordIQHandler extends IQHandler { public ResumeRecordIQHandler() { super("Rayo: XEP 0327 - Resume Record");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("resume", RAYO_RECORD); } } private class SayIQHandler extends IQHandler { public SayIQHandler() { super("Rayo: XEP 0327 - Say (text to speech)");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("say", RAYO_SAY); } } private class PauseSayIQHandler extends IQHandler { public PauseSayIQHandler() { super("Rayo: XEP 0327 - Pause Say");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("pause", RAYO_SAY); } } private class ResumeSayIQHandler extends IQHandler { public ResumeSayIQHandler() { super("Rayo: XEP 0327 - Resume Say");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("resume", RAYO_SAY); } } private class AcceptIQHandler extends IQHandler { public AcceptIQHandler() { super("Rayo: XEP 0327 - Accept");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("accept", RAYO_CORE); } } private class AnswerIQHandler extends IQHandler { public AnswerIQHandler() { super("Rayo: XEP 0327 - Answer");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("answer", RAYO_CORE); } } private class DialIQHandler extends IQHandler { 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 class HangupIQHandler extends IQHandler { 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); } } private class RedirectIQHandler extends IQHandler { public RedirectIQHandler() { super("Rayo: XEP 0327 - Redirect");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("redirect", RAYO_CORE); } } private class DtmfIQHandler extends IQHandler { public DtmfIQHandler() { super("Rayo: XEP 0327 - DTMF");} @Override public IQ handleIQ(IQ iq) {try {return handleIQGet(iq);} catch(Exception e) { return null;}} @Override public IQHandlerInfo getInfo() { return new IQHandlerInfo("dtmf", RAYO_CORE); } } }