/*
* 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.servlet.sip.pbx.registrar;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.ServletException;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.URI;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mobicents.servlet.sip.pbx.ResponseException;
import org.mobicents.servlet.sip.pbx.SipHeaders;
import org.mobicents.servlet.sip.pbx.URIUtil;
import org.mobicents.servlet.sip.pbx.location.Binding;
import org.mobicents.servlet.sip.pbx.location.LocationService;
/**
* @author Thomas Leseney
*/
public class RegistrarService {
private static Log logger = LogFactory.getLog(RegistrarService.class);
private LocationService locationService;
private SipFactory sipFactory;
private int minExpires = 60;
private int maxExpires = 86400;
private int defaultExpires = 3600;
private java.text.DateFormat dateFormat;
public RegistrarService() {
dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}
public void setLocationService(LocationService locationService) {
this.locationService = locationService;
}
public void setSipFactory(SipFactory sipFactory) {
this.sipFactory = sipFactory;
}
public void doRegister(SipServletRequest request) throws IOException, ServletException {
boolean success = false;
try {
locationService.beginTransaction();
URI uri = request.getRequestURI();
if (!isLocal(uri)) {
throw new ResponseException(SipServletResponse.SC_FORBIDDEN, "Not local domain: " + uri);
}
String aor = URIUtil.toCanonical(request.getTo().getURI());
logger.debug("Handling register for aor: " + aor);
List<Binding> bindings = locationService.getBindings(aor);
if (bindings == null) {
bindings = Collections.emptyList();
}
Iterator<Address> it = request.getAddressHeaders(SipHeaders.CONTACT);
if (it.hasNext()) {
List<Address> contacts = new ArrayList<Address>();
boolean wildcard = false;
while (it.hasNext()) {
Address contact = it.next();
if (contact.isWildcard()) {
wildcard = true;
if (it.hasNext() || contacts.size() > 0 || request.getExpires() > 0) {
throw new ResponseException(SipServletResponse.SC_BAD_REQUEST, "invalid wildcard");
}
}
contacts.add(contact);
}
String callId = request.getCallId();
int cseq = getCSeq(request);
if (wildcard) {
for (Binding binding : bindings) {
if (callId.equals(binding.getCallId()) && cseq < binding.getCseq()) {
throw new ResponseException(SipServletResponse.SC_SERVER_INTERNAL_ERROR, "lower cseq");
}
locationService.removeBinding(binding);
}
} else {
for (Address contact : contacts) {
int expires = -1;
expires = contact.getExpires();
if (expires < 0) {
expires = request.getExpires();
}
if (expires != 0) {
if (expires < 0) {
expires = defaultExpires;
}
if (expires > maxExpires) {
expires = maxExpires;
} else if (expires < minExpires) {
ResponseException e = new ResponseException(SipServletResponse.SC_INTERVAL_TOO_BRIEF);
e.addHeader(SipHeaders.MIN_EXPIRES, Integer.toString(minExpires));
throw e;
}
}
boolean exist = false;
for (Binding binding : bindings) {
URI contactUri = null;
try {
contactUri = sipFactory.createURI(binding.getContact());
} catch (ServletParseException e) {
throw new ResponseException(SipServletResponse.SC_SERVER_INTERNAL_ERROR, "Invalid URI: " + binding.getContact());
}
if (contact.getURI().equals(contactUri)) {
exist = true;
if (callId.equals(binding.getCallId()) && cseq < binding.getCseq()) {
throw new ResponseException(SipServletResponse.SC_SERVER_INTERNAL_ERROR, "lower cseq");
}
if (expires == 0) {
locationService.removeBinding(binding);
logger.debug("Removed binding: " + binding);
} else {
binding.setContact(contact.getURI().toString());
binding.setCallId(callId);
binding.setCseq(cseq);
binding.setExpires(expires);
locationService.updateBinding(binding);
logger.debug("Updated binding: " + binding);
}
}
}
if (!exist && expires != 0) {
Binding binding = locationService.createBinding(aor, contact.getURI().toString());
binding.setCallId(callId);
binding.setCseq(cseq);
binding.setExpires(expires);
locationService.addBinding(binding);
logger.debug("Added binding: " + binding);
}
}
}
bindings = locationService.getBindings(aor);
}
SipServletResponse ok = request.createResponse(SipServletResponse.SC_OK);
ok.addHeader(SipHeaders.DATE, dateFormat.format(new Date()));
if (bindings != null) {
for (Binding binding : bindings) {
ok.addHeader(SipHeaders.CONTACT, "<" + binding.getContact() + ">;expires=" + binding.getExpires());
}
}
ok.send();
success = true;
} catch (ResponseException e) {
logger.warn("Error while handling register", e);
request.createResponse(e.getStatus(), e.getReason()).send();
} catch (Throwable t) {
logger.warn("Error while handling register", t);
request.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR, t.getMessage());
} finally {
if (success) {
locationService.commitTransaction();
} else {
locationService.rollbackTransaction();
}
}
}
private boolean isLocal(URI uri) {
return true;
}
private int getCSeq(SipServletRequest request) throws ResponseException {
String s = request.getHeader(SipHeaders.CSEQ);
try {
return Integer.parseInt(s.substring(0, s.indexOf(' ')));
} catch (Exception e) {
throw new ResponseException(400, "invalid cseq");
}
}
}