/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. * * Copyright 2007 Sun Microsystems, Inc. */ package com.sun.voip.server; import java.io.IOException; import java.text.*; import java.util.*; import javax.sip.*; import javax.sip.address.*; import javax.sip.header.*; import javax.sip.message.*; import com.sun.voip.Logger; import org.voicebridge.*; /** * <p>Title: SIP COMMUNICATOR-1.1</p> * <p>Description: JAIN-SIP-1.1 Audio/Video Phone Application</p> * <p>Copyright: Copyright (c) 2003</p> * <p>Organisation: LSIIT Laboratory (http://lsiit.u-strasbg.fr)</p> * <p>Network Research Team (http://www-r2.u-strasbg.fr))</p> * <p>Louis Pasteur University - Strasbourg - France</p> * @author Emil Ivov * @version 1.1 */ class RegisterProcessing implements SipListener { private Request registerRequest = null; private boolean isRegistered = false; private Timer reRegisterTimer = new Timer(); private String registrar; private String address; private ProxyCredentials proxyCredentials; private String sipCallId; private SipServer.SipServerCallback sipServerCallback; int registrarPort = 5060; int expires = 120; private HeaderFactory headerFactory = SipServer.getHeaderFactory(); private AddressFactory addressFactory = SipServer.getAddressFactory(); private MessageFactory messageFactory = SipServer.getMessageFactory(); private SipProvider sipProvider = SipServer.getSipProvider(); public RegisterProcessing(String address, String registrar, ProxyCredentials proxyCredentials) { Logger.println("Start registering...." + registrar); this.registrar = registrar; this.proxyCredentials = proxyCredentials; this.address = address; sipServerCallback = SipServer.getSipServerCallback(); try { register(); } catch (IOException e) { Logger.println(e.getMessage()); } } public void processRequest(RequestEvent requestReceivedEvent) { Logger.println("Request ignored: " + requestReceivedEvent.getRequest()); } public void processResponse(ResponseEvent responseReceivedEvent) { //Logger.println("Registering response...." + sipCallId); Response response = (Response)responseReceivedEvent.getResponse(); int statusCode = response.getStatusCode(); String method = ((CSeqHeader) response.getHeader(CSeqHeader.NAME)).getMethod(); if (Logger.logLevel >= Logger.LOG_MOREINFO) { Logger.println("Got response " + response); } if (statusCode == Response.OK) { isRegistered = true; Logger.println("Voice bridge successfully registered with " + registrar + " for " + proxyCredentials.getXmppUserName()); Application.registerNotification("Registered", proxyCredentials); sipServerCallback.removeSipListener(sipCallId); } else if (statusCode == Response.UNAUTHORIZED || statusCode == Response.PROXY_AUTHENTICATION_REQUIRED) { if (method.equals(Request.REGISTER)) { CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); if (cseq.getSequenceNumber() < 2) { ClientTransaction regTrans = SipServer.handleChallenge(response, responseReceivedEvent.getClientTransaction(), proxyCredentials); if (regTrans != null) { try { regTrans.sendRequest(); } catch (Exception e) { Logger.println("Registration failed, cannot send transaction " + e); Application.registerNotification("RegistrationFailed", proxyCredentials); } } else { Logger.println("Registration failed, cannot create transaction"); Application.registerNotification("RegistrationFailed", proxyCredentials); } } else { Logger.println("Registration failed " + responseReceivedEvent); Application.registerNotification("RegistrationFailed", proxyCredentials); } } } else { Logger.println("Unrecognized response: " + response); } } public void processTimeout(TimeoutEvent timeoutEvent) { Logger.println("Timeout trying to register with " + registrar); sipServerCallback.removeSipListener(sipCallId); } public void processDialogTerminated(DialogTerminatedEvent dte) { if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("processDialogTerminated called"); } sipServerCallback.removeSipListener(sipCallId); } public void processTransactionTerminated(TransactionTerminatedEvent tte) { if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("processTransactionTerminated called"); } sipServerCallback.removeSipListener(sipCallId); } public void processIOException(IOExceptionEvent ioee) { if (Logger.logLevel >= Logger.LOG_SIP) { Logger.println("processTransactionTerminated called"); } sipServerCallback.removeSipListener(sipCallId); } private void register() throws IOException { Logger.println("Registering with " + registrar); Application.registerNotification("Registering", proxyCredentials); FromHeader fromHeader = getFromHeader(); Address fromAddress = fromHeader.getAddress(); //Request URI SipURI requestURI = null; try { requestURI = addressFactory.createSipURI(null, registrar); } catch (ParseException e) { throw new IOException("Bad registrar address:" + registrar + " " + e.getMessage()); } //requestURI.setPort(registrarPort); try { requestURI.setTransportParam( sipProvider.getListeningPoint().getTransport()); } catch (ParseException e) { throw new IOException(sipProvider.getListeningPoint().getTransport() + " is not a valid transport! " + e.getMessage()); } //Call ID Header CallIdHeader callIdHeader = sipProvider.getNewCallId(); //CSeq Header CSeqHeader cSeqHeader = null; try { cSeqHeader = headerFactory.createCSeqHeader(1, Request.REGISTER); } catch (ParseException e) { //Should never happen throw new IOException("Corrupt Sip Stack " + e.getMessage()); } catch (InvalidArgumentException e) { //Should never happen throw new IOException("The application is corrupt " ); } //To Header ToHeader toHeader = null; try { String proxyWorkAround = System.getProperty("com.sun.mc.softphone.REGISTRAR_WORKAROUND"); if (proxyWorkAround != null && proxyWorkAround.toUpperCase().equals("TRUE")) { SipURI toURI = (SipURI)(requestURI.clone()); toURI.setUser(System.getProperty("user.name")); toHeader = headerFactory.createToHeader( addressFactory.createAddress(toURI), null); } else { toHeader = headerFactory.createToHeader(fromAddress, null); } } catch (ParseException e) { throw new IOException("Could not create a To header for address:" + fromHeader.getAddress() + " " + e.getMessage()); } //Via Headers ArrayList viaHeaders = getLocalViaHeaders(); //MaxForwardsHeader MaxForwardsHeader maxForwardsHeader = getMaxForwardsHeader(); //Request Request request = null; try { request = messageFactory.createRequest(requestURI, Request.REGISTER, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwardsHeader); } catch (ParseException e) { throw new IOException( "Could not create the register request! " + e.getMessage()); } //Expires Header ExpiresHeader expHeader = null; for (int retry = 0; retry < 2; retry++) { try { expHeader = headerFactory.createExpiresHeader( expires); } catch (InvalidArgumentException e) { if (retry == 0) { continue; } throw new IOException( "Invalid registrations expiration parameter - " + expires + " " + e.getMessage()); } } request.addHeader(expHeader); //Contact Header should contain IP - bug report - Eero Vaarnas ContactHeader contactHeader = getRegistrationContactHeader(); request.addHeader(contactHeader); try { SipURI routeURI = (SipURI) addressFactory.createURI("sip:" + proxyCredentials.getProxy() + ";lr"); RouteHeader routeHeader = headerFactory.createRouteHeader(addressFactory.createAddress(routeURI)); request.addHeader(routeHeader); } catch (Exception e) { Logger.error("Creating registration route error " + e); } //Transaction ClientTransaction regTrans = null; try { regTrans = sipProvider.getNewClientTransaction(request); } catch (TransactionUnavailableException e) { throw new IOException("Could not create a register transaction!\n" + "Check that the Registrar address is correct! " + e.getMessage()); } try { sipCallId = callIdHeader.getCallId(); sipServerCallback.addSipListener(sipCallId, this); registerRequest = request; regTrans.sendRequest(); if (Logger.logLevel >= Logger.LOG_MOREINFO) { Logger.println("Sent register request " + registerRequest); } if (expires > 0) { scheduleReRegistration(); } } catch (Exception e) { //we sometimes get a null pointer exception here so catch them all throw new IOException("Could not send out the register request! " + e.getMessage()); } this.registerRequest = request; } public void unregister() throws IOException { if (!isRegistered) { return; } cancelPendingRegistrations(); isRegistered = false; if (this.registerRequest == null) { Logger.println("Couldn't find the initial register request"); throw new IOException("Couldn't find the initial register request"); } Request unregisterRequest = (Request) registerRequest.clone(); try { unregisterRequest.getExpires().setExpires(0); CSeqHeader cSeqHeader = (CSeqHeader)unregisterRequest.getHeader(CSeqHeader.NAME); //[issue 1] - increment registration cseq number //reported by - Roberto Tealdi <roby.tea@tin.it> cSeqHeader.setSequenceNumber(cSeqHeader.getSequenceNumber()+1); } catch (InvalidArgumentException e) { Logger.println("Unable to set Expires Header " + e.getMessage()); return; } ClientTransaction unregisterTransaction = null; try { unregisterTransaction = sipProvider.getNewClientTransaction( unregisterRequest); } catch (TransactionUnavailableException e) { throw new IOException("Unable to create a unregister transaction " + e.getMessage()); } try { unregisterTransaction.sendRequest(); } catch (SipException e) { Logger.println("Faied to send unregister request " + e.getMessage()); return; } Application.registerNotification("Unregistering", proxyCredentials); } public boolean isRegistered() { return isRegistered; } private FromHeader fromHeader; private FromHeader getFromHeader() throws IOException { if (fromHeader != null) { return fromHeader; } try { SipURI fromURI = (SipURI) addressFactory.createURI("sip:" + proxyCredentials.getUserName() + "@" + registrar); fromURI.setTransportParam(sipProvider.getListeningPoint().getTransport()); fromURI.setPort(sipProvider.getListeningPoint().getPort()); Address fromAddress = addressFactory.createAddress(fromURI); fromAddress.setDisplayName(proxyCredentials.getUserDisplay()); fromHeader = headerFactory.createFromHeader(fromAddress, Integer.toString(hashCode())); } catch (ParseException e) { throw new IOException( "A ParseException occurred while creating From Header! " + e.getMessage()); } return fromHeader; } private ArrayList viaHeaders; private ArrayList getLocalViaHeaders() throws IOException { /* * We can't keep a cached copy because the callers * of this method change the viaHeaders. In particular * a branch may be added which causes INVITES to fail. */ if (viaHeaders != null) { return viaHeaders; } ListeningPoint lp = sipProvider.getListeningPoint(); viaHeaders = new ArrayList(); try { String addr = lp.getIPAddress(); ViaHeader viaHeader = headerFactory.createViaHeader( addr, lp.getPort(), lp.getTransport(), null); viaHeader.setRPort(); viaHeaders.add(viaHeader); return viaHeaders; } catch (ParseException e) { throw new IOException ( "A ParseException occurred while creating Via Headers! " + e.getMessage()); } catch (InvalidArgumentException e) { throw new IOException( "Unable to create a via header for port " + lp.getPort() + " " + e.getMessage()); } } private static final int MAX_FORWARDS = 70; private MaxForwardsHeader maxForwardsHeader; private MaxForwardsHeader getMaxForwardsHeader() throws IOException { if (maxForwardsHeader != null) { return maxForwardsHeader; } try { maxForwardsHeader = headerFactory.createMaxForwardsHeader(MAX_FORWARDS); return maxForwardsHeader; } catch (InvalidArgumentException e) { throw new IOException( "A problem occurred while creating MaxForwardsHeader " + e.getMessage()); } } private ContactHeader contactHeader; private ContactHeader getRegistrationContactHeader() throws IOException { if (contactHeader != null) { return contactHeader; } try { SipURI contactURI = (SipURI) addressFactory.createURI("sip:" + proxyCredentials.getUserName() + "@" + Config.getInstance().getPublicHost()); contactURI.setTransportParam( sipProvider.getListeningPoint().getTransport()); contactURI.setPort(sipProvider.getListeningPoint().getPort()); Address contactAddress = addressFactory.createAddress(contactURI); contactAddress.setDisplayName(proxyCredentials.getUserDisplay()); contactHeader = headerFactory.createContactHeader(contactAddress); return contactHeader; } catch (ParseException e) { throw new IOException( "A ParseException occurred while creating From Header! " + " " + e.getMessage()); } } class ReRegisterTask extends TimerTask { public ReRegisterTask() { } public void run() { try { if (isRegistered()) { register(); } } catch (IOException e) { Logger.println("Failed to reRegister " + e.getMessage()); } } } private void cancelPendingRegistrations() { reRegisterTimer.cancel(); reRegisterTimer = null; reRegisterTimer = new Timer(); } private void scheduleReRegistration() { ReRegisterTask reRegisterTask = new ReRegisterTask(); //java.util.Timer thinks in miliseconds //bug report and fix by Willem Romijn (romijn at lucent.com) reRegisterTimer.schedule(reRegisterTask, expires * 1000); } }