Package org.mobicents.slee.util

Source Code of org.mobicents.slee.util.SipUtilsImpl

/*
* Mobicents: The Open Source SLEE Platform     
*
* Copyright 2003-2005, CocoonHive, LLC.,
* and individual contributors as indicated
* by the @authors tag. See the copyright.txt
* in the distribution for a full listing of  
* individual contributors.
*
* This is free software; you can redistribute it
* and/or modify it under the terms of the
* GNU Lesser General Public License as
* published by the Free Software Foundation;
* either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the
* GNU Lesser General Public
* License along with this software;
* if not, write to the Free
* Software Foundation, Inc., 51 Franklin St,
* Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site:
* http://www.fsf.org.
*/

package org.mobicents.slee.util;

import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.ArrayList;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentLengthHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ProxyAuthenticateHeader;
import javax.sip.header.ProxyAuthorizationHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.apache.log4j.Logger;
import org.mobicents.slee.resource.sip.SipFactoryProvider;

public class SipUtilsImpl implements SipUtils {

  private static Logger log = Logger.getLogger(SipUtilsImpl.class);

  private SipProvider sipProvider;

  private HeaderFactory headerFactory;

  private MessageFactory messageFactory;

  private AddressFactory addressFactory;

  public SipUtilsImpl(SipProvider sipProvider, HeaderFactory headerFactory,
      MessageFactory messageFactory, AddressFactory addressFactory) {
    this.sipProvider = sipProvider;
    this.headerFactory = headerFactory;
    this.messageFactory = messageFactory;
    this.addressFactory = addressFactory;
  }

  public Dialog getDialog(ResponseEvent event) throws SipException {
    Dialog retVal = null;

    if (event.getDialog() == null) {
      // This is non recoverable and can occur if AUTOMATIC_DIALOG_SUPPORT
      // is disabled in the sip stack.
      log
          .error("responseEvent.getDialog returned null, if AUTOMATIC_DIALOG_SUPPORT is disabled "
              + "you must obtain a dialog before the first sip response arrives");
      throw new SipException(
          "responseEvent.getDialog returned null, if AUTOMATIC_DIALOG_SUPPORT is disabled "
              + "you must obtain a dialog before the first sip response arrives");
    } else {
      retVal = event.getDialog();
      log
          .debug("Returning dialog in getDialog(ResponseEvent) obtained directly from ResponseEvent");
    }

    return retVal;
  }

  public Dialog getDialog(RequestEvent event) throws SipException {
    Dialog retVal = null;
    try {
      if (event.getDialog() == null) {
        retVal = sipProvider.getNewDialog(event.getServerTransaction());
      } else {
        retVal = event.getDialog();
      }
    } catch (SipException ex) {
      log
          .error(
              "Exception in creating a new dialog in getDialog(RequestEvent)",
              ex);
      throw ex;
    }
    return retVal;
  }

  /**
   * Hex characters
   */
  private final char[] toHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
      '9', 'a', 'b', 'c', 'd', 'e', 'f' };

  public String generateTag() {
    String tag = new Integer((int) (Math.random() * 10000)).toString();
    return tag;
  }

  /**
   * Convert an array of bytes to an hexadecimal string
   *
   * @param b
   *            The byte array to convert to a hexadecimal string
   * @return The hexadecimal string representation of the byte array
   *
   */
  private String toHexString(byte b[]) {
    int pos = 0;
    char[] c = new char[b.length * 2];
    for (int i = 0; i < b.length; i++) {
      c[pos++] = toHex[(b[i] >> 4) & 0x0F];
      c[pos++] = toHex[b[i] & 0x0f];
    }
    return new String(c);
  }

  private AddressFactory getAddressFactory() {
    AddressFactory addressFactory = null;
    try {
      Context myEnv = (Context) new InitialContext()
          .lookup("java:comp/env");
      SipFactoryProvider factoryProvider = (SipFactoryProvider) myEnv
          .lookup("slee/resources/jainsip/1.2/provider");
      addressFactory = factoryProvider.getAddressFactory();
    } catch (NamingException e) {
      e.printStackTrace();
    }
    return addressFactory;
  }

  public Address convertURIToAddress(String uri) {
    SipURI sipURI = convertURIToSipURI(uri);
    return getAddressFactory().createAddress(sipURI);
  }

  public SipURI convertURIToSipURI(String uri) {
    String[] sipUserAndHost = null;
    SipURI sipURI = null;
    try {
      sipUserAndHost = parseSipUri(uri);
      final String sipUser = sipUserAndHost[0];
      final String sipHost = sipUserAndHost[1];
      sipURI = getAddressFactory().createSipURI(sipUser, sipHost);
    } catch (URISyntaxException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ParseException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return sipURI;
  }

  public String[] parseSipUri(String sipURI) throws URISyntaxException {
    String[] userAndHost = new String[2];
    final String sipPrexfix = "sip:";
    final String uriSeparator = "@";

    final int sipPrefixIndex = sipURI.indexOf(sipPrexfix) + 3;
    final int uriIndex = sipURI.indexOf(uriSeparator);

    final int nameIndex = sipPrefixIndex + 1;
    final int hostIndex = uriIndex + 1;

    if (sipURI.indexOf(sipPrexfix) == -1
        || sipURI.indexOf(uriSeparator) == -1
        || sipPrefixIndex > uriIndex) {
      throw new URISyntaxException(
          sipURI,
          "Malformed URI, the URI must use the format \"sip:user@host\". The incorrect URI was \""
              + sipURI + "\".");
    }

    userAndHost[0] = sipURI.substring(nameIndex, uriIndex);
    userAndHost[1] = sipURI.substring(hostIndex);

    return userAndHost;
  }

  public SipURI convertAddressToSipURI(Address address) throws ParseException {
    URI sipURI = address.getURI();
    SipURI retVal = null;
    if (sipURI.isSipURI())
      retVal = (SipURI) sipURI;
    else {
      throw new ParseException("URI was not of type SipURI!", -1);
    }
    return retVal;
  }

  public void sendCancel(ClientTransaction ct) throws SipException {
    Request request = ct.createCancel();
    // TODO Verify that this is the correct procedure for sending the CANCEL
    // It works but the api is not very clear about what happens when the
    // request
    // is created with ct.createCancel and sent in another new client
    // transaction
    ct = sipProvider.getNewClientTransaction(request);
    ct.sendRequest();
  }

  public Request buildInvite(Address fromAddress, Address toAddress,
      byte[] content, int cSeq) throws ParseException,
      InvalidArgumentException {
    return buildInvite(fromAddress, toAddress, content, cSeq, null);
  }

  public Request buildInvite(Address fromAddress, Address toAddress,
      byte[] content, int cSeq, String callId) throws ParseException,
      InvalidArgumentException {

    // From Header:
    FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,
        generateTag());

    // To header:
    ToHeader toHeader = headerFactory.createToHeader(toAddress, null);

    // Create request
    Request request = null;

    // Set the sequence number for the invite
    CSeqHeader cseqHeader = headerFactory.createCSeqHeader(cSeq,
        Request.INVITE);

    // Create the Via header and add to an array list
    ArrayList viaHeadersList = new ArrayList();
    viaHeadersList.add(createLocalViaHeader());

    /*
     * The Max-Forwards header field serves to limit the number of hops a
     * request can transit on the way to its destination. It consists of an
     * integer that is decremented by one at each hop. If the Max-Forwards
     * value reaches 0 before the request reaches its destination, it will
     * be rejected with a 483(Too Many Hops) error response.
     */
    MaxForwardsHeader maxForwardsHeader = headerFactory
        .createMaxForwardsHeader(70);

    final URI requestURI = convertAddressToSipURI(toAddress);

    /*
     * Create the request
     */
    CallIdHeader callIdHeader = sipProvider.getNewCallId();

    if ((callId != null) && (callId.trim().length() > 0)) {
      callIdHeader.setCallId(callId);
    }

    // callId = callIdHeader.getCallId();
    request = messageFactory.createRequest(requestURI, Request.INVITE,
        callIdHeader, cseqHeader, fromHeader, toHeader, viaHeadersList,
        maxForwardsHeader);

    // If a Content (such as an SDP) is to be attached with this
    // INVITE, attach it! Otherwise this part is just skipped
    if (content != null) {
      setContent(request, "application", "sdp", content);
    }

    /*
     * Add a contact header, this is mandatory for INVITE's. The Contact
     * header field value contains the URI at which the User Agent would
     * like to receive requests, and this URI MUST be valid even if used in
     * subsequent requests outside of any dialogs.
     */
    ContactHeader contactHeader = createLocalContactHeader();
    request.setHeader(contactHeader);

    if (log.isDebugEnabled()) {
      log.debug("Contact Header = " + contactHeader);
    }

    return request;
  }

  public ContactHeader createLocalContactHeader() throws ParseException {

    // First get the sip stack from the sip provider
    SipStack sipStack = sipProvider.getSipStack();
    // Get the host name of this listening point
    final String host = sipProvider.getListeningPoints()[0].getIPAddress();
    // Get the port
    final int port = sipProvider.getListeningPoints()[0].getPort();
    // Get the transport
    final String transport = sipProvider.getListeningPoints()[0]
        .getTransport();

    // Create a SIP URI of the host name
    SipURI sipURI = null;
    sipURI = addressFactory.createSipURI(null, host);
    sipURI.setTransportParam(transport);
    // Attach the port to the SIP URI
    sipURI.setPort(port);
    // Attach the transport

    // Create the contact address using the address factory
    Address contactAddress = addressFactory.createAddress(sipURI);
    // Create the contact header from the contact address
    ContactHeader contactHeader = headerFactory
        .createContactHeader(contactAddress);
    return contactHeader;
  }

  public ViaHeader createLocalViaHeader() throws ParseException,
      InvalidArgumentException {
    // First get the sip stack from the sip provider
    SipStack sipStack = sipProvider.getSipStack();
    // Get the host name of this listening point
    final String host = sipProvider.getListeningPoints()[0].getIPAddress();
    // Get the port
    final int port = sipProvider.getListeningPoints()[0].getPort();
    // Get the transport
    final String transport = sipProvider.getListeningPoints()[0]
        .getTransport();
    ViaHeader viaHeader = null;
    viaHeader = headerFactory.createViaHeader(host, port, transport, null);

    return viaHeader;
  }

  /**
   * Sets a content for a request
   *
   * @param request
   *            The request to set the content for
   * @param contentType
   *            the new string content type value.
   * @param contentSubType
   *            the new string content sub-type value.
   * @param content
   *            The content to set
   * @throws ParseException
   * @throws InvalidArgumentException
   */
  private void setContent(Request request, String contentType,
      String contentSubType, byte[] content) throws ParseException,
      InvalidArgumentException {
    /*
     * Create Content-Type The Content-Type header field indicates the media
     * type of the message-body sent to the recipient. The Content-Type
     * header field MUST be present if the body is not empty. If the body is
     * empty, and a Content-Type header field is present, it indicates that
     * the body of the specific type has zero length (for example, an empty
     * audio file).
     */
    ContentTypeHeader contentTypeHeader = headerFactory
        .createContentTypeHeader(contentType, contentSubType);
    // Apply the header
    // request.setHeader(contentTypeHeader);

    request.setContent(content, contentTypeHeader);

    /*
     * Content-Length header
     */
    ContentLengthHeader contentLengthHeader = headerFactory
        .createContentLengthHeader(content.length);
    request.setContentLength(contentLengthHeader);
  }

  public Request buildRequestWithAuthorizationHeader(ResponseEvent event,
      String password) throws TransactionUnavailableException {
    // If user is not authenticated, initialize the authentication
    // process. First we start off by retrieving the request that
    // triggered this response.

    Request request = event.getClientTransaction().getRequest();
    Response response = event.getResponse();

    if (request == null) {
      if (log.isDebugEnabled()) {
        log
            .debug("The request that caused the 407 could not be retrieved.");
      }
      return null;
    } else {
      // Clone the previous request since we'd like to modify
      // it with an authentication header and send it back to the
      // server.
      // Dialog dialog = event.getDialog();
      // Request requestClone = (Request) request.clone();
      // Get the sequence number from the request clone
      CSeqHeader cseqHeader = (CSeqHeader) request
          .getHeader(CSeqHeader.NAME);
      // try {
      // // Increase the sequence number by one
      // cseqHeader
      // .setSequenceNumber(cseqHeader.getSequenceNumber() + 1);
      // } catch (InvalidArgumentException e) {
      // log.error("InvalidArgumentException while setting
      // cseqHeader.setSequenceNumber", e);
      // e.printStackTrace();
      // }

      FromHeader fromHeaderReq = (FromHeader) request
          .getHeader(FromHeader.NAME);
      Address fromAddressReq = fromHeaderReq.getAddress();

      ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);
      Address toAddress = toHeader.getAddress();
      Request newRequest = null;
      String callId = ((CallIdHeader) response
          .getHeader(CallIdHeader.NAME)).getCallId();
      try {
        newRequest = buildInvite(fromAddressReq, toAddress, null,
            cseqHeader.getSequenceNumber() + 1, callId);
      } catch (ParseException parseExc) {
        parseExc.printStackTrace();
      } catch (InvalidArgumentException invaliArgExc) {
        invaliArgExc.printStackTrace();
      }

      // Now can begin to build the authentication header using a MD5
      // digest scheme
      WWWAuthenticateHeader wwwAuthenticateHeader = (WWWAuthenticateHeader) response
          .getHeader(WWWAuthenticateHeader.NAME);
      ProxyAuthenticateHeader proxyAuthenticateHeader = (ProxyAuthenticateHeader) response
          .getHeader(ProxyAuthenticateHeader.NAME);

      String realm = null;
      String nonce = null;
      // Check whether we've received an wwwAuthenticationHeader
      // or a ProxyAuthenticationHeader from the proxy server
      if (wwwAuthenticateHeader != null) {
        if (log.isDebugEnabled()) {
          log.debug("wwwAuthenticateHeader found!");
        }
        // Retrieve the realm from the authentication header
        realm = wwwAuthenticateHeader.getRealm();
        // Retrieve the nonce from the wwwAuthenticateHeader
        nonce = wwwAuthenticateHeader.getNonce();

      } else if (proxyAuthenticateHeader != null) {
        if (log.isDebugEnabled()) {
          log.debug("ProxyAuthenticateHeader found!");
        }
        // Retrieve the realm from the authentication header
        realm = proxyAuthenticateHeader.getRealm();
        // Retrieve the nonce from the wwwAuthenticateHeader
        nonce = proxyAuthenticateHeader.getNonce();
      } else {
        if (log.isDebugEnabled()) {
          log
              .debug("Neither a ProxyAuthenticateHeader or AuthorizationHeader found!");
        }
        return null;
      }

      // Retrieve the method from the CSeqHeader
      final String method = cseqHeader.getMethod();

      // Get the user name
      final FromHeader fromHeader = ((FromHeader) response
          .getHeader(FromHeader.NAME));
      Address address = fromHeader.getAddress();
      String fromHost = null;
      String fromUser = null;
      int fromPort = 0;

      String toHost = null;
      String toUser = null;
      int toPort = 0;

      SipURI fromSipURI = null;
      SipURI toSipURI = null;
      try {
        fromSipURI = convertAddressToSipURI(address);
        toSipURI = convertAddressToSipURI(toAddress);
      } catch (ParseException e2) {
        // TODO Auto-generated catch block
        e2.printStackTrace();
      }
      fromHost = fromSipURI.getHost();
      fromUser = fromSipURI.getUser();
      fromPort = fromSipURI.getPort();

      toHost = toSipURI.getHost();
      toUser = toSipURI.getUser();
      toPort = toSipURI.getPort();

      // Appened the port to the fromHost if available
      if (fromPort != -1) {
        fromHost += ":" + fromPort;
      }

      // Get the URI to set in the header
      SipURI uri = null;
      try {
        // uri = request.getRequestURI();
        uri = addressFactory.createSipURI(toUser, toHost);
      } catch (ParseException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
      }

      // create the digest for the response
      MessageDigest md5 = null;
      try {
        md5 = MessageDigest.getInstance("MD5");
      } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
      }

      // Generate the response digest according to RFC 2069, i.e.
      // response-digest = <"> < KD ( H(A1), unquoted nonce-value ":"
      // H(A2) > <">
      // A1 = unquoted username-value ":" unquoted realm-value ":"
      // password
      // password = < user's password >
      // A2 = Method ":" digest-uri-value
      // H(A1) = The digested value of A1 converted to a hex string
      // H(A2) = The digested value of A2 converted to a hex string
      // KD = H(A1) ":" nonce ":" H(A2)

      // Create A1 and A2
      String A1 = fromUser + ":" + realm + ":" + password;
      String A2 = method.toUpperCase() + ":" + uri.toString();
      // MD5 digest A1
      byte mdbytes[] = md5.digest(A1.getBytes());
      // Create H(A1)
      String HA1 = toHexString(mdbytes);
      // MD5 digest A2
      mdbytes = md5.digest(A2.getBytes());
      // Create H(A2)
      String HA2 = toHexString(mdbytes);
      // Create KD
      String KD = HA1 + ":" + nonce + ":" + HA2;
      mdbytes = md5.digest(KD.getBytes());

      // Check if we're dealing with an wwwAuthenticateHeader or a
      // ProxyAuthenticationHeader one more time, we're needed to reply
      // with the same header type.
      // as the one we received from the proxy server.
      if (wwwAuthenticateHeader != null) {
        AuthorizationHeader ah = null;

        try {
          ah = headerFactory.createAuthorizationHeader("Digest");
          ah.setUsername(fromUser);
          ah.setRealm(realm);
          ah.setAlgorithm("MD5");
          ah.setURI(uri);
          ah.setNonce(nonce);

          ah.setResponse(toHexString(mdbytes));

          newRequest.setHeader(ah);
        } catch (ParseException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      } else { // if Proxy Authentication
        ProxyAuthorizationHeader pah = null;
        try {
          pah = headerFactory
              .createProxyAuthorizationHeader("Digest");
          pah.setUsername(fromUser);
          pah.setRealm(realm);
          pah.setAlgorithm("MD5");
          pah.setURI(uri);
          pah.setNonce(nonce);
          pah.setResponse(toHexString(mdbytes));

          newRequest.setHeader(pah);
        } catch (ParseException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }

      System.out.println("********* New Request *******************");
      System.out.println(newRequest);

      // Return the new request with the proper authorization header.
      return newRequest;

    }
  }

  public Request buildAck(Dialog dialog, Object content) throws SipException {

    // Request ackRequest = dialog.createRequest(Request.ACK);

    Request ackRequest = null;

    try {
      ackRequest = dialog.createAck(dialog.getLocalSeqNumber());
    } catch (InvalidArgumentException invalidArgExc) {
      invalidArgExc.printStackTrace();
    }

    if (content != null) {

      String contentString = null;
      if (content instanceof byte[]) {
        contentString = new String((byte[]) content);

      } else if (content instanceof String) {
        contentString = (String) content;
      }
      if (log.isDebugEnabled()) {
        log.debug("sendCalleeAck Content = " + contentString);
      }

      // TODO Change signature of setContent to accept object
      final byte[] sdpContent = contentString.getBytes();
      try {
        setContent(ackRequest, "application", "sdp", sdpContent);
      } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (InvalidArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }

    return ackRequest;
  }

  public void sendOk(Request request) throws ParseException, SipException {
    Response okResponse = messageFactory.createResponse(Response.OK,
        request);
    sipProvider.sendResponse(okResponse);
  }

  public void sendStatefulOk(RequestEvent event) throws ParseException,
      SipException, InvalidArgumentException {
    ServerTransaction tx = event.getServerTransaction();
    Request request = event.getRequest();

    Response response = messageFactory.createResponse(Response.OK, request);
    tx.sendResponse(response);

  }
}
TOP

Related Classes of org.mobicents.slee.util.SipUtilsImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.