/*
* Created on Aug 5, 2005
*
*Copyright Reliable Response, 2005
*/
package net.reliableresponse.notification.sip;
import gov.nist.javax.sip.message.SIPRequest;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Properties;
import java.util.Vector;
import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.PeerUnavailableException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.Hop;
import javax.sip.address.Router;
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.ContentTypeHeader;
import javax.sip.header.Header;
import javax.sip.header.ProxyAuthenticateHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import net.reliableresponse.notification.broker.BrokerFactory;
/**
* @author drig
*
* Copyright 2004 - David Rudder
*/
public class SipHandler implements SipListener, Router {
RtpTransmitter transmitter;
SipStack sipStack;
SipFactory sipFactory;
SipProvider sipProvider;
private String localHost = "127.0.0.1";
private String remoteHost;
private int localPort = 5060;
private int remotePort = 5060;
private static SipHandler instance;
private Vector requestListeners, responseListeners, dtmfListeners;
int sequenceNum;
String registrarHost;
int registrarPort;
String registrarDomain;
String username;
String password;
String callID;
boolean registered;
static boolean initialized = false;
public boolean isRegistered() {
return registered;
}
public void setRegistered(boolean registered) {
this.registered = registered;
}
public SipHandler(SipStack sipStack, String defaultRoute) {
}
private SipHandler(String remoteHost, int port) {
BrokerFactory.getLoggingBroker().logDebug("New Sip Handler with remoteHost="+remoteHost);
sipStack = null;
sipFactory = null;
this.remoteHost = remoteHost;
this.remotePort = port;
requestListeners = new Vector();
responseListeners = new Vector();
dtmfListeners = new Vector();
sequenceNum = 1;
// get the local address
String foundLocalHost = "127.0.0.1";
try {
foundLocalHost = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e1) {
BrokerFactory.getLoggingBroker().logError(e1);
}
localHost = BrokerFactory.getConfigurationBroker().getStringValue(
"local.hostname", foundLocalHost);
if (localHost.equals("127.0.0.1")) {
BrokerFactory.getLoggingBroker().logWarn("SIP could not determine local host name. Please set local.hostname in reliable.properties");
}
}
public void init() {
if (!initialized) {
BrokerFactory.getLoggingBroker().logDebug("Initializing SIP Handler");
initialized = true;
try {
Properties props = new Properties();
props.setProperty("javax.sip.IP_ADDRESS", localHost);
props.setProperty("javax.sip.ROUTER_PATH",
"net.reliableresponse.notification.sip.SipHandler");
props.setProperty("javax.sip.STACK_NAME", "SipHandler");
props.setProperty("javax.sip.RETRANSMISSION_FILTER", "true");
props.setProperty("gov.nist.javax.sip.MAX_MESSAGE_SIZE",
"1048576");
// Register
if (getRegistrarHost() != null) {
BrokerFactory.getLoggingBroker().logDebug(
"Initializing w/ Register " + getRegistrarHost()
+ ":" + getRegistrarPort() + "/udp");
props.setProperty("javax.sip.OUTBOUND_PROXY",
getRegistrarHost() + ":" + getRegistrarPort()
+ "/udp");
props
.setProperty(
"net.java.sip.communicator.sip.DEFAULT_AUTHENTICATION_REALM",
getRegistrarDomain());
props.setProperty(
"net.java.sip.communicator.sip.USER_NAME",
getUsername());
setRegistered(false);
} else {
setRegistered(true);
}
// Drop the client connection after we are done with the
// transaction.
props.setProperty(
"gov.nist.javax.sip.CACHE_CLIENT_CONNECTIONS", "false");
// Set to 0 in your production code for max speed.
// You need 16 for logging traces. 32 for debug + traces.
// Your code will limp at 32 but it is best for debugging.
props.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
sipStack = sipFactory.createSipStack(props);
ListeningPoint udpListeningPoint = sipStack
.createListeningPoint(localPort, "udp");
sipProvider = sipStack.createSipProvider(udpListeningPoint);
sipProvider.addSipListener(this);
// Setup callID
callID = System.currentTimeMillis() + "@" + localHost;
if (getRegistrarHost() != null) {
register();
}
while (!isRegistered()) {
BrokerFactory.getLoggingBroker().logDebug(
"Waiting for register");
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void sendRequest (String recipient, String type, String content, ContentTypeHeader contentType) throws SipException, ParseException , InvalidArgumentException {
URI uri = sipFactory.createAddressFactory().createURI(recipient);
Request request = sipFactory.createMessageFactory().createRequest(
uri,
type,
sipFactory.createHeaderFactory().createCallIdHeader(getCallID()),
sipFactory.createHeaderFactory().createCSeqHeader(sequenceNum++, type),
sipFactory.createHeaderFactory().createFromHeader(
sipFactory.createAddressFactory().createAddress(
"sip:"+username+"@"
+ SipHandler.getInstance()
.getLocalHost()
+ ":"
+ SipHandler.getInstance()
.getLocalPort()),
Integer.toString(hashCode())),
sipFactory.createHeaderFactory().createToHeader(
sipFactory.createAddressFactory().createAddress(
recipient), null),
new ArrayList(),
sipFactory.createHeaderFactory().createMaxForwardsHeader(10));
ContactHeader contactHeader = sipFactory.createHeaderFactory()
.createContactHeader(
sipFactory.createAddressFactory().createAddress(
"sip:"+username+"@"
+ SipHandler.getInstance()
.getLocalHost()
+ ":"
+ SipHandler.getInstance()
.getLocalPort()));
request.addHeader(contactHeader);
if (content != null) {
request.setContent(content, contentType);
}
BrokerFactory.getLoggingBroker().logDebug("Sending SIP Request "+request);
ClientTransaction ct = getSipProvider().getNewClientTransaction(request);
ct.sendRequest();
}
public static SipHandler getInstance() {
return instance;
}
public static SipHandler getInstance(String remoteHost, int port) {
if (instance == null) {
instance = new SipHandler(remoteHost, port);
}
return instance;
}
public SipProvider getSipProvider() {
return sipProvider;
}
public int getLocalPort() {
return localPort;
}
public String getLocalHost() {
return localHost;
}
public String getRegistrarHost() {
return registrarHost;
}
public void setRegistrarHost(String registrarHost) {
this.registrarHost = registrarHost;
}
public int getRegistrarPort() {
return registrarPort;
}
public void setRegistrarPort(int registrarPort) {
this.registrarPort = registrarPort;
}
public String getRegistrarDomain() {
return registrarDomain;
}
public void setRegistrarDomain(String registrarDomain) {
this.registrarDomain = registrarDomain;
}
public String getUsername() {
if (username == null) return "reliableresponse";
return username;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getCallID() {
return callID;
}
public void setCallID(String callID) {
this.callID = callID;
}
public ListIterator getNextHops(Request request) {
LinkedList ll = new LinkedList();
String remoteHost = getInstance().getRemoteHost();
int port = getInstance().getRemotePort();
ll.add(new HopImpl(remoteHost + ":" + port + "/udp"));
return ll.listIterator();
}
public Hop getOutboundProxy() {
return null;
}
public void hangup(String recipient) {
try {
sendRequest(recipient, Request.BYE, null, null);
} catch (PeerUnavailableException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (TransactionUnavailableException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (ParseException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (InvalidArgumentException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (SipException e) {
BrokerFactory.getLoggingBroker().logError(e);
}
}
public void processRequest(RequestEvent requestEvent) {
long begin = System.currentTimeMillis();
Request request = requestEvent.getRequest();
BrokerFactory.getLoggingBroker().logDebug("Process Request in Sip Handler "+request);
for (int i = 0; i < requestListeners.size(); i++) {
RequestListener listener = (RequestListener) requestListeners
.elementAt(i);
listener.handleRequest(requestEvent);
}
String method = ((CSeqHeader) request.getHeader(CSeqHeader.NAME))
.getMethod();
Header contentTypeHeader = request.getHeader("Content-Type");
String contentType = "";
if (contentTypeHeader != null)
contentType = contentTypeHeader.toString();
if ((contentType != null) && (contentType.indexOf(":") >= 0)) {
contentType = contentType.substring(contentType.indexOf(":") + 2,
contentType.length());
}
byte[] rawContent = request.getRawContent();
String content = "";
if (rawContent != null) {
content = new String(rawContent);
}
if (method.equals(Request.INFO)) {
if (contentType.toLowerCase().indexOf("application/dtmf-relay") >= 0) {
int signalIndex = content.indexOf("Signal=");
if (signalIndex >= 0) {
signalIndex += 7;
String dtmfString = content.substring(signalIndex,
signalIndex + 1);
BrokerFactory.getLoggingBroker().logDebug("Sending OK");
Response okayResponse = ((SIPRequest) request)
.createResponse(Response.OK);
try {
requestEvent.getServerTransaction().sendResponse(
okayResponse);
} catch (SipException e) {
e.printStackTrace();
}
for (int i = 0; i < dtmfListeners.size(); i++) {
((DTMFListener) dtmfListeners.elementAt(i))
.handleDTMF(dtmfString);
}
}
}
}
if (method.equals(Request.REGISTER)) {
sendOKReply(requestEvent);
}
BrokerFactory.getLoggingBroker().logDebug("processRequest took "+
(System.currentTimeMillis()-begin)+" millis");
}
private void sendOKReply(RequestEvent requestEvent) {
try {
Request request = requestEvent.getRequest();
ServerTransaction st = requestEvent.getServerTransaction();
// Send the 180 Trying
Response response = sipFactory.createMessageFactory()
.createResponse(200, request);
ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
toHeader.setTag("4321"); // Application is supposed to set.
response.addHeader(toHeader);
Address address = sipFactory.createAddressFactory().createAddress(
"Reliable Response <sip:" + getLocalHost() + ":"
+ getLocalPort() + ">");
ContactHeader contactHeader = sipFactory.createHeaderFactory()
.createContactHeader(address);
response.addHeader(contactHeader);
if (st == null) {
st = getSipProvider().getNewServerTransaction(
request);
}
BrokerFactory.getLoggingBroker().logDebug(
"Sending response = " + response);
BrokerFactory.getLoggingBroker().logDebug(
"Sending status = " + response.getStatusCode());
st.sendResponse(response);
// Send the 200 OK Invite
response = sipFactory.createMessageFactory().createResponse(200,
request);
response.addHeader(toHeader);
response.addHeader(contactHeader);
int seqNum = ((CSeqHeader) request.getHeader(CSeqHeader.NAME)).getSequenceNumber();
String method = ((CSeqHeader) request.getHeader(CSeqHeader.NAME)).getMethod();
response.addHeader (sipFactory.createHeaderFactory().createCSeqHeader(seqNum,method));
st.sendResponse(response);
} catch (Exception e) {
BrokerFactory.getLoggingBroker().logError(e);
}
}
public void register() throws PeerUnavailableException, ParseException{
// Register with Registrar server
if (registrarHost != null) {
register(Request.REGISTER, "sip:"+registrarHost+":"+registrarPort+";transport=udp",
"sip:"+username+"@"+localHost+":"+localPort+";transport=udp", null,
sipFactory.createHeaderFactory().createCallIdHeader(callID));
}
}
public void register(String type, String registrarURI, String to, AuthorizationHeader authnHeader, CallIdHeader callID) {
try {
Request registerRequest = sipFactory
.createMessageFactory()
.createRequest(
sipFactory.createAddressFactory().createURI(registrarURI),
type,callID,
sipFactory.createHeaderFactory().createCSeqHeader(
sequenceNum++, type),
sipFactory
.createHeaderFactory()
.createFromHeader(
sipFactory
.createAddressFactory()
.createAddress("sip:"+getUsername()+"@"+localHost+":"+localPort+";transport=udp"),
Integer.toString(hashCode())),
sipFactory
.createHeaderFactory()
.createToHeader(
sipFactory
.createAddressFactory()
.createAddress(to),
null),
new ArrayList(),
sipFactory.createHeaderFactory()
.createMaxForwardsHeader(10));
ContactHeader contactHeader = sipFactory.createHeaderFactory()
.createContactHeader(
sipFactory.createAddressFactory().createAddress(
"sip:"+getUsername()+"@"
+ localHost
+ ":"
+ localPort));
registerRequest.addHeader(contactHeader);
if (authnHeader != null) registerRequest.addHeader(authnHeader);
ClientTransaction ct = getSipProvider().getNewClientTransaction(
registerRequest);
ct.sendRequest();
} catch (Exception e) {
BrokerFactory.getLoggingBroker().logError(e);
}
}
public void processResponse(ResponseEvent responseEvent) {
Response response = responseEvent.getResponse();
BrokerFactory.getLoggingBroker().logDebug("Process Response in Sip Handler "+response);
String method = ((CSeqHeader) response.getHeader(CSeqHeader.NAME)).getMethod();
if (response.getStatusCode() == Response.OK) {
if (method.equals(Request.REGISTER)) {
setRegistered(true);
}
}
if (response.getStatusCode() == 401) {
handleAuthorization(responseEvent, response, method);
} else if (response.getStatusCode() == 407) {
handleProxyAuthorization(responseEvent, response, method);
}
for (int i = 0; i < responseListeners.size(); i++) {
ResponseListener listener = (ResponseListener) responseListeners
.elementAt(i);
listener.handleResponse(responseEvent);
}
}
/**
* @param responseEvent
* @param response
* @param method
*/
private void handleAuthorization(ResponseEvent responseEvent, Response response, String method) {
// Handle Unauthorized
Header authenticateHeader = response.getHeader("WWW-Authenticate");
if (authenticateHeader instanceof WWWAuthenticateHeader) {
try {
Request registerRequest = responseEvent.getClientTransaction().getRequest();
WWWAuthenticateHeader wwwAuthnHeader = (WWWAuthenticateHeader)authenticateHeader;
BrokerFactory.getLoggingBroker().logDebug("method="+method);
BrokerFactory.getLoggingBroker().logDebug("algo="+wwwAuthnHeader.getAlgorithm());
BrokerFactory.getLoggingBroker().logDebug("content="+(registerRequest.getContent()==null?"":registerRequest.getContent().toString()));
BrokerFactory.getLoggingBroker().logDebug("qop="+wwwAuthnHeader.getQop());
String digest = MessageDigestAlgorithm.calculateResponse
(wwwAuthnHeader.getAlgorithm(),
getUsername(), wwwAuthnHeader.getRealm(), password,
wwwAuthnHeader.getNonce(),
null, null, method,
registerRequest.getRequestURI().toString(),
registerRequest.getContent()==null?"":registerRequest.getContent().toString(),
wwwAuthnHeader.getQop());
AuthorizationHeader authorization = sipFactory.createHeaderFactory().createAuthorizationHeader(wwwAuthnHeader.getScheme());
authorization.setUsername(getUsername());
authorization.setRealm(wwwAuthnHeader.getRealm());
authorization.setNonce(wwwAuthnHeader.getNonce());
authorization.setParameter("uri",registerRequest.getRequestURI().toString());
authorization.setResponse(digest);
if(wwwAuthnHeader.getAlgorithm() != null)
authorization.setAlgorithm(wwwAuthnHeader.getAlgorithm());
if(wwwAuthnHeader.getOpaque() != null)
authorization.setOpaque(wwwAuthnHeader.getOpaque());
register (registerRequest.getMethod(), "sip:"+username+"@"+registrarHost+":"+registrarPort+";transport=udp",
"sip:"+getUsername()+"@"+localHost+":"+localPort+";transport=udp", authorization,
(CallIdHeader)registerRequest.getHeader(CallIdHeader.NAME));
} catch (PeerUnavailableException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (ParseException e) {
BrokerFactory.getLoggingBroker().logError(e);
}
}
}
/**
* @param responseEvent
* @param response
* @param method
*/
private void handleProxyAuthorization(ResponseEvent responseEvent, Response response, String method) {
// Handle Unauthorized
Header authenticateHeader = response.getHeader("Proxy-Authenticate");
if (authenticateHeader instanceof ProxyAuthenticateHeader) {
try {
Request registerRequest = responseEvent.getClientTransaction().getRequest();
ProxyAuthenticateHeader wwwAuthnHeader = (ProxyAuthenticateHeader)authenticateHeader;
// BrokerFactory.getLoggingBroker().logDebug("method="+method);
// BrokerFactory.getLoggingBroker().logDebug("algo="+wwwAuthnHeader.getAlgorithm());
// BrokerFactory.getLoggingBroker().logDebug("content="+(registerRequest.getContent()==null?"":registerRequest.getContent().toString()));
// BrokerFactory.getLoggingBroker().logDebug("qop="+wwwAuthnHeader.getQop());
String digest = MessageDigestAlgorithm.calculateResponse
(wwwAuthnHeader.getAlgorithm(),
getUsername(), wwwAuthnHeader.getRealm(), password,
wwwAuthnHeader.getNonce(),
null, null, method,
registerRequest.getRequestURI().toString(),
registerRequest.getContent()==null?"":registerRequest.getContent().toString(),
wwwAuthnHeader.getQop());
AuthorizationHeader authorization = sipFactory.createHeaderFactory().createProxyAuthorizationHeader(wwwAuthnHeader.getScheme());
authorization.setUsername(getUsername());
authorization.setRealm(wwwAuthnHeader.getRealm());
authorization.setNonce(wwwAuthnHeader.getNonce());
authorization.setParameter("uri",registerRequest.getRequestURI().toString());
authorization.setResponse(digest);
if(wwwAuthnHeader.getAlgorithm() != null)
authorization.setAlgorithm(wwwAuthnHeader.getAlgorithm());
if(wwwAuthnHeader.getOpaque() != null)
authorization.setOpaque(wwwAuthnHeader.getOpaque());
register (registerRequest.getMethod(), ((ToHeader)registerRequest.getHeader("to")).getAddress().toString(),
((ToHeader)registerRequest.getHeader("to")).getAddress().toString(), authorization,
(CallIdHeader)registerRequest.getHeader(CallIdHeader.NAME));
} catch (PeerUnavailableException e) {
BrokerFactory.getLoggingBroker().logError(e);
} catch (ParseException e) {
BrokerFactory.getLoggingBroker().logError(e);
}
}
}
public void processTimeout(TimeoutEvent timeoutEvent) {
// TODO Auto-generated method stub
System.out.println("timeoutEvent = " + timeoutEvent);
}
public void addRequestListener(RequestListener listener) {
requestListeners.addElement(listener);
}
public void addResponseListener(ResponseListener listener) {
responseListeners.addElement(listener);
}
public void addDTMFListener(DTMFListener listener) {
dtmfListeners.addElement(listener);
}
public void removeDTMFListener(DTMFListener listener) {
dtmfListeners.removeElement(listener);
}
public String getRemoteHost() {
return remoteHost;
}
public void setRemoteHost(String remoteHost) {
this.remoteHost = remoteHost;
}
public int getRemotePort() {
return remotePort;
}
public void setRemotePort(int remotePort) {
this.remotePort = remotePort;
}
public static void main(String args[]) throws Exception {
BrokerFactory.getConfigurationBroker().setConfiguration(
new FileInputStream("conf/reliable.properties"));
SipHandler handler = SipHandler.getInstance();
}
}