/**
* Copyright 2012 Voxbone SA/NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ifsoft.sip;
import gov.nist.javax.sip.header.Require;
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import java.security.*;
import javax.sip.*;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
import org.jivesoftware.util.*;
import org.slf4j.*;
import org.slf4j.Logger;
/**
* Handles incoming sip requests/responses
*
*/
public class VideoBridgeSipListener implements SipListener
{
private static final Logger Log = LoggerFactory.getLogger(VideoBridgeSipListener.class);
private static boolean optionsmode = false;
private static boolean subscribeRport = false;
private static boolean subscribeEmu = false;
private static SipServerCallback sipServerCallback;
private static Hashtable sipListenersTable;
public String host;
public static void configure(Properties properties)
{
optionsmode = Boolean.parseBoolean(properties.getProperty("com.voxbone.kelpie.feature.options.probe", "false"));
subscribeRport = Boolean.parseBoolean(properties.getProperty("com.voxbone.kelpie.feature.subscribe.rport", "false"));
subscribeEmu = Boolean.parseBoolean(properties.getProperty("com.voxbone.kelpie.feature.subscribe.force-emu", "false"));
}
public VideoBridgeSipListener(String host)
{
this.host = host;
sipServerCallback = new SipServerCallback();
sipListenersTable = new Hashtable();
}
public static SipServerCallback getSipServerCallback() {
return sipServerCallback;
}
public static void rejectCall(Response res, ServerTransaction serverTransaction)
{
try {
serverTransaction.sendResponse(res);
} catch (Exception e1) {
Log.error("rejectCall", e1);
}
}
public void processDialogTerminated(DialogTerminatedEvent evt)
{
}
public void processIOException(IOExceptionEvent evt)
{
}
public void processRequest(final RequestEvent evt)
{
Request req = evt.getRequest();
Log.debug("[[SIP]] Got a request " + req.getMethod());
try
{
SipListener sipListener = findSipListener(evt);
if (sipListener != null)
{
sipListener.processRequest(evt);
return;
}
if (req.getMethod().equals(Request.MESSAGE))
{
Log.info("[[SIP]] Forwarding message");
/*
MessageMessage mm = new MessageMessage(req);
//JID destination = UriMappings.toJID(mm.to);
ContentTypeHeader cth = (ContentTypeHeader) req.getHeader(ContentTypeHeader.NAME);
if ( !cth.getContentType().equals("text")
&& !cth.getContentSubType().equals("plain"))
{
Log.warn("[[SIP]] Message isn't text, rejecting");
Response res = SipService.messageFactory.createResponse(Response.NOT_IMPLEMENTED, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
if (mm.body.startsWith("/echo")) {
mm.body = mm.body.substring(mm.body.lastIndexOf('/') + 5);
mm.to = mm.from;
String domain = host;
SipSubscription sub = SipSubscriptionManager.getWatcher(mm.from, mm.to);
if (sub != null)
{
domain = ((SipURI)sub.remoteParty.getURI()).getHost();
}
// Log.debug("[[SIP]] Echo message: " + mm.body);
SipService.sendMessageMessage(mm, domain);
return;
} else if (mm.body.startsWith("/me")) {
mm.body = mm.from + " " + mm.body.substring(mm.body.lastIndexOf('/') + 3);
}
Log.debug("[[SIP]] Jabber destination is " + destination);
Session sess = SessionManager.findCreateSession(host, destination);
if (sess != null)
{
if (sess.sendMessageMessage(mm))
{
Log.debug("[[SIP]] Message forwarded ok");
Response res = SipService.messageFactory.createResponse(Response.OK, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
}
*/
Log.error("[[SIP]] Forwarding failed!");
}
else if (req.getMethod().equals(Request.SUBSCRIBE))
{
Log.info("[[SIP]] Received a Subscribe message");
String callid = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId();
FromHeader fh = (FromHeader) req.getHeader("From");
URI ruri = req.getRequestURI();
String src = ((SipURI) fh.getAddress().getURI()).getUser();
String dest = ((SipURI) ruri).getUser();
SipSubscription sub = SipSubscriptionManager.getWatcherByCallID(dest, callid);
if (subscribeRport) {
// ContactHeader contact = (ContactHeader) req.getHeader(ContactHeader.NAME);
ViaHeader viaHeaderr = (ViaHeader)req.getHeader(ViaHeader.NAME);
int rport = Integer.parseInt( viaHeaderr.getParameter("rport") );
String received = viaHeaderr.getParameter("received");
Log.debug("[[SIP]] Forcing Contact RPORT: "+received+":"+rport);
Address localrAddress = SipService.addressFactory.createAddress("sip:" + src + "@" + received + ":" + rport );
ContactHeader nch = SipService.headerFactory.createContactHeader(localrAddress);
req.removeHeader("Contact");
req.addHeader(nch);
}
ToHeader th = (ToHeader) req.getHeader("To");
int expires = ((ExpiresHeader) req.getHeader(ExpiresHeader.NAME)).getExpires();
Response res = SipService.messageFactory.createResponse(Response.ACCEPTED, req);
if (expires > 0)
{
Log.debug("[[SIP]] New subscription or refresh");
if (sub == null)
{
if (th.getTag() == null)
{
Log.info("[[SIP]] New Subscription, sending add request to user");
/*
sub = new SipSubscription(req);
//sub.localTag = ((ToHeader) res.getHeader(ToHeader.NAME)).getTag();
((ToHeader) res.getHeader(ToHeader.NAME)).setTag(sub.localTag);
SipSubscriptionManager.addWatcher(dest, sub);
JID destination = UriMappings.toJID(dest);
JID source = new JID(src + "@" + host);
if (destination != null)
{
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendSubscribeRequest(source, destination, "subscribe");
}
else
{
Log.warn("[[SIP]] Unknown Jabber user...");
res = SipService.messageFactory.createResponse(Response.NOT_FOUND, req);
}
*/
}
else
{
Log.warn("[[SIP]] Rejecting Unknown in-dialog subscribe for "+ ruri);
res = SipService.messageFactory.createResponse(481, req);
}
}
else
{
Log.debug("[[SIP]] Refresh subscribe, sending poll");
/*
JID destination = UriMappings.toJID(dest);
JID source = new JID(src + "@" + host);
if (destination != null)
{
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendSubscribeRequest(source, destination, "probe");
}
else
{
res = SipService.messageFactory.createResponse(Response.NOT_FOUND, req);
Log.error("[[SIP]] Unknown destination!");
}
*/
}
}
else
{
Log.debug("[[SIP]] Expire subscribe");
if (sub != null)
{
Log.debug("[[SIP]] Subscription found, removing");
/*
sub.sendNotify(true, null);
SipSubscriptionManager.removeWatcher(dest, sub);
JID destination = UriMappings.toJID(dest);
JID source = new JID(src + "@" + host);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendSubscribeRequest(source, destination, "unsubscribe");
*/
}
}
res.addHeader(req.getHeader(ExpiresHeader.NAME));
ListeningPoint lp = SipService.sipProvider.getListeningPoint();
Address localAddress = SipService.addressFactory.createAddress("sip:" + dest + "@" + lp.getIPAddress() + ":" + lp.getPort());
ContactHeader ch = SipService.headerFactory.createContactHeader(localAddress);
res.addHeader(ch);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
else if (req.getMethod().equals(Request.NOTIFY))
{
Log.info("[[SIP]] Received a Notify message");
try
{
String callid = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId();
FromHeader fh = (FromHeader) req.getHeader("From");
URI ruri = req.getRequestURI();
String src = ((SipURI) fh.getAddress().getURI()).getUser();
String dest = ((SipURI) ruri).getUser();
SipSubscription sub = SipSubscriptionManager.getSubscriptionByCallID(dest, callid);
if (sub != null)
{
Log.debug("[[SIP]] Subscription found!");
SubscriptionStateHeader ssh = (SubscriptionStateHeader) req.getHeader(SubscriptionStateHeader.NAME);
if (ssh.getState().equalsIgnoreCase(SubscriptionStateHeader.PENDING))
{
Log.debug("[[SIP]] Subscription pending. Updating");
sub.updateSubscription(req);
}
else if ( ssh.getState().equalsIgnoreCase(SubscriptionStateHeader.ACTIVE)
&& !sub.isActive())
{
Log.debug("[[SIP]] Subscription accepted. Informing");
sub.updateSubscription(req);
/*
JID destination = UriMappings.toJID(dest);
JID source = new JID(src + "@" + host);
sub.makeActive();
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendSubscribeRequest(source, destination, "subscribed");
*/
}
else if (ssh.getState().equalsIgnoreCase(SubscriptionStateHeader.TERMINATED))
{
Log.debug("[[SIP]] Subscription is over, removing");
SipSubscriptionManager.removeSubscriptionByCallID(dest, sub.callId);
/*
JID destination = UriMappings.toJID(dest);
@SuppressWarnings("unused")
JID source = new JID(src + "@" + host);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendPresence(Presence.buildOfflinePresence(src, dest));
*/
Log.debug("[[SIP]] Reason code is " + ssh.getReasonCode());
if ( ssh.getReasonCode() != null
&& ( ssh.getReasonCode().equalsIgnoreCase(SubscriptionStateHeader.TIMEOUT)
|| ssh.getReasonCode().equalsIgnoreCase(SubscriptionStateHeader.DEACTIVATED)))
{
Log.debug("[[SIP]] Reason is timeout, sending re-subscribe");
sub = new SipSubscription(dest, src);
SipSubscriptionManager.addSubscriber(dest, sub);
sub.sendSubscribe(false);
}
}
if (req.getRawContent() != null)
{
try
{
/*
Presence pres = new Presence(req);
JID destination = UriMappings.toJID(dest);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendPresence(pres);
*/
}
catch (Exception e)
{
Log.error("[[SIP]] Error parsing presence document!\n" + req.toString(), e);
}
}
else if (sub.isActive())
{
/*
Presence pres = Presence.buildUnknownPresence(src, dest, host);
JID destination = UriMappings.toJID(dest);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendPresence(pres);
*/
}
Response res = SipService.messageFactory.createResponse(Response.OK, req);
ListeningPoint lp = SipService.sipProvider.getListeningPoint();
Address localAddress = SipService.addressFactory.createAddress("sip:" + dest + "@" + lp.getIPAddress() + ":" + lp.getPort());
ContactHeader ch = SipService.headerFactory.createContactHeader(localAddress);
res.addHeader(ch);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
}
else
{
Response res = SipService.messageFactory.createResponse(481, req);
ListeningPoint lp = SipService.sipProvider.getListeningPoint();
Address localAddress = SipService.addressFactory.createAddress("sip:" + dest + "@" + lp.getIPAddress() + ":" + lp.getPort());
ContactHeader ch = SipService.headerFactory.createContactHeader(localAddress);
res.addHeader(ch);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
}
}
catch (Exception e)
{
//Log.error("[[SIP]] failure while handling NOTIFY message", e);
}
return;
}
else if (req.getMethod().equals(Request.INVITE))
{
if (evt.getDialog() == null)
{
FromHeader fh = (FromHeader) req.getHeader("From");
String from = ((SipURI) fh.getAddress().getURI()).getUser();
String name = fh.getAddress().getDisplayName();
URI ruri = req.getRequestURI();
String dest = ((SipURI) ruri).getUser();
ToHeader th = (ToHeader) req.getHeader("To");
String to = ((SipURI) th.getAddress().getURI()).getUser();
Log.info("[[SIP]] Got initial invite! " + from + " " + to + " " + dest);
final ServerTransaction trans;
if (evt.getServerTransaction() == null)
{
trans = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
}
else
{
trans = evt.getServerTransaction();
}
trans.sendResponse(SipService.messageFactory.createResponse(Response.RINGING, req));
final Response res = SipService.messageFactory.createResponse(Response.FORBIDDEN, req);
// TODO broadcast incomingInvite(from, to, name, res, trans);
new Timer().schedule(new TimerTask()
{
@Override public void run()
{
rejectCall(res, trans);
}
}, (1000 * 60 * 60 * 2));
/*
if (cs != null)
{
Dialog dialog = SipService.sipProvider.getNewDialog(trans);
cs.parseInvite(req, dialog, trans);
dialog.setApplicationData(cs);
SipService.acceptCall(cs);
} else {
Response res = SipService.messageFactory.createResponse(Response.FORBIDDEN, req);
trans.sendResponse(res);
}
*/
return;
} else {
// SIP RE-INVITE (dumbstart implementation, ignores timers, etc)
Log.info("[[SIP]] Got a re-invite!");
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
if (cs != null)
{
Response res = null;
/*
Session sess = SessionManager.findCreateSession(cs.jabberLocal.getDomain(), cs.jabberRemote);
if (sess == null)
{
res = SipService.messageFactory.createResponse(488, req);
} else {
res = SipService.messageFactory.createResponse(Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST, req);
}
*/
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
}
}
else if (req.getMethod().equals(Request.BYE))
{
if (evt.getDialog() != null)
{
FromHeader fh = (FromHeader) req.getHeader("From");
String from = ((SipURI) fh.getAddress().getURI()).getUser();
String name = fh.getAddress().getDisplayName();
ToHeader th = (ToHeader) req.getHeader("To");
String to = ((SipURI) th.getAddress().getURI()).getUser();
Log.info("[[SIP]] Got in dialog bye " + from + " " + to + " " + name);
// TODO broadcast incomingBye(from, to, name);
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
if (cs != null)
{
cs.sendBye();
/*
Session sess = SessionManager.findCreateSession(cs.jabberLocal.getDomain(), cs.jabberRemote);
if (sess != null)
{
sess.sendBye(cs);
}
*/
}
Response res = SipService.messageFactory.createResponse(Response.OK, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
}
else if (req.getMethod().equals(Request.CANCEL))
{
if (evt.getDialog() != null)
{
Log.info("[[SIP]] Got in dialog cancel");
Response res = SipService.messageFactory.createResponse(Response.OK, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
if (cs != null)
{
cs.sendBye();
/*
Session sess = SessionManager.findCreateSession(cs.jabberLocal.getDomain(), cs.jabberRemote);
if (sess != null)
{
SipService.sendReject(cs);
sess.sendBye(cs);
}
*/
}
return;
}
}
else if (req.getMethod().equals(Request.REFER))
{
Response res = SipService.messageFactory.createResponse(Response.ACCEPTED, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
else if (req.getMethod().equals(Request.REGISTER))
{
Response res = processRegister(req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
else if (req.getMethod().equals(Request.ACK))
{
return;
}
else if (req.getMethod().equals(Request.OPTIONS))
{
int resp = Response.OK;
if (optionsmode) {
if (evt.getDialog() != null)
{
Log.info("[[SIP]] Got in dialog OPTIONS");
resp = Response.OK;
// temp: debug message to validate this OPTIONS scenario
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
if (cs == null)
{
Log.error("[[SIP]] OPTIONS CallSession is null?");
}
} else {
Log.info("[[SIP]] Rejecting out-of-dialog OPTIONS");
resp = Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
}
}
try
{
DatagramSocket ds = new DatagramSocket();
ds.close();
}
catch (SocketException e)
{
Log.error("[[SIP]] No more sockets available", e);
resp = Response.SERVER_INTERNAL_ERROR;
}
Response res = SipService.messageFactory.createResponse(resp, req);
SipService.sipProvider.sendResponse(res);
return;
}
// SIP UPDATE (dumbstart, purposed as Jingle session-info counterpart)
else if (req.getMethod().equals(Request.UPDATE))
{
int resp = Response.OK;
if (optionsmode) {
if (evt.getDialog() != null)
{
Log.info("[[SIP]] Got UPDATE request");
resp = Response.OK;
// temp: debug message to validate this OPTIONS scenario
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
if (cs == null)
{
Log.error("[[SIP]] UPDATE CallSession is null?");
}
Header require = (Header)req.getHeader(Require.NAME);
Header sessexp = (Header)req.getHeader("Session-Expires");
Log.debug("[[SIP]] SESSION-TIMER: "+require+":"+sessexp);
} else {
Log.info("[[SIP]] No Session - Rejecting UPDATE");
resp = Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
}
}
try
{
DatagramSocket ds = new DatagramSocket();
ds.close();
}
catch (SocketException e)
{
Log.error("[[SIP]] No more sockets available", e);
resp = Response.SERVER_INTERNAL_ERROR;
}
Response res = SipService.messageFactory.createResponse(resp, req);
SipService.sipProvider.sendResponse(res);
return;
}
else if (req.getMethod().equals(Request.INFO))
{
CallSession cs = (CallSession) evt.getDialog().getApplicationData();
/*
if (cs != null && cs.vRelay != null)
{
cs.vRelay.sendFIR();
Response res = SipService.messageFactory.createResponse(Response.OK, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
return;
}
*/
}
Response res = SipService.messageFactory.createResponse(Response.FORBIDDEN, req);
if (evt.getServerTransaction() == null)
{
ServerTransaction tx = ((SipProvider) evt.getSource()).getNewServerTransaction(req);
tx.sendResponse(res);
}
else
{
evt.getServerTransaction().sendResponse(res);
}
Log.error("[[SIP]] Rejecting request");
}
catch (ParseException e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
catch (TransactionAlreadyExistsException e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
catch (TransactionUnavailableException e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
catch (SipException e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
catch (InvalidArgumentException e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
catch(Exception e)
{
Log.error("[[SIP]] Error processing sip Request!\n" + req.toString(), e);
}
}
public void processResponse(ResponseEvent evt)
{
Response resp = evt.getResponse();
String method = ((CSeqHeader) resp.getHeader(CSeqHeader.NAME)).getMethod();
int status = resp.getStatusCode();
Log.info("[[SIP]] Got a response to " + method);
try
{
SipListener sipListener = findSipListener(evt);
if (sipListener != null)
{
sipListener.processResponse(evt);
return;
}
if (method.equals(Request.SUBSCRIBE))
{
if (subscribeEmu) {
// force emulation regardless of reply
status = 500;
}
if (status == Response.PROXY_AUTHENTICATION_REQUIRED || status == Response.UNAUTHORIZED)
{
ClientTransaction clientTransaction = evt.getClientTransaction();
if (SipService.sipAccount != null)
{
try {
SipService.handleChallenge(resp, clientTransaction, SipService.sipAccount).sendRequest();
} catch (Exception e) {
Log.error("Proxy authentification failed", e);
}
}
return;
}
else if (status >= 200 && status < 300)
{
Log.info("[[SIP]] 200 OK to SUBSCRIBE, updating route info");
String callid = ((CallIdHeader) resp.getHeader(CallIdHeader.NAME)).getCallId();
FromHeader fh = (FromHeader) resp.getHeader("From");
String user = ((SipURI) fh.getAddress().getURI()).getUser();
SipSubscription sub = SipSubscriptionManager.getSubscriptionByCallID(user, callid);
// Subscription can be null if it's a response to Subscribe / Expires 0
if (sub != null)
{
sub.updateSubscription(resp);
}
}
else if (status >= 400)
{
Log.error("[[SIP]] Subscribe failed");
FromHeader fh = (FromHeader) resp.getHeader("From");
String dest = ((SipURI) fh.getAddress().getURI()).getUser();
ToHeader th = (ToHeader) resp.getHeader("To");
String src = ((SipURI) th.getAddress().getURI()).getUser();
String callid = ((CallIdHeader) resp.getHeader(CallIdHeader.NAME)).getCallId();
if (status != 404)
{
Log.info("[[SIP]] emulating presence");
/*
JID destination = UriMappings.toJID(dest);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendSubscribeRequest(new JID(src + "@" + host), destination, "subscribed");
sess.sendPresence(Presence.buildOnlinePresence(src, dest, host));
*/
}
@SuppressWarnings("unused")
SipSubscription sub = SipSubscriptionManager.removeSubscriptionByCallID(dest, callid);
}
}
else if (method.equals(Request.INVITE))
{
if (status >= 200 && status < 300)
{
Dialog d = evt.getDialog();
if (d == null)
{
Log.error("[[SIP]] Dialog is null");
return;
}
ClientTransaction ct = evt.getClientTransaction();
if (ct == null)
{
Log.error("[[SIP]] Client transaction null!!!!");
return;
}
FromHeader fh = (FromHeader) resp.getHeader("From");
String dest = ((SipURI) fh.getAddress().getURI()).getUser();
ToHeader th = (ToHeader) resp.getHeader("To");
String source = ((SipURI) th.getAddress().getURI()).getUser();
Log.info("[[SIP]] INVITE Response " + source + " " + dest + "\n" + new String(resp.getRawContent()));
if (SipService.callSessions.containsKey(dest + source))
{
CallSession cs = SipService.callSessions.get(dest + source);
if(!cs.callAccepted)
{
// RFC3261 says that all 200 OK to an invite get passed to UAC, even re-trans, so we need to filter
cs.parseSDP(d, new String(resp.getRawContent()), false);
cs.callAccepted = true;
}
} else Log.error("[[SIP]] can't find call session object " + dest + source);
}
else if (status == Response.PROXY_AUTHENTICATION_REQUIRED || status == Response.UNAUTHORIZED)
{
ClientTransaction clientTransaction = evt.getClientTransaction();
if (SipService.sipAccount != null)
{
try {
SipService.handleChallenge(resp, clientTransaction, SipService.sipAccount).sendRequest();
} catch (Exception e) {
Log.error("Proxy authentification failed", e);
}
}
return;
}
else if (status >= 400)
{
Log.error("[[SIP]] Invite failed, ending call");
Dialog d = evt.getDialog();
if (d == null)
{
Log.error("[[SIP]] Dialog is null");
return;
}
CallSession cs = (CallSession) d.getApplicationData();
// terminate the jabber side if it hasn't been done already
if (cs != null)
{
cs.sendBye();
/*
if (CallManager.getSession(cs.jabberSessionId) != null)
{
FromHeader fh = (FromHeader) resp.getHeader("From");
String dest = ((SipURI) fh.getAddress().getURI()).getUser();
JID destination = UriMappings.toJID(dest);
Session sess = SessionManager.findCreateSession(host, destination);
sess.sendBye(cs);
}
*/
}
}
}
else if (method.equals(Request.NOTIFY))
{
if (status == 418)
{
Log.info("[[SIP]] Subcription is no longer known, removing");
FromHeader fh = (FromHeader) resp.getHeader("From");
String dest = ((SipURI) fh.getAddress().getURI()).getUser();
String callid = ((CallIdHeader) resp.getHeader(CallIdHeader.NAME)).getCallId();
SipSubscription sub = SipSubscriptionManager.getWatcherByCallID(dest, callid);
if (sub != null)
{
Log.debug("[[SIP]] Watcher removed ok");
SipSubscriptionManager.removeWatcher(dest, sub);
}
}
}
// Very basic MESSAGE handling for replies
else if (method.equals(Request.MESSAGE))
{
if (status >= 400) {
Log.info("[[SIP]] MESSAGE failed with status "+status);
}
else if (status == 200) {
Log.info("[[SIP]] MESSAGE delivered");
}
}
}
catch (Exception e)
{
Log.error("[[SIP]] Error processing sip Response!\n" + resp.toString(), e);
}
}
public void processTimeout(TimeoutEvent evt)
{
}
public void processTransactionTerminated(TransactionTerminatedEvent evt)
{
}
private SipListener findSipListener(EventObject event)
{
String sipCallId = null;
try {
CallIdHeader callIdHeader;
if (event instanceof RequestEvent) {
Request request = ((RequestEvent)event).getRequest();
callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
} else if (event instanceof ResponseEvent) {
Response response = ((ResponseEvent)event).getResponse();
callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
} else {
Log.error("Invalid event object " + event);
return null;
}
sipCallId = callIdHeader.getCallId();
synchronized (sipListenersTable)
{
return (SipListener)sipListenersTable.get(sipCallId);
}
} catch (NullPointerException e) {
if (sipCallId == null || "".equals(sipCallId))
{
Log.error("could not get SIP CallId from incoming message. Dropping message", e);
}
throw e;
}
}
private Response processRegister(Request request) throws ParseException
{
DigestServerAuthenticationMethod dsam = null;
try
{
dsam = new DigestServerAuthenticationMethod(JiveGlobals.getProperty("xmpp.domain", "localhost"), new String[] { "MD5" });
}
catch (NoSuchAlgorithmException ex)
{
Log.error("Cannot create authentication method. Some algorithm is not implemented: ", ex);
return SipService.messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR, request);
}
try
{
if (!checkAuthorization(request, dsam) )
{
Log.info("Request rejected ( Unauthorized )");
Response response = SipService.messageFactory.createResponse(Response.UNAUTHORIZED,request);
WWWAuthenticateHeader wwwAuthenticateHeader = SipService.headerFactory.createWWWAuthenticateHeader("Digest");
wwwAuthenticateHeader.setParameter("realm",dsam.getDefaultRealm());
wwwAuthenticateHeader.setParameter("nonce",dsam.generateNonce(dsam.getPreferredAlgorithm()));
wwwAuthenticateHeader.setParameter("opaque","");
wwwAuthenticateHeader.setParameter("stale","FALSE");
wwwAuthenticateHeader.setParameter("algorithm", dsam.getPreferredAlgorithm());
response.setHeader(wwwAuthenticateHeader);
return response;
}
}
catch (Exception e)
{
Log.error("processRegister failed", e);
return SipService.messageFactory.createResponse(Response.NOT_FOUND, request);
}
ContactHeader cont = (ContactHeader) request.getHeader(ContactHeader.NAME);
String from = ((SipURI) cont.getAddress().getURI()).toString();
ToHeader th = (ToHeader) request.getHeader("To");
String to = ((SipURI) th.getAddress().getURI()).getUser();
Log.info("Request accepted ( Authorized ) " + from + " " + to);
//CallControlComponent.self.registrations.put(to, from);
return SipService.messageFactory.createResponse(Response.OK, request);
}
public boolean checkAuthorization(Request request, DigestServerAuthenticationMethod dsam)
{
Log.info("checkAuthorization ");
AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
if (authorizationHeader == null)
{
Log.info("Authentication failed: Authorization header missing.");
return false;
}
else
{
String username = JiveGlobals.getProperty("videobridge.sip.username", "videobridge");
String password = JiveGlobals.getProperty("videobridge.sip.password", "videobridge");
String username_h = authorizationHeader.getUsername();
Log.info("checkAuthorization " + username_h + " " + username + " " + password);
if (username_h == null) return false;
if (username_h.indexOf('@') != -1) username_h = username_h.substring(0, username_h.indexOf('@'));
// If user names are not equal, authorization failed
if (!username.equals(username_h)) return false;
return dsam.doAuthenticate(request, authorizationHeader, username_h, password);
}
}
public class DigestServerAuthenticationMethod
{
private String defaultRealm;
private final Random random;
private final Hashtable<String, MessageDigest> algorithms;
private final char[] toHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* Default constructor.
* @param defaultRealm Realm to use when realm part is not specified in authentication headers.
* @param algorithms List of algorithms that can be used in authentication.
* @throws NoSuchAlgorithmException If one of algorithms specified in <i>algorithms</i> is not realized in current Java version.
*/
public DigestServerAuthenticationMethod(String defaultRealm, String[] algorithms) throws NoSuchAlgorithmException
{
this.defaultRealm = defaultRealm;
this.algorithms = new Hashtable<String, MessageDigest>();
random = new Random(System.currentTimeMillis());
for (String algorithm : algorithms)
this.algorithms.put(algorithm, MessageDigest.getInstance(algorithm));
}
/**
* @return The default realm that is to be used when no domain is specified in authentication headers.
*/
public String getDefaultRealm()
{
return defaultRealm;
}
/**
* @return The algorithm that is to be used when no algorithm is specified in authentication headers.
*/
public String getPreferredAlgorithm()
{
return algorithms.keys().nextElement();
}
/**
* Generate the challenge string.
* @param algorithm Encryption algorithm. "MD5", for example.
* @return a generated nonce. Empty string if specified <i>algorithm</i> is not recognized.
*/
public String generateNonce(String algorithm)
{
MessageDigest messageDigest = algorithms.get(algorithm);
if (messageDigest == null) return "";
// Get the time of day and run MD5 over it.
long time = System.currentTimeMillis();
long pad = random.nextLong();
String nonceString = (new Long(time)).toString() + (new Long(pad)).toString();
byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
// Convert the mdbytes array into a hex string.
return toHexString(mdbytes);
}
/**
* Actually performs authentication of subscriber.
* @param authHeader Authroization header from the SIP request.
* @param request Request to authorize
* @param user Username to check with
* @param password to check with
* @return true if request is authorized, false in other case.
*/
public boolean doAuthenticate(Request request, AuthorizationHeader authHeader, String user, String password)
{
Log.info("doAuthenticate " + user + " " + password);
String username = authHeader.getUsername();
if (username == null || !username.equals(user))
return false;
String realm = authHeader.getRealm();
if (realm == null)
realm = defaultRealm;
URI uri = authHeader.getURI();
if (uri == null) return false;
String algorithm = authHeader.getAlgorithm();
if (algorithm == null)
algorithm = getPreferredAlgorithm();
MessageDigest messageDigest = algorithms.get(algorithm);
if (messageDigest == null) return false;
byte mdbytes[];
String A1 = username + ":" + realm + ":" + password;
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
mdbytes = messageDigest.digest(A1.getBytes());
String HA1 = toHexString(mdbytes);
mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String nonce = authHeader.getNonce();
String cnonce = authHeader.getCNonce();
String KD = HA1 + ":" + nonce;
if (cnonce != null)
KD += ":" + cnonce;
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
String response = authHeader.getResponse();
return mdString.compareTo(response) == 0;
}
public 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);
}
}
class SipServerCallback {
public void addSipListener(String key, SipListener sipListener) {
synchronized (sipListenersTable) {
if (!sipListenersTable.containsKey(key)) {
sipListenersTable.put(key, sipListener);
} else {
Log.error("key: " + key + " already mapped!");
}
}
}
public void removeSipListener(String key) {
synchronized (sipListenersTable) {
if (sipListenersTable.containsKey(key)) {
sipListenersTable.remove(key);
} else {
Log.error("could not find a SipListener " + "entry to remove with the key:" + key);
}
}
}
}
}