/** * Copyright 2012 Voxbone SA/NV * * 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 com.voxbone.kelpie; import java.text.ParseException; import java.util.ArrayList; import java.util.Properties; import java.util.TooManyListenersException; import javax.sip.ClientTransaction; import javax.sip.Dialog; import javax.sip.DialogState; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.PeerUnavailableException; import javax.sip.SipException; import javax.sip.SipFactory; import javax.sip.SipProvider; import javax.sip.SipStack; import javax.sip.TransactionUnavailableException; import javax.sip.TransportNotSupportedException; import javax.sip.address.Address; import javax.sip.address.AddressFactory; import javax.sip.address.SipURI; import javax.sip.address.URI; import javax.sip.header.AllowHeader; import javax.sip.header.CSeqHeader; import javax.sip.header.CallIdHeader; import javax.sip.header.ContactHeader; import javax.sip.header.ContentTypeHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderFactory; import javax.sip.header.MaxForwardsHeader; import javax.sip.header.ToHeader; import javax.sip.header.ViaHeader; import javax.sip.header.UserAgentHeader; import javax.sip.message.MessageFactory; import javax.sip.message.Request; import javax.sip.message.Response; import org.apache.log4j.Logger; /** * Functions useful for building SIP messages * */ public class SipService { public static AddressFactory addressFactory; public static HeaderFactory headerFactory; public static MessageFactory messageFactory; public static KelpieSipListener sipListener; public static SipProvider sipProvider; private SipStack sipStack; private SipFactory sipFactory; private static Logger logger = Logger.getLogger(SipService.class); private int localport; private static String localip; private static String remoteip; private static String remoteim; private static String agentName; private static String clientVersion; private static Boolean clientJingle; public SipService(Properties properties) { localip = properties.getProperty("com.voxbone.kelpie.ip"); localport = Integer.parseInt(properties.getProperty("com.voxbone.kelpie.sip_port", "5060")); remoteip = properties.getProperty("com.voxbone.kelpie.sip_gateway"); remoteim = properties.getProperty("com.voxbone.kelpie.im_gateway", remoteip); clientJingle = Boolean.parseBoolean(properties.getProperty("com.voxbone.kelpie.jingle", "false")); sipListener = new KelpieSipListener(properties.getProperty("com.voxbone.kelpie.hostname")); agentName = properties.getProperty("com.voxbone.kelpie.sip.user-agent", "Kelpie/QXIP"); clientVersion = properties.getProperty("com.voxbone.kelpie.version"); if (clientJingle) { clientVersion = clientVersion + "+Jingle"; } String nameOS = System.getProperty("os.name"); String versOS = System.getProperty("os.version"); agentName = agentName + " " + clientVersion + " (" + nameOS + "/" + versOS + ")"; sipFactory = SipFactory.getInstance(); sipFactory.setPathName("gov.nist"); properties.setProperty("javax.sip.STACK_NAME", "KelpieStack"); try { sipStack = sipFactory.createSipStack(properties); headerFactory = sipFactory.createHeaderFactory(); addressFactory = sipFactory.createAddressFactory(); messageFactory = sipFactory.createMessageFactory(); } catch (PeerUnavailableException e) { logger.error(e, e); } try { ListeningPoint udp = sipStack.createListeningPoint(localip, localport, "udp"); sipProvider = sipStack.createSipProvider(udp); sipProvider.setAutomaticDialogSupportEnabled(false); sipProvider.addSipListener(sipListener); } catch (TransportNotSupportedException e) { logger.error(e, e); } catch (InvalidArgumentException e) { logger.error(e, e); } catch (ObjectInUseException e) { logger.error(e, e); } catch (TooManyListenersException e) { logger.error(e, e); } } public static String getRemoteIP() { return remoteip; } public static String getRemoteIM() { return remoteim; } public static String getLocalIP() { return localip; } public static boolean acceptCall(CallSession cs) { try { Request req = cs.inviteTransaction.getRequest(); Response resp = messageFactory.createResponse(Response.OK, cs.inviteTransaction.getRequest()); ContentTypeHeader cth = headerFactory.createContentTypeHeader("application", "sdp"); Object sdp = cs.buildSDP(false); ToHeader th = (ToHeader) req.getHeader("To"); String dest = ((SipURI) th.getAddress().getURI()).getUser(); ListeningPoint lp = sipProvider.getListeningPoint(ListeningPoint.UDP); Address localAddress = addressFactory.createAddress("sip:" + dest + "@" + lp.getIPAddress() + ":" + lp.getPort()); ContactHeader ch = headerFactory.createContactHeader(localAddress); AllowHeader allowHeader = headerFactory.createAllowHeader("INVITE, ACK, CANCEL, OPTIONS, BYE, UPDATE, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); resp.addHeader(allowHeader); resp.addHeader(ch); UserAgentHeader userAgent = (UserAgentHeader) headerFactory.createHeader(UserAgentHeader.NAME, agentName); resp.setHeader(userAgent); resp.setContent(sdp, cth); cs.inviteTransaction.sendResponse(resp); } catch (ParseException e) { logger.error("Error accepting call", e); return false; } catch (SipException e) { logger.error("Error accepting call", e); return false; } catch (InvalidArgumentException e) { logger.error("Error accepting call", e); return false; } return true; } public static boolean sendBye(CallSession cs) { Request req; try { if ( cs.inviteOutTransaction != null && (cs.sipDialog.getState() == null || cs.sipDialog.getState() == DialogState.EARLY)) { req = cs.inviteOutTransaction.createCancel(); ClientTransaction t = sipProvider.getNewClientTransaction(req); t.sendRequest(); return false; } else { req = cs.sipDialog.createRequest(Request.BYE); ClientTransaction t = sipProvider.getNewClientTransaction(req); cs.sipDialog.sendRequest(t); } } catch (SipException e) { logger.error("Error sending BYE", e); } return true; } public static boolean sendReject(CallSession cs) { try { Request req = cs.inviteTransaction.getRequest(); Response resp = messageFactory.createResponse(Response.TEMPORARILY_UNAVAILABLE, cs.inviteTransaction.getRequest()); ToHeader th = (ToHeader) req.getHeader("To"); String dest = ((SipURI) th.getAddress().getURI()).getUser(); ListeningPoint lp = sipProvider.getListeningPoint(ListeningPoint.UDP); Address localAddress = addressFactory.createAddress("sip:" + dest + "@" + lp.getIPAddress() + ":" + lp.getPort()); ContactHeader ch = headerFactory.createContactHeader(localAddress); resp.addHeader(ch); cs.inviteTransaction.sendResponse(resp); } catch (Exception e) { logger.error("Error sending Reject", e); } return true; } public static boolean sendDTMFinfo(CallSession cs, char dtmf, int dtmfl) { Request req; try { logger.debug("Sending SIP INFO DTMF - Signal: " + dtmf + " Duration:" + dtmfl); ContentTypeHeader cth = headerFactory.createContentTypeHeader("application", "dtmf-relay"); String body = "Signal=" + dtmf + "\r\nDuration="+dtmfl; req = cs.sipDialog.createRequest(Request.INFO); ClientTransaction t = sipProvider.getNewClientTransaction(req); req.setContent(body, cth); cs.sipDialog.sendRequest(t); } catch (SipException e) { logger.error("Error sending DTMF INFO", e); } catch (ParseException e) { logger.error("Error sending DTMF INFO", e); } return true; } public static boolean sendVideoUpdate(CallSession cs) { Request req; try { ContentTypeHeader cth = headerFactory.createContentTypeHeader("application", "media_control+xml"); String body = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "<media_control>" + "<vc_primitive>" + "<to_encoder>" + "<picture_fast_update>" + "</picture_fast_update>" + "</to_encoder>" + "</vc_primitive>" + "</media_control>"; req = cs.sipDialog.createRequest(Request.INFO); ClientTransaction t = sipProvider.getNewClientTransaction(req); req.setContent(body, cth); cs.sipDialog.sendRequest(t); } catch (SipException e) { logger.error("Error sending FVR INFO", e); } catch (ParseException e) { logger.error("Error sending FVR INFO", e); } return true; } public static boolean sendInvite(CallSession cs, String domain) { FromHeader fromHeader = null; ToHeader toHeader = null; URI requestURI = null; URI fromURI = null; try { requestURI = addressFactory.createURI("sip:" + cs.jabberLocal.getNode() + "@" + remoteip); toHeader = headerFactory.createToHeader(addressFactory.createAddress(requestURI), null); fromURI = addressFactory.createURI("sip:" + UriMappings.toSipId(cs.jabberRemote) + "@" + domain); fromHeader = headerFactory.createFromHeader(addressFactory.createAddress(fromURI), null); int tag = (int) (Math.random() * 100000); fromHeader.setTag(Integer.toString(tag)); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ViaHeader viaHeader = null; ListeningPoint lp = sipProvider.getListeningPoint(ListeningPoint.UDP); viaHeader = headerFactory.createViaHeader(lp.getIPAddress(), lp.getPort(), lp.getTransport(), null); viaHeaders.add(viaHeader); CallIdHeader callIdHeader = sipProvider.getNewCallId(); CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE); MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70); ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp"); Request request = messageFactory.createRequest(requestURI, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards, contentTypeHeader, cs.buildSDP(true)); Address localAddress = addressFactory.createAddress("sip:" + UriMappings.toSipId(cs.jabberRemote) + "@" + lp.getIPAddress() + ":" + lp.getPort()); ContactHeader ch = headerFactory.createContactHeader(localAddress); request.addHeader(ch); AllowHeader allowHeader = headerFactory.createAllowHeader("INVITE, ACK, CANCEL, OPTIONS, BYE, UPDATE, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); request.addHeader(allowHeader); UserAgentHeader userAgent = (UserAgentHeader) headerFactory.createHeader(UserAgentHeader.NAME, agentName); request.setHeader(userAgent); ClientTransaction t = sipProvider.getNewClientTransaction(request); //t.setApplicationData(new ResponseInfo(listener, transaction)); Dialog d = SipService.sipProvider.getNewDialog(t); cs.sipDialog = d; d.setApplicationData(cs); t.setApplicationData(cs); cs.inviteOutTransaction = t; t.sendRequest(); return true; } catch (ParseException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (InvalidArgumentException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (TransactionUnavailableException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (SipException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } return false; } public static boolean sendMessageMessage(MessageMessage mm, String domain) { FromHeader fromHeader = null; ToHeader toHeader = null; URI requestURI = null; URI fromURI = null; try { requestURI = addressFactory.createURI("sip:" + mm.to + "@" + remoteim); toHeader = headerFactory.createToHeader(addressFactory.createAddress(requestURI), null); fromURI = addressFactory.createURI("sip:" + mm.from + "@" + domain); fromHeader = headerFactory.createFromHeader(addressFactory.createAddress(fromURI), null); int tag = (int) (Math.random() * 100000); fromHeader.setTag(Integer.toString(tag)); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ViaHeader viaHeader = null; ListeningPoint lp = sipProvider.getListeningPoint(ListeningPoint.UDP); viaHeader = headerFactory.createViaHeader(lp.getIPAddress(), lp.getPort(), lp.getTransport(), null); viaHeaders.add(viaHeader); CallIdHeader callIdHeader = sipProvider.getNewCallId(); CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE); MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70); ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("text", "plain"); Request request = messageFactory.createRequest(requestURI, "MESSAGE", callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards, contentTypeHeader, mm.body); ClientTransaction t = sipProvider.getNewClientTransaction(request); t.sendRequest(); return true; } catch (ParseException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (InvalidArgumentException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (TransactionUnavailableException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } catch (SipException e) { logger.error("Error on SIPTransmitter:deliverMessage", e); } return false; } }