/* * Copyright 2007 Sun Microsystems, Inc. * * This file is part of jVoiceBridge. * * jVoiceBridge is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation and distributed hereunder * to you. * * jVoiceBridge is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Sun designates this particular file as subject to the "Classpath" * exception as provided by Sun in the License file that accompanied this * code. */ package com.sun.voip.server; import com.sun.voip.CallParticipant; import com.sun.voip.Logger; import com.sun.voip.SdpManager; import com.sun.voip.MediaInfo; import com.sun.voip.SdpInfo; import com.sun.voip.RtpPacket; import javax.sip.*; import javax.sip.header.*; import javax.sip.message.*; import javax.sip.address.*; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Vector; import java.text.ParseException; import org.voicebridge.*; /** * A utility class used to construct and parse sip * messages, and other SIP-transaction-related tasks. */ public class SipUtil { /* static variables */ private static HeaderFactory headerFactory; private static AddressFactory addressFactory; private static MessageFactory messageFactory; private static SipProvider sipProvider; private static String ourIpAddress; private static int ourSipPort; private static String ourPublicIpAddress; private static int ourPublicSipPort; private static Vector supportedMedia = new Vector(); private static boolean initialized = false; private SdpManager sdpManager; public SipUtil() { this(null); } public SipUtil(MediaInfo mediaInfo) { if (!initialized) { initialize(); } sdpManager = new SdpManager(); if (mediaInfo == null) { try { mediaInfo = sdpManager.findMediaInfo(RtpPacket.PCMU_ENCODING, 8000, 1); Logger.println("SipUtil: Preference default media " + mediaInfo); } catch (ParseException e) { Logger.println("SipUtil: Invalid media info, can't set preference" + e.getMessage()); } } sdpManager.setPreferredMediaInfo(mediaInfo); } /** * Static initializer. */ public static void initialize() { headerFactory = SipServer.getHeaderFactory(); addressFactory = SipServer.getAddressFactory(); messageFactory = SipServer.getMessageFactory(); sipProvider = SipServer.getSipProvider(); ourIpAddress = SipServer.getSipStack().getIPAddress(); ourSipPort = sipProvider.getListeningPoint().getPort(); ourPublicIpAddress = ourIpAddress; String s = System.getProperty("com.sun.voip.server.PUBLIC_IP_ADDRESS"); if (s != null && s.length() > 0) { try { ourPublicIpAddress = InetAddress.getByName(s).getHostAddress(); } catch (UnknownHostException e) { Logger.println("Invalid public IP address, using " + ourIpAddress); } } Logger.println("Bridge public address: " + ourPublicIpAddress); ourPublicSipPort = ourSipPort; s = System.getProperty("com.sun.voip.server.PUBLIC_SIP_PORT"); if (s != null) { try { ourPublicSipPort = Integer.parseInt(s); } catch (NumberFormatException e) { Logger.println("Invalid public SIP Port, using " + ourSipPort); } } Logger.println("Bridge public SIP port: " + ourSipPort); supportedMedia.add(new MediaInfo( (byte)0, RtpPacket.PCMU_ENCODING, 8000, 1, false)); // supportedMedia.add(new MediaInfo( // (byte)101, RtpPacket.PCM_ENCODING, 8000, 1, false)); supportedMedia.add(new MediaInfo( (byte)102, RtpPacket.PCM_ENCODING, 8000, 2, false)); supportedMedia.add(new MediaInfo( (byte)103, RtpPacket.PCM_ENCODING, 16000, 1, false)); supportedMedia.add(new MediaInfo( (byte)104, RtpPacket.PCM_ENCODING, 16000, 2, false)); supportedMedia.add(new MediaInfo( (byte)105, RtpPacket.PCM_ENCODING, 32000, 1, false)); supportedMedia.add(new MediaInfo( (byte)106, RtpPacket.PCM_ENCODING, 32000, 2, false)); supportedMedia.add(new MediaInfo( (byte)107, RtpPacket.PCM_ENCODING, 44100, 1, false)); supportedMedia.add(new MediaInfo( (byte)108, RtpPacket.PCM_ENCODING, 44100, 2, false)); if (false) { supportedMedia.add(new MediaInfo( (byte)109, RtpPacket.PCM_ENCODING, 48000, 1, false)); supportedMedia.add(new MediaInfo( (byte)110, RtpPacket.PCM_ENCODING, 48000, 2, false)); } supportedMedia.add(new MediaInfo( (byte)111, RtpPacket.PCM_ENCODING, 48000, 2, false)); supportedMedia.add(new MediaInfo( (byte)112, RtpPacket.PCMU_ENCODING, 16000, 1, false)); supportedMedia.add(new MediaInfo( (byte)113, RtpPacket.PCMU_ENCODING, 16000, 2, false)); supportedMedia.add(new MediaInfo( (byte)114, RtpPacket.PCMU_ENCODING, 32000, 1, false)); supportedMedia.add(new MediaInfo( (byte)115, RtpPacket.PCMU_ENCODING, 32000, 2, false)); if (false) { supportedMedia.add(new MediaInfo( (byte)116, RtpPacket.PCMU_ENCODING, 44100, 1, false)); supportedMedia.add(new MediaInfo( (byte)117, RtpPacket.PCMU_ENCODING, 44100, 2, false)); supportedMedia.add(new MediaInfo( (byte)118, RtpPacket.PCMU_ENCODING, 48000, 1, false)); supportedMedia.add(new MediaInfo( (byte)119, RtpPacket.PCMU_ENCODING, 48000, 2, false)); } supportedMedia.add(new MediaInfo( (byte)120, RtpPacket.SPEEX_ENCODING, 8000, 1, false)); supportedMedia.add(new MediaInfo( (byte)121, RtpPacket.SPEEX_ENCODING, 8000, 2, false)); supportedMedia.add(new MediaInfo( (byte)122, RtpPacket.SPEEX_ENCODING, 16000, 1, false)); supportedMedia.add(new MediaInfo( (byte)123, RtpPacket.SPEEX_ENCODING, 16000, 2, false)); supportedMedia.add(new MediaInfo( (byte)124, RtpPacket.SPEEX_ENCODING, 32000, 1, false)); supportedMedia.add(new MediaInfo( (byte)125, RtpPacket.SPEEX_ENCODING, 32000, 2, false)); SdpManager.setSupportedMedia(supportedMedia); initialized = true; } public ClientTransaction sendInvite(CallParticipant cp, InetSocketAddress isa) throws InvalidArgumentException, SipException, ParseException { if (Bridge.getPublicHost().equals(isa.getAddress()) == false) { isa = new InetSocketAddress(Bridge.getPublicHost(), isa.getPort()); } String sdp = generateSdp(cp, isa); return sendInvite(cp, sdp); } public String generateSdp(CallParticipant cp, InetSocketAddress isa) { String sdp = sdpManager.generateSdp(cp, "MeetingCentral", isa); String s = "a=conferenceId:" + cp.getConferenceId(); if (cp.getMediaPreference() != null) { s += ":" + cp.getMediaPreference(); if (cp.getConferenceDisplayName() != null) { s += ":" + cp.getConferenceDisplayName(); } } sdp += s + "\r\n"; if (cp.getRemoteCallId() != null) { if (Logger.logLevel >= Logger.LOG_MOREINFO) { Logger.println("Setting callId in sdp to " + cp.getRemoteCallId()); } sdp += "a=callId:" + cp.getRemoteCallId() + "\r\n"; } if (cp.isDistributedBridge()) { sdp += "a=userName:DistributedBridge\r\n"; sdp += "a=distributedBridge\r\n"; } sdp += "a=transmitMediaInfoOk\r\n"; return sdp; } /** * Builds and sends a standard INVITE message, with a * CSeq number 1. * @param cp the CallParticipant * @param tpccName String identifying who is making the call. * @return transaction Id of newly created transaction. * @throws ParseException if message cannot be parsed * @throws SipException if general Sip Exception occurs. */ public ClientTransaction sendInvite(CallParticipant cp, String sdp) throws ParseException, InvalidArgumentException, SipException { // variables used for message building SipURI fromAddress = null; Address fromNameAddress = null; FromHeader fromHeader = null; SipURI toAddress = null; Address toNameAddress = null; ToHeader toHeader = null; SipURI requestURI = null; CallIdHeader callIdHeader = null; CSeqHeader cSeqHeader = null; AllowEventsHeader allowEventsHeader = null; ViaHeader viaHeader = null; ArrayList viaHeaders = null; ContentTypeHeader contentTypeHeader = null; Request invite = null; String obProxy = null; /* * We need a name and number to identify the party placing the call. * There are some restrictions when using the Vocal Proxy. * * If the number being called has 4 digits, then the request is * going to the proxy. The vocal proxy requires the <tpccName> * in the From: header to be provisioned. * We use 4099 which we provisioned. * * We generate a header like this: * * From: "<display name>" <sip:<tpccName>@129.148.75.131:5060> * To: "20315" <sip:20315@129.148.75.22:5060> * * 20315 is the phone being called. * * "<display name>" is the string that will show up as the callerID * on phone 20315. * * <tpccName> is the identifier of who's making the call. */ String fromName = cp.getDisplayName(); String fromNumber = cp.getDisplayName(); //cp.getFromPhoneNumber(); String toNumber = cp.getPhoneNumber(); String transport = "udp"; Logger.println("XXX from = " + fromName + " " + cp); // int toSipPort = SipServer.getSipAddress().getPort(); // XXX this should be the proxy or gateway port! int toSipPort = 5060; String proxy = cp.getSipProxy(); if (proxy == null) { proxy = SipServer.getDefaultSipProxy(); } String voipGateway = null; if (toNumber.indexOf("sip:") == 0) { /* * If a SIP URI is specified, parse it and send * the request directly to the target unless sendSipUriToProxy is false. * * If this request is sent to the proxy * the endpoint must be registered with the proxy. */ Address address = null; SipURI sipURI = null; String host = null; String user = null; try { address = addressFactory.createAddress(toNumber); sipURI = (SipURI)address.getURI(); host = sipURI.getHost(); user = sipURI.getUser(); } catch (ParseException e) { Logger.println("parse exception: " + toNumber + " sipUri " + sipURI + " host " + host + " user " + user); } if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("address: " + address); Logger.println("sipURI: " + sipURI); Logger.println("host: " + host); Logger.println("user: " + user); } if (SipServer.getSendSipUriToProxy() == false && user != null) { InetAddress inetAddress; try { inetAddress = InetAddress.getByName(host); voipGateway = host; //inetAddress.getHostAddress(); int port = sipURI.getPort(); if (port > 0) { toSipPort = port; } toNumber = user; /* * Keep just the User information from the URI. * XXX Not sure why I should do this. */ //cp.setPhoneNumber(toNumber); Logger.println("Call " + cp + " Sending INVITE directly to " + inetAddress + ":" + toSipPort); } catch (UnknownHostException e) { /* * Let proxy handle it */ voipGateway = proxy; Logger.println("Call " + cp + " Using proxy " + proxy + " for " + toNumber); // XXX Not sure why I should do this. //cp.setPhoneNumber(toNumber.substring(4)); // skip sip: toNumber = toNumber.substring(4); } } else { voipGateway = proxy; Logger.println("Call " + cp + " Using proxy " + proxy + " for " + toNumber); // XXX Not sure why I should do this. //cp.setPhoneNumber(toNumber.substring(4)); // skip sip: toNumber = toNumber.substring(4); } } else { // telephone number transport = System.getProperty("com.sun.voip.server.PROTOCOL"); if (toNumber.indexOf("tel:") == 0) { toNumber = toNumber.substring(4); } voipGateway = proxy; Logger.println("Call " + cp + " Using proxy " + proxy + " for " + toNumber); } if (toNumber.indexOf("@") < 0 && CallHandler.enablePSTNCalls() == false) { throw new SipException("PSTN calls are not allowed: " + cp); } ArrayList<ProxyCredentials> proxyCredentialList = SipServer.getProxyCredentials(); boolean gatewayRequired = false; if (voipGateway == null) { if (proxy == null) { if (proxyCredentialList.size() == 0) { Logger.println("Call " + cp + " no voipGateway is available!"); throw new SipException("No voip Gateway! " + cp); } else gatewayRequired = true; } else { voipGateway = proxy; gatewayRequired = true; } } else { if (voipGateway.equals(proxy)) gatewayRequired = true; } if (gatewayRequired) { Logger.println("XXXX gatewayRequired"); if (proxyCredentialList.size() != 0) { Logger.println("XXXX gatewayRequired 1"); int voipIndex = 0; for (int i=0; i<proxyCredentialList.size(); i++) { ProxyCredentials proxyCredentials = proxyCredentialList.get(i); if (voipGateway.equals(proxyCredentials.getName())) { voipIndex = i; } } ProxyCredentials proxyCredentials = proxyCredentialList.get(voipIndex); //fromName = proxyCredentials.getUserDisplay(); voipGateway = proxyCredentials.getHost(); obProxy = proxyCredentials.getProxy(); //fromAddress = addressFactory.createSipURI(proxyCredentials.getUserName(), voipGateway); fromAddress = addressFactory.createSipURI(fromName, voipGateway); //cp.setProxyCredentials(proxyCredentials); // we need this to match SIP transaction later //cp.setDisplayName(proxyCredentials.getUserDisplay()); // we need this to get proxy authentication details later } else { Logger.println("XXXX gatewayRequired 2"); } Logger.println("XXXX gatewayRequired 3"); toAddress = addressFactory.createSipURI(toNumber, voipGateway); } else { Logger.println("XXXX gatewayRequired 4"); Logger.println("fromNumber " + fromNumber); if (fromNumber.startsWith("sip:")) fromAddress = (SipURI)addressFactory.createAddress(fromNumber).getURI(); else fromAddress = addressFactory.createSipURI(fromNumber, ourIpAddress); fromAddress.setPort(ourSipPort); toAddress = addressFactory.createSipURI(toNumber, voipGateway); } Logger.println("XXXX gatewayRequired 5"); Logger.println("from " + fromAddress); Logger.println("to " + toAddress); fromNameAddress = addressFactory.createAddress(fromName, fromAddress); fromHeader = headerFactory.createFromHeader(fromNameAddress, new Integer((int)(Math.random() * 10000)).toString()); /* create To Header * e.g. "Willie Walker"<sip:30039@152.70.1.28:5060> * where "Willie Walker" == cp.getName() * 30039 == cp.getNumber() * 152.70.1.28 == cp.getIpAddress() * 5060 == cp.getPort() */ Logger.println("XXXX gatewayRequired 6"); if (Bridge.getPrivateHost().startsWith("127.") && voipGateway.equals("127.0.0.1") == false) { throw new SipException("The bridge's ip address is " + Bridge.getPrivateHost() + ". It is not possible to initiate a call to " + voipGateway); } if (Bridge.getPrivateHost().startsWith("127.") == false && voipGateway.startsWith("127.") == true) { throw new SipException("The bridge's ip address must be " + Bridge.getPrivateHost() + " in order to issue a call to " + voipGateway); } Logger.writeFile("Call " + cp + " voip gateway / proxy " + voipGateway + " port " + toSipPort); /* * Don't do this because port should be that of the toNumber if specified * otherwise that of the voipGateway */ // toAddress.setPort(toSipPort); toNameAddress = addressFactory.createAddress(toNumber, toAddress); toHeader = headerFactory.createToHeader(toNameAddress, null); /* create request URI (the first line of the request) * e.g. INVITE sip:61202@129.145.176.239:5060;transport=udp SIP/2.0 * where INVITE == (specfied later) * (requestURI) == (same as toAddress) * transport=udp SIP/2.0 == (transport) */ int ix = toNumber.indexOf("@"); if (ix >= 0) { toNumber = toNumber.substring(0, ix); } Logger.println("XXXX gatewayRequired 7"); requestURI = addressFactory.createSipURI(toNumber, voipGateway); requestURI.setPort(toSipPort); requestURI.setTransportParam (sipProvider.getListeningPoint(transport).getTransport()); /* create Via headers * e.g. Via: SIP/2.0/UDP 152.70.1.43:5060;branch=z9hG4bK5 * where SIP/2.0/UDP == (transport) * 152.70.1.43:5060 == (local address and SIP port) * branch=z9hG4bk5 == (auto generated branch id) */ viaHeader = headerFactory.createViaHeader(ourIpAddress, ourSipPort, sipProvider.getListeningPoint(transport).getTransport(), null); //viaHeader.setBranch(MessageFactoryImpl.generateBranchId()); viaHeaders = new ArrayList(); viaHeaders.add(viaHeader); /* create CallId header * e.g. Call-Id: 7727452ebac7ff5ac0c65baa7250e2f5@152.70.1.43 * where 77274.... == (globally unique identifier) */ callIdHeader = sipProvider.getNewCallId(); /* create Seq header * e.g. CSeq: 1 INVITE * where 1 == (CSeq number - motonically increasing within * each SIP callId) * INVITE == (request method) */ cSeqHeader = headerFactory.createCSeqHeader(1, Request.INVITE); // Create a new MaxForwardsHeader MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70); // create INVITE message. Put everything together invite = messageFactory.createRequest(requestURI, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); Logger.println("XXXX gatewayRequired 10"); if (SdpManager.useTelephoneEvent() == true) { allowEventsHeader = headerFactory.createAllowEventsHeader("telephone-event"); invite.addHeader(allowEventsHeader); } /* Contact Header (where subsequent requests should be sent to) * e.g. Contact: "Awarenex" <sip:Awarenex@152.70.1.43:5060>; * where "Awarenex" <sip:... == (local Address) */ SipURI contactURI = null; if (fromNumber.startsWith("sip:")) contactURI = (SipURI)addressFactory.createAddress(fromNumber).getURI(); else contactURI = addressFactory.createSipURI(fromNumber, ourPublicIpAddress); Logger.println("XXXX gatewayRequired 12"); contactURI.setPort(ourPublicSipPort); Address contactAddress = addressFactory.createAddress(contactURI); contactAddress.setDisplayName(fromName); ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress); invite.addHeader(contactHeader); Logger.println("XXXX gatewayRequired 14"); if (obProxy != null) { try { SipURI routeURI = (SipURI) addressFactory.createURI("sip:" + obProxy + ";lr"); RouteHeader routeHeader = headerFactory.createRouteHeader(addressFactory.createAddress(routeURI)); invite.addHeader(routeHeader); } catch (Exception e) { Logger.error("Creating registration route error " + e); } } Logger.println("XXXX gatewayRequired 16"); if (cp.isAutoAnswer()) { Logger.println("Call " + cp + " alert-info added"); try { SIPAlertInfo alertInfo = new SIPAlertInfo(); alertInfo.setNamePair("info=alert-autoanswer") ; invite.addHeader(alertInfo) ; } catch (Exception e) { Logger.error("Creating alert info error " + e); } } Logger.println("XXXX gatewayRequired 18"); if (sdp != null) { contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp"); invite.setContent(sdp, contentTypeHeader); } // Create the client transaction. ClientTransaction clientTransaction; Logger.writeFile("Invite\n" + invite); try { clientTransaction = sipProvider.getNewClientTransaction(invite); } catch (Exception e) { Logger.println("Call " + cp + " sendInvite failed: " + e.getMessage()); return null; } Logger.println("XXXX gatewayRequired 19"); clientTransaction.sendRequest(); Logger.println("XXXX gatewayRequired 20"); return clientTransaction; } public static boolean isSipUri(String phoneNumber) { if (phoneNumber == null || phoneNumber.indexOf("sip:") != 0) { return false; } Address address = null; SipURI sipURI = null; String host = null; String user = null; try { address = addressFactory.createAddress(phoneNumber); sipURI = (SipURI)address.getURI(); host = sipURI.getHost(); user = sipURI.getUser(); } catch (ParseException e) { return false; } return user != null; } /** * builds and sends a standard ACK message with the TPC * server's address and port in the sdp. * * @param clientTransaction ClientTransaction for this call * @param sdpBody String sdp body for this call * @param isa InetSocketAddress of conference receiver * @throws ParsException if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendAckWithTPCAddress(ClientTransaction clientTransaction, String sdpBody, InetSocketAddress isa) throws TransactionDoesNotExistException, ParseException, SipException { int start = sdpBody.indexOf("c=IN IP4 "); int finish = sdpBody.indexOf("\r", start); if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("modifying sdp with IP " + isa.getAddress() + " port " + isa.getPort()); } String newSdp = sdpBody.substring(0, start+9) + isa.getAddress().getHostAddress() + sdpBody.substring(finish); start = newSdp.indexOf("m=audio "); if (start > - 1) { newSdp = newSdp.substring(0, start + 8) + isa.getPort() + newSdp.substring(newSdp.indexOf(" RTP/AVP")); //only return PCMU 8000 start = newSdp.indexOf("RTP/AVP "); String pcmu = "RTP/AVP 0 13 101\r\n" + "a=rtpmap:0 PCMU/8000\r\n"; newSdp = newSdp.substring(0, start) + pcmu ; } if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("sdp 1\n" + newSdp); } /** * Build and send a standard ACK message, based on the sdpBody * given and the previous transaction of the call participant. */ Dialog dialog = clientTransaction.getDialog(); Request ackRequest = dialog.createRequest(Request.ACK); ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp"); ackRequest.setContent(newSdp, contentTypeHeader); Logger.writeFile(ackRequest.toString()); dialog.sendAck(ackRequest); return; } /** * builds and sends a standard ACK message with no SDP. * * @param clientTransaction ClientTransaction for this call * @throws ParsException if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendAck(ClientTransaction clientTransaction) throws TransactionDoesNotExistException, ParseException, SipException { /** * Build and send a standard ACK message */ Dialog dialog = clientTransaction.getDialog(); //Request ackRequest = dialog.createRequest(Request.ACK); Request ackRequest = clientTransaction.createAck(); dialog.sendAck(ackRequest); return; } /** * builds and sends a CANCEL message starting with an message. * @param clientTransaction most recent client transaction for call * @return transaction id of the request * @throws ParsEexception if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendCancel(ClientTransaction clientTransaction) throws TransactionDoesNotExistException, ParseException, SipException { Request cancel = clientTransaction.createCancel(); clientTransaction = sipProvider.getNewClientTransaction(cancel); clientTransaction.sendRequest(); } public void sendCancel(ServerTransaction st) throws TransactionDoesNotExistException, ParseException, SipException { Dialog dialog = st.getDialog(); dialog.incrementLocalSequenceNumber(); Request cancelRequest = dialog.createRequest(Request.CANCEL); dialog.sendRequest(sipProvider.getNewClientTransaction(cancelRequest)); } /** * builds and sends a standard BYE message. * @param clientTransaction Most recent client transaction for call * @throws ParseException if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendBye(ClientTransaction clientTransaction) throws TransactionDoesNotExistException, ParseException, SipException, InvalidArgumentException { Dialog dialog = clientTransaction.getDialog(); /* * Sip stack takes care of this. */ //dialog.incrementLocalSequenceNumber(); Request byeRequest = dialog.createRequest(Request.BYE); dialog.sendRequest(sipProvider.getNewClientTransaction(byeRequest)); } /** * builds and sends a standard BYE message. * @param serverTransaction */ public void sendBye(ServerTransaction st) throws TransactionDoesNotExistException, ParseException, SipException, InvalidArgumentException { Dialog dialog = st.getDialog(); /* * Sip stack takes care of this. */ //dialog.incrementLocalSequenceNumber(); Request byeRequest = dialog.createRequest(Request.BYE); Logger.writeFile(byeRequest.toString()); dialog.sendRequest(sipProvider.getNewClientTransaction(byeRequest)); } /** * builds and sends a standard OK message with SDP. * @return serverTransaction ServerTransaction for call * @param isa InetSocketAddress of conference receiver * @throws ParsEexception if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendOkWithSdp(Request request, ServerTransaction st, InetSocketAddress isa, SdpInfo remoteSdpInfo) throws TransactionDoesNotExistException, ParseException, SipException { Response response = messageFactory.createResponse(Response.OK, request); ToHeader to = (ToHeader) response.getHeader(ToHeader.NAME); if (to == null) { Logger.println("something is wrong, no ToHeader..."); return; } try { if (to.getTag() == null || to.getTag().trim().length() == 0) { Dialog dialog = st.getDialog(); to.setTag(Integer.toString(dialog.hashCode())); } } catch (ParseException ex) { Logger.println("can't set to tag"); return; } /* * Contact Header (where subsequent requests should be sent to) * e.g. Contact: "Awarenex" <sip:Awarenex@152.70.1.43:5060>; * where "Awarenex" <sip:... == (local Address) */ Address address = (Address) to.getAddress(); SipURI toURI = (SipURI) address.getURI(); SipURI contactURI = addressFactory.createSipURI(toURI.getUser(), ourPublicIpAddress); contactURI.setPort(ourPublicSipPort); Address contactAddress = addressFactory.createAddress(contactURI); contactAddress.setDisplayName(to.getName()); ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress); response.addHeader(contactHeader); String mySdp; try { mySdp = sdpManager.generateSdp("MeetingCentral", isa, remoteSdpInfo); } catch (IOException e) { throw new SipException("Failed to generate sdp " + remoteSdpInfo.getMediaInfo()); } ContentTypeHeader contentTypeHeader = headerFactory. createContentTypeHeader("application", "sdp"); response.setContent(mySdp, contentTypeHeader); try { Logger.writeFile(response.toString()); st.sendResponse(response); } catch (InvalidArgumentException e) { Logger.println("SendOKWithSdp: " + e.getMessage()); throw new SipException("Failed to send response: " + e.getMessage()); } } /* * Send an OK with no SDP */ public void sendOK(Request request, ServerTransaction st) throws TransactionDoesNotExistException, ParseException, SipException { Response response = messageFactory.createResponse(Response.OK, request); ToHeader to = (ToHeader) response.getHeader(ToHeader.NAME); if (to == null) { Logger.println("something is wrong, no to header..."); return; } try { if (to.getTag() == null || to.getTag().trim().length() == 0) { Dialog dialog = st.getDialog(); to.setTag(Integer.toString(dialog.hashCode())); } } catch (ParseException ex) { Logger.println("can't set to tag"); return; } /* * Contact Header (where subsequent requests should be sent to) * e.g. Contact: "Awarenex" <sip:Awarenex@152.70.1.43:5060>; * where "Awarenex" <sip:... == (local Address) */ Address address = (Address) to.getAddress(); SipURI toURI = (SipURI) address.getURI(); SipURI contactURI = addressFactory.createSipURI(toURI.getUser(), ourPublicIpAddress); contactURI.setPort(ourPublicSipPort); Address contactAddress = addressFactory.createAddress(contactURI); contactAddress.setDisplayName(to.getName()); ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress); response.addHeader(contactHeader); try { st.sendResponse(response); } catch (InvalidArgumentException e) { Logger.println("SendOk: " + e.getMessage()); throw new SipException("Failed to send response: " + e.getMessage()); } } /** * builds and sends a BUSY message. * @param clientTransaction ClientTransaction for call * @return serverTransaction ServerTransaction for call * @throws ParsEexception if message cannot be parsed * @throws SipException if general sip exception occurs. * @throws TransactionDoesNotExistException if transaction can * not be found. */ public void sendBusy(Request request, ServerTransaction serverTransaction) throws TransactionDoesNotExistException, ParseException, SipException { Response response = messageFactory.createResponse(Response.BUSY_HERE, request); /* * Contact Header (where subsequent requests should be sent to) * e.g. Contact: "Awarenex" <sip:Awarenex@152.70.1.43:5060>; * where "Awarenex" <sip:... == (local Address) */ //SipURI contactUrl = // addressFactory.createSipURI(cp.getPhoneNumber(), ourPublicIpAddress); //contactUrl.setPort(ourPublicSipPort); //SipURI contactURI = // addressFactory.createSipURI(cp.getPhoneNumber(), ourPublicIpAddress); //contactURI.setPort(port); //Address contactAddress = // addressFactory.createAddress(contactURI); //contactAddress.setDisplayName(cp.getName()); //ContactHeader contactHeader = // headerFactory.createContactHeader(contactAddress); //response.addHeader(contactHeader); try { serverTransaction.sendResponse(response); } catch (InvalidArgumentException e) { Logger.println("SendBusy: " + e.getMessage()); throw new SipException("Failed to send response: " + e.getMessage()); } } public SdpInfo getSdpInfo(String sdpBody) throws ParseException { return getSdpInfo(sdpBody, true); } public SdpInfo getSdpInfo(String sdpBody, boolean isRequest) throws ParseException { SdpInfo remoteSdpInfo = sdpManager.parseSdp(sdpBody); MediaInfo myPreferredMediaInfo = sdpManager.getPreferredMediaInfo(); byte payload; Logger.writeFile("My preferred media " + myPreferredMediaInfo); /* * If this is a remote SIP REQUEST and the remote side supports our * preferred mediaInfo, we will reply selecting our preferred media. * Otherwise, we reply using the remote's media choice, which is * either the remote's preferred or the "best" choice. */ if (isRequest && remoteSdpInfo.isSupported(myPreferredMediaInfo)) { payload = myPreferredMediaInfo.getPayload(); remoteSdpInfo.setMediaInfo(myPreferredMediaInfo); Logger.println("My preferred payload being used " + payload); } else { if (isRequest) { Logger.writeFile("My preferred media " + myPreferredMediaInfo + " not supported..."); } try { payload = remoteSdpInfo.getMediaInfo().getPayload(); remoteSdpInfo.setMediaInfo(sdpManager.findMediaInfo(payload)); Logger.writeFile("media setting is " + remoteSdpInfo.getMediaInfo()); } catch (ParseException e) { remoteSdpInfo.setMediaInfo(new MediaInfo((byte)0, RtpPacket.PCMU_ENCODING, 8000, 1, false)); } } return remoteSdpInfo; } /* * We add an attribute to the sdp data when we send a request * to a sip phone number */ public static String getCallIdFromSdp(Request request) { byte[] rawContent = request.getRawContent(); if (rawContent == null) { return null; } String sdpBody = new String(rawContent); SdpInfo sdpInfo; try { sdpInfo = SdpManager.parseSdp(sdpBody); } catch (ParseException e) { return null; } return sdpInfo.getCallId(); } /* * We add an attribute to the sdp data when we send a request * to a sip phone number */ public static String getConferenceIdFromSdp(Request request) { byte[] rawContent = request.getRawContent(); if (rawContent == null) { return null; } String sdpBody = new String(rawContent); SdpInfo sdpInfo; try { sdpInfo = SdpManager.parseSdp(sdpBody); } catch (ParseException e) { return null; } return sdpInfo.getConferenceId(); } public static String getUserNameFromSdp(Request request) { byte[] rawContent = request.getRawContent(); if (rawContent == null) { return null; } String sdpBody = new String(rawContent); SdpInfo sdpInfo; try { sdpInfo = SdpManager.parseSdp(sdpBody); } catch (ParseException e) { return null; } return sdpInfo.getUserName(); } public static boolean getDistributedBridgeFromSdp(Request request) { byte[] rawContent = request.getRawContent(); if (rawContent == null) { return false; } String sdpBody = new String(rawContent); SdpInfo sdpInfo; try { sdpInfo = SdpManager.parseSdp(sdpBody); } catch (ParseException e) { return false; } return sdpInfo.isDistributedBridge(); } /* * Utility method to parse a request and get the requestor's phone number */ public static String getFromPhoneNumber(Object requestEvent) { Request request = ((RequestEvent)requestEvent).getRequest(); FromHeader from = (FromHeader) request.getHeader(FromHeader.NAME); Address address = from.getAddress(); SipURI uri = (SipURI)address.getURI(); if (uri.toString().indexOf("@") >= 0) { String s = uri.getUser() + "@" + uri.getHost(); if (uri.getPort() != -1) { s += ":" + uri.getPort(); } return s; } return ((SipURI)address.getURI()).getUser(); } public static String getRequest(Object requestEvent) { Request request = ((RequestEvent)requestEvent).getRequest(); return request.toString(); } /* * Utility method to parse a request and get the To phone number */ public static String getToPhoneNumber(Object requestEvent) { Request request = ((RequestEvent)requestEvent).getRequest(); ToHeader to = (ToHeader) request.getHeader(ToHeader.NAME); Address address = to.getAddress(); return ((SipURI)address.getURI()).getUser(); } public static String getPhoneNumber(String phoneNumber) { if (SipUtil.isSipUri(phoneNumber) == false) { return phoneNumber; } try { Address address = addressFactory.createAddress(phoneNumber); SipURI sipURI = (SipURI)address.getURI(); return sipURI.getUser(); } catch (ParseException e) { Logger.println("parse exception: " + phoneNumber); return phoneNumber; } } /* * Utility method to parse a request and get the requestor's host address */ public static String getFromHost(Object requestEvent) { Request request = ((RequestEvent)requestEvent).getRequest(); FromHeader from = (FromHeader) request.getHeader(FromHeader.NAME); Address address = (Address) from.getAddress(); return ((SipURI)address.getURI()).getHost(); } /* * Utility method to parse a request and get the requestor's name */ public static String getFromName(Object requestEvent) { Request request = ((RequestEvent)requestEvent).getRequest(); FromHeader from = (FromHeader) request.getHeader(FromHeader.NAME); Address address = (Address) from.getAddress(); String name = address.getDisplayName(); if (name == null) { name = address.getURI().toString(); } return name; } public static void sendAckWithSDP(ClientTransaction ct, String sdp) throws ParseException, SipException { Dialog dialog = ct.getDialog(); Request ackRequest = dialog.createRequest(Request.ACK); if(sdp != null){ ContentTypeHeader contentTypeHeader; contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp"); ackRequest.setContent(sdp, contentTypeHeader); } dialog.sendAck(ackRequest); return; } }