org.mobicents.servlet.sip.message.SipServletMessageImpl Maven / Gradle / Ivy
/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
*/
package org.mobicents.servlet.sip.message;
import gov.nist.javax.sip.header.HeaderExt;
import gov.nist.javax.sip.header.SIPHeader;
import gov.nist.javax.sip.stack.SIPTransaction;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import javax.servlet.sip.Address;
import javax.servlet.sip.Parameterable;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipSession;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ServerTransaction;
import javax.sip.SipFactory;
import javax.sip.Transaction;
import javax.sip.header.AcceptLanguageHeader;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentLanguageHeader;
import javax.sip.header.ContentLengthHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.Header;
import javax.sip.header.HeaderAddress;
import javax.sip.header.RequireHeader;
import javax.sip.header.SupportedHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Message;
import javax.sip.message.Request;
import org.apache.log4j.Logger;
import org.mobicents.ha.javax.sip.ClusteredSipStack;
import org.mobicents.ha.javax.sip.ReplicationStrategy;
import org.mobicents.servlet.sip.JainSipUtils;
import org.mobicents.servlet.sip.address.AddressImpl;
import org.mobicents.servlet.sip.address.AddressImpl.ModifiableRule;
import org.mobicents.servlet.sip.address.ParameterableHeaderImpl;
import org.mobicents.servlet.sip.core.MobicentsExtendedListeningPoint;
import org.mobicents.servlet.sip.core.SipContext;
import org.mobicents.servlet.sip.core.message.MobicentsSipServletMessage;
import org.mobicents.servlet.sip.core.security.SipPrincipal;
import org.mobicents.servlet.sip.core.session.MobicentsSipApplicationSession;
import org.mobicents.servlet.sip.core.session.MobicentsSipSession;
import org.mobicents.servlet.sip.core.session.MobicentsSipSessionKey;
import org.mobicents.servlet.sip.core.session.SessionManagerUtil;
import org.mobicents.servlet.sip.core.session.SipApplicationSessionKey;
import org.mobicents.servlet.sip.startup.StaticServiceHolder;
/**
* Implementation of SipServletMessage
*
* @author mranga
* @author [email protected]
*
*/
public abstract class SipServletMessageImpl implements MobicentsSipServletMessage, Externalizable {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(SipServletMessageImpl.class
.getCanonicalName());
private static final String CONTENT_TYPE_TEXT = "text";
private static final String CONTENT_TYPE_MULTIPART = "multipart";
private static final String MULTIPART_BOUNDARY = "boundary";
private static final String MULTIPART_START = "start";
private static final String MULTIPART_BOUNDARY_DELIM = "--";
private static final String LINE_RETURN_DELIM = "\n";
public static final String REL100_OPTION_TAG = "100rel";
// private static final String HCOLON = " : ";
protected Message message;
protected SipFactoryImpl sipFactoryImpl;
protected MobicentsSipSessionKey sessionKey;
//lazy loaded and not serialized to avoid unecessary replication
protected transient MobicentsSipSession sipSession;
protected Map attributes;
// Made it transient for Issue 1523 : http://code.google.com/p/mobicents/issues/detail?id=1523
// NotSerializableException happens if a message is stored in the sip session during HA
private transient Transaction transaction;
// used for failover to recover the transaction
private String transactionId;
private boolean transactionType;
// We need this object separate from transaction.getApplicationData, because the actualy transaction
// may be create later and we still need to accumulate useful data. Also the transaction might be
// cleaned up earlier. The transaction and this object have different lifecycle.
protected TransactionApplicationData transactionApplicationData;
protected HeaderForm headerForm = HeaderForm.DEFAULT;
// IP address of the next upstream/downstream hop from which this message
// was received. Applications can determine the actual IP address of the UA
// that originated the message from the message Via header fields.
// But for upstream - thats a proxy stuff, fun with ReqURI, RouteHeader
//protected transient InetAddress remoteAddr = null;
//protected transient int remotePort = -1;
protected transient String transport = null;
protected String currentApplicationName = null;
protected transient SipPrincipal userPrincipal;
protected boolean isMessageSent;
// Made it transient for Issue 1523 : http://code.google.com/p/mobicents/issues/detail?id=1523
// NotSerializableException happens if a message is stored in the sip session during HA
protected transient Dialog dialog;
protected transient String method;
// needed for orphan routing
boolean orphan;
private String appSessionId;
// needed for externalizable
public SipServletMessageImpl () {}
protected SipServletMessageImpl(Message message,
SipFactoryImpl sipFactoryImpl, Transaction transaction,
MobicentsSipSession sipSession, Dialog dialog) {
if (sipFactoryImpl == null)
throw new NullPointerException("Null factory");
if (message == null)
throw new NullPointerException("Null message");
// if (sipSession == null)
// throw new NullPointerException("Null session");
this.sipFactoryImpl = sipFactoryImpl;
this.message = message;
this.transaction = transaction;
if(sipSession != null) {
this.sessionKey = sipSession.getKey();
}
if(transaction != null && getMethod().equals(Request.INVITE)) {
if(transaction.getApplicationData() != null) {
this.transactionApplicationData = (TransactionApplicationData) transaction.getApplicationData();
}
}
if(transactionApplicationData == null){
this.transactionApplicationData = new TransactionApplicationData(this);
}
isMessageSent = false;
this.dialog = dialog;
if(sipSession != null && dialog != null) {
sipSession.setSessionCreatingDialog(dialog);
if(dialog.getApplicationData() == null) {
if (logger.isDebugEnabled()) {
logger.debug("dialog app data is null, setting it to " + transactionApplicationData);
}
dialog.setApplicationData(transactionApplicationData);
}
}
// good behaviour, lets make some default
//seems like bad behavior finally
//check http://forums.java.net/jive/thread.jspa?messageID=260944
// => commented out
// if (this.message.getContentEncoding() == null)
// try {
// this.message.addHeader(this.headerFactory
// .createContentEncodingHeader(this.defaultEncoding));
// } catch (ParseException e) {
// logger.debug("Couldnt add deafualt enconding...");
// e.printStackTrace();
// }
if (transaction != null && transaction.getApplicationData() == null) {
transaction.setApplicationData(transactionApplicationData);
}
}
private void checkCommitted() {
if(this.isCommitted()) {
throw new IllegalStateException("This message is in committed state. You can not modify it");
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#addAcceptLanguage(java.util.Locale)
*/
public void addAcceptLanguage(Locale locale) {
checkCommitted();
AcceptLanguageHeader ach = SipFactoryImpl.headerFactory
.createAcceptLanguageHeader(locale);
message.addHeader(ach);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#addAddressHeader(java.lang.String, javax.servlet.sip.Address, boolean)
*/
public void addAddressHeader(String name, Address addr, boolean first)
throws IllegalArgumentException {
checkCommitted();
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled()) {
logger.debug("Adding address header [" + hName + "] as first ["
+ first + "] value [" + addr + "]");
}
//we should test for
//This method can be used with headers which are defined to contain one
//or more entries matching (name-addr | addr-spec) *(SEMI generic-param) as defined in RFC 3261
// if (!isAddressTypeHeader(hName)) {
// logger.error("Header [" + hName + "] is not address type header");
// throw new IllegalArgumentException("Header[" + hName
// + "] is not of an address type");
// }
if (isSystemHeaderAndNotGruu(getModifiableRule(hName), (Parameterable)addr.getURI())) {
logger.error("Error, can't add system header [" + hName + "]");
throw new IllegalArgumentException("Header[" + hName
+ "] is system header, cant add, modify it!!!");
}
if (hName.equalsIgnoreCase("From") || hName.equalsIgnoreCase("To")) {
logger.error("Error, can't add From or To header [" + hName + "]");
throw new IllegalArgumentException(
"Can't add From or To header, see JSR 289 Section 4.1.2");
}
try {
String nameToAdd = getCorrectHeaderName(hName);
Header h = SipFactoryImpl.headerFactory.createHeader(nameToAdd, addr.toString());
if (first) {
this.message.addFirst(h);
} else {
this.message.addLast(h);
}
} catch (Exception e) {
throw new IllegalArgumentException("Error adding header", e);
}
}
public void addHeaderInternal(String name, String value, boolean bypassSystemHeaderCheck) {
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled())
logger.debug("Adding header under name [" + hName + "]");
if (!bypassSystemHeaderCheck && isSystemHeader(getModifiableRule(hName))) {
logger.error("Cant add system header [" + hName + "]");
throw new IllegalArgumentException("Header[" + hName
+ "] is system header, cant add,cant modify it!!!");
}
String nameToAdd = getCorrectHeaderName(hName);
try {
// Fix to Issue 1015 by alexander.kozlov.IV
if(JainSipUtils.SINGLETON_HEADER_NAMES.contains(name)) {
Header header = SipFactory.getInstance().createHeaderFactory().createHeader(nameToAdd, value);
this.message.setHeader(header);
} else {
// Dealing with Allow:INVITE, ACK, CANCEL, OPTIONS, BYE kind of values
if(JainSipUtils.LIST_HEADER_NAMES.contains(name)) {
List headers = SipFactory.getInstance().createHeaderFactory()
.createHeaders(name + ":" + value);
for (Header header : headers) {
this.message.addHeader(header);
}
} else {
// Extension Header: those cannot be lists. See jain sip issue 270
Header header = SipFactory.getInstance().createHeaderFactory()
.createHeader(name, value);
this.message.addLast(header);
}
}
} catch (Exception ex) {
throw new IllegalArgumentException("Illegal args supplied ", ex);
}
}
public void setHeaderInternal(String name, String value, boolean bypassSystemHeaderCheck) {
if(name == null) {
throw new NullPointerException ("name parameter is null");
}
if(value == null) {
throw new NullPointerException ("value parameter is null");
}
if(!bypassSystemHeaderCheck && isSystemHeader(getModifiableRule(name))) {
throw new IllegalArgumentException(name + " is a system header !");
}
try {
Header header = SipFactory.getInstance().createHeaderFactory()
.createHeader(name, value);
this.message.setHeader(header);
} catch (Exception e) {
throw new IllegalArgumentException("Error creating header!", e);
}
}
//check if the submitted value is of the form header-value *(COMMA header-value)
// private boolean isMultipleValue(String value) {
// StringTokenizer tokenizer = new StringTokenizer(value, ",");
// tokenizer.nextToken();
// return tokenizer.hasMoreTokens();
// }
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#addHeader(java.lang.String, java.lang.String)
*/
public void addHeader(String name, String value) {
checkCommitted();
addHeaderInternal(name, value, false);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#addParameterableHeader(java.lang.String, javax.servlet.sip.Parameterable, boolean)
*/
public void addParameterableHeader(String name, Parameterable param,
boolean first) {
checkCommitted();
try {
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled())
logger.debug("Adding parametrable header under name [" + hName
+ "] as first [" + first + "] value [" + param + "]");
String body = param.toString();
String nameToAdd = getCorrectHeaderName(hName);
Header header = SipFactoryImpl.headerFactory.createHeader(nameToAdd,
body);
if (first)
this.message.addFirst(header);
else
this.message.addLast(header);
} catch (Exception ex) {
throw new IllegalArgumentException("Illegal args supplied", ex);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAcceptLanguage()
*/
public Locale getAcceptLanguage() {
// See section 14.4 of RFC 2616 (HTTP/1.1) for more information about how the Accept-Language header
// must interpreted to determine the preferred language of the client.
Locale preferredLocale = null;
float q = 0;
Iterator it = (Iterator) this.message
.getHeaders(AcceptLanguageHeader.NAME);
while (it.hasNext()) {
AcceptLanguageHeader alh = (AcceptLanguageHeader) it.next();
if(preferredLocale == null) {
preferredLocale = alh.getAcceptLanguage();
q = alh.getQValue();
} else {
if(alh.getQValue() > q) {
preferredLocale = alh.getAcceptLanguage();
q = alh.getQValue();
}
}
}
return preferredLocale;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAcceptLanguages()
*/
public Iterator getAcceptLanguages() {
LinkedList ll = new LinkedList();
Iterator it = (Iterator) this.message
.getHeaders(AcceptLanguageHeader.NAME);
while (it.hasNext()) {
AcceptLanguageHeader alh = (AcceptLanguageHeader) it.next();
ll.add(alh.getAcceptLanguage());
}
return ll.iterator();
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAddressHeader(java.lang.String)
*/
@SuppressWarnings("unchecked")
public Address getAddressHeader(String name) throws ServletParseException {
if (name == null)
throw new NullPointerException();
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled())
logger.debug("Fetching address header for name [" + hName + "]");
// if (!isAddressTypeHeader(hName)) {
// logger.error("Header of name [" + hName
// + "] is not address type header!!!");
// throw new ServletParseException("Header of type [" + hName
// + "] cant be parsed to address, wrong content type!!!");
// }
String nameToSearch = getCorrectHeaderName(hName);
ListIterator headers = (ListIterator) this.message
.getHeaders(nameToSearch);
ListIterator lit = headers;
if (lit != null && lit.hasNext()) {
Header first = lit.next();
if (first instanceof HeaderAddress) {
try {
if(this.isCommitted()) {
return new AddressImpl((HeaderAddress) first, ModifiableRule.NotModifiable);
} else {
return new AddressImpl((HeaderAddress) first, getModifiableRule(hName));
}
} catch (ParseException e) {
throw new ServletParseException("Bad address " + first);
}
} else {
Parameterable parametrable = createParameterable(first, first.getName(), message instanceof Request);
try {
logger.debug("parametrable Value " + parametrable.getValue());
if(this.isCommitted()) {
return new AddressImpl(SipFactoryImpl.addressFactory.createAddress(parametrable.getValue()), ((ParameterableHeaderImpl)parametrable).getInternalParameters(), ModifiableRule.NotModifiable);
} else {
return new AddressImpl(SipFactoryImpl.addressFactory.createAddress(parametrable.getValue()), ((ParameterableHeaderImpl)parametrable).getInternalParameters(), getModifiableRule(hName));
}
} catch (ParseException e) {
throw new ServletParseException("Impossible to parse the following header " + name + " as an address.", e);
}
}
}
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAddressHeaders(java.lang.String)
*/
@SuppressWarnings("unchecked")
public ListIterator getAddressHeaders(String name)
throws ServletParseException {
String hName = getFullHeaderName(name);
// Fix from Thomas Leseney from Nexcom systems
// if (!isAddressTypeHeader(hName)) {
// throw new ServletParseException(
// "Header [" + hName + "] is not address type header");
// }
LinkedList retval = new LinkedList();
String nameToSearch = getCorrectHeaderName(hName);
for (Iterator it = this.message.getHeaders(nameToSearch); it
.hasNext();) {
Header header = (Header) it.next();
if (header instanceof HeaderAddress) {
HeaderAddress aph = (HeaderAddress) header;
try {
AddressImpl addressImpl = new AddressImpl(
aph, getModifiableRule(hName));
retval.add(addressImpl);
} catch (ParseException ex) {
throw new ServletParseException("Bad header", ex);
}
} else {
Parameterable parametrable = createParameterable(header, header.getName(), message instanceof Request);
try {
AddressImpl addressImpl = new AddressImpl(SipFactoryImpl.addressFactory.createAddress(parametrable.getValue()), ((ParameterableHeaderImpl)parametrable).getInternalParameters(), getModifiableRule(hName));
retval.add(addressImpl);
} catch (ParseException e) {
throw new ServletParseException("Impossible to parse the following header " + name + " as an address.", e);
}
}
}
return retval.listIterator();
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletMessage#getApplicationSession()
*/
public SipApplicationSession getApplicationSession() {
MobicentsSipApplicationSession sipApplicationSession = getSipApplicationSession(true);
if(sipApplicationSession == null) {
return null;
} else {
return sipApplicationSession.getFacade();
}
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletMessage#getApplicationSession(boolean)
*/
public SipApplicationSession getApplicationSession(boolean create) {
MobicentsSipApplicationSession sipApplicationSession = getSipApplicationSession(create);
if(sipApplicationSession == null) {
return null;
} else {
return sipApplicationSession.getFacade();
}
}
public MobicentsSipApplicationSession getSipApplicationSession(boolean create) {
MobicentsSipSession sipSession = getSipSession();
if(sipSession != null) {
MobicentsSipApplicationSession sipApplicationSession = sipSession.getSipApplicationSession();
if(sipApplicationSession != null) {
return sipApplicationSession;
}
}
String applicationName = getCurrentApplicationName();
if(sessionKey != null) {
applicationName = sessionKey.getApplicationName();
} else {
if(this instanceof SipServletRequestImpl && isOrphan()) {
if(logger.isDebugEnabled()) {
logger.debug("Orphans session " + applicationName + " " + sessionKey);
}
orphan = true;
sessionKey = SessionManagerUtil.getSipSessionKey(
SessionManagerUtil.getSipApplicationSessionKey(applicationName, getAppSessionId(), null).getId(),
applicationName, message, false);
}
}
if(applicationName != null && sessionKey != null) {
final SipContext sipContext = sipFactoryImpl.getSipApplicationDispatcher().findSipApplication(applicationName);
//call id not needed anymore since the sipappsessionkey is not a callid anymore but a random uuid
final SipApplicationSessionKey sipApplicationSessionKey = SessionManagerUtil.getSipApplicationSessionKey(
applicationName,
sessionKey.getApplicationSessionId(), null);
if(logger.isDebugEnabled()) {
logger.debug("trying to load the sip app session with Key " + sipApplicationSessionKey + " and create = " + true);
}
MobicentsSipApplicationSession applicationSession = sipContext.getSipManager().getSipApplicationSession(sipApplicationSessionKey, create);
if(applicationSession != null) {
// application session can be null if create is false and it is an orphan request
applicationSession.setOrphan(isOrphan());
}
return applicationSession;
}
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAttribute(java.lang.String)
*/
public Object getAttribute(String name) {
if (name == null)
throw new NullPointerException("Attribute name can not be null.");
return this.getAttributeMap().get(name);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getAttributeNames()
*/
public Enumeration getAttributeNames() {
Vector names = new Vector(this.getAttributeMap().keySet());
return names.elements();
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getCallId()
*/
public String getCallId() {
CallIdHeader id = (CallIdHeader) this.message
.getHeader(getCorrectHeaderName(CallIdHeader.NAME));
if (id != null)
return id.getCallId();
else
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getCharacterEncoding()
*/
public String getCharacterEncoding() {
if (this.message.getContentEncoding() != null) {
return this.message.getContentEncoding().getEncoding();
} else {
ContentTypeHeader cth = (ContentTypeHeader)
this.message.getHeader(ContentTypeHeader.NAME);
if(cth == null) return null;
return cth.getParameter("charset");
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getContent()
*/
public Object getContent() throws IOException, UnsupportedEncodingException {
ContentTypeHeader contentTypeHeader = (ContentTypeHeader)
this.message.getHeader(ContentTypeHeader.NAME);
if(contentTypeHeader != null && logger.isDebugEnabled()) {
logger.debug("Content type " + contentTypeHeader.getContentType());
logger.debug("Content sub type " + contentTypeHeader.getContentSubType());
}
if(contentTypeHeader!= null && CONTENT_TYPE_TEXT.equals(contentTypeHeader.getContentType())) {
String content = null;
if(message.getRawContent() != null) {
String charset = this.getCharacterEncoding();
if(charset == null) {
content = new String(message.getRawContent());
} else {
content = new String(message.getRawContent(), charset);
}
} else {
content = "";
}
return content;
} else if(contentTypeHeader!= null && CONTENT_TYPE_MULTIPART.equals(contentTypeHeader.getContentType())) {
try {
return new MimeMultipart(new ByteArrayDataSource(message.getRawContent(),
contentTypeHeader.toString().replaceAll(ContentTypeHeader.NAME+": ", "")));
} catch (MessagingException e) {
logger.warn("Problem with multipart message.", e);
return this.message.getRawContent();
}
} else {
return this.message.getRawContent();
}
}
/**
* Return a mimemultipart from raw Content
* FIXME Doesn't support nested multipart in the body content
* @param contentTypeHeader content type header related to the rawContent
* @param rawContent body content
* @return a mimemultipart from raw Content
*/
private static MimeMultipart getContentAsMimeMultipart(ContentTypeHeader contentTypeHeader, byte[] rawContent) {
// Issue 1123 : http://code.google.com/p/mobicents/issues/detail?id=1123 : Multipart type is supported
String delimiter = contentTypeHeader.getParameter(MULTIPART_BOUNDARY);
String start = contentTypeHeader.getParameter(MULTIPART_START);
MimeMultipart mimeMultipart = new MimeMultipart(contentTypeHeader.getContentSubType());
if (delimiter == null) {
MimeBodyPart mbp = new MimeBodyPart();
DataSource ds = new ByteArrayDataSource(rawContent, contentTypeHeader.getContentSubType());
try {
mbp.setDataHandler(new DataHandler(ds));
mimeMultipart.addBodyPart(mbp);
} catch (MessagingException e) {
throw new IllegalArgumentException("couldn't create the multipart object from the message content " + rawContent, e);
}
} else {
// splitting the body content by delimiter
String[] fragments = new String(rawContent).split(MULTIPART_BOUNDARY_DELIM + delimiter);
for (String fragment: fragments) {
final String trimmedFragment = fragment.trim();
// skipping empty fragment and ending fragment looking like --
if(trimmedFragment.length() > 0 && !MULTIPART_BOUNDARY_DELIM.equals(trimmedFragment)) {
String fragmentHeaders = null;
String fragmentBody = fragment;
// if there is a start, it means that there is probably headers before the content that need to be added to the mime body part
// so we split headers from body content
if(start != null && start.length() > 0) {
int indexOfStart = fragment.indexOf(start);
if(indexOfStart != -1) {
fragmentHeaders = fragmentBody.substring(0, indexOfStart + start.length());
fragmentBody = fragmentBody.substring(indexOfStart + start.length()).trim();
}
}
MimeBodyPart mbp = new MimeBodyPart();
try {
String contentType = contentTypeHeader.getContentSubType();
// check if the body content start with a Content-Type header
// if so we strip it from the content body
if(fragmentBody.startsWith(ContentTypeHeader.NAME)) {
int indexOfLineReturn = fragmentBody.indexOf(LINE_RETURN_DELIM);
contentType = fragmentBody.substring(0, indexOfLineReturn -1).trim();
fragmentBody = fragmentBody.substring(indexOfLineReturn).trim();
}
// setting the content body stripped from the headers
mbp.setContent(fragmentBody, contentType);
// adding the headers to the body part
mbp.addHeaderLine(contentType);
if(fragmentHeaders != null) {
StringTokenizer stringTokenizer = new StringTokenizer(fragmentHeaders, LINE_RETURN_DELIM);
while (stringTokenizer.hasMoreTokens()) {
String token = stringTokenizer.nextToken().trim();
if(token != null && token.length() > 0) {
mbp.addHeaderLine(token);
}
}
}
mimeMultipart.addBodyPart(mbp);
} catch (MessagingException e) {
throw new IllegalArgumentException("couldn't create the multipart object from the message content " + rawContent, e);
}
}
}
}
return mimeMultipart;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getContentLanguage()
*/
public Locale getContentLanguage() {
if (this.message.getContentLanguage() != null)
return this.message.getContentLanguage().getContentLanguage();
else
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getContentLength()
*/
public int getContentLength() {
if (this.message.getContentLength() != null) {
return this.message.getContentLength().getContentLength();
} else {
return 0;
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getContentType()
*/
public String getContentType() {
ContentTypeHeader cth = (ContentTypeHeader) this.message
.getHeader(getCorrectHeaderName(ContentTypeHeader.NAME));
if (cth != null) {
// Fix For Issue http://code.google.com/p/mobicents/issues/detail?id=2659
// getContentType doesn't return the full header value
// String contentType = cth.getContentType();
// String contentSubType = cth.getContentSubType();
// if(contentSubType != null)
// return contentType + "/" + contentSubType;
return ((HeaderExt)cth).getValue();
}
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getExpires()
*/
public int getExpires() {
if (this.message.getExpires() != null)
return this.message.getExpires().getExpires();
else
return -1;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getFrom()
*/
public Address getFrom() {
FromHeader from = (FromHeader) this.message
.getHeader(getCorrectHeaderName(FromHeader.NAME));
// AddressImpl address = new AddressImpl(from.getAddress(), AddressImpl.getParameters((Parameters)from), ModifiableRule.From);
// https://code.google.com/p/sipservlets/issues/detail?id=245
try {
return new AddressImpl(from, ModifiableRule.From);
} catch (ParseException e) {
throw new IllegalArgumentException("Couldn't parse From Header " + from, e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getHeader(java.lang.String)
*/
public String getHeader(String name) {
String nameToSearch = getCorrectHeaderName(name);
String value = null;
if (this.message.getHeader(nameToSearch) != null) {
value = ((SIPHeader) this.message.getHeader(nameToSearch))
.getValue();
}
// if(logger.isDebugEnabled()) {
// logger.debug("getHeader "+ name+ ", value="+ value );
// }
return value;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getHeaderForm()
*/
public HeaderForm getHeaderForm() {
return this.headerForm;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getHeaderNames()
*/
public Iterator getHeaderNames() {
return this.message.getHeaderNames();
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getHeaders(java.lang.String)
*/
public ListIterator getHeaders(String name) {
String nameToSearch = getCorrectHeaderName(name);
ArrayList result = new ArrayList();
try {
ListIterator list = this.message.getHeaders(nameToSearch);
while (list != null && list.hasNext()) {
Header h = list.next();
result.add(((SIPHeader)h).getHeaderValue());
}
} catch (Exception e) {
logger.fatal("Couldnt fetch headers, original name[" + name
+ "], name searched[" + nameToSearch + "]", e);
return result.listIterator();
}
return result.listIterator();
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletMessage#getMethod()
*/
public final String getMethod() {
if(method == null) {
method = message instanceof Request ? ((Request) message).getMethod()
: ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
}
return method;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getParameterableHeader(java.lang.String)
*/
public Parameterable getParameterableHeader(String name)
throws ServletParseException {
if (name == null)
throw new NullPointerException(
"Parametrable header name cant be null!!!");
String nameToSearch = getCorrectHeaderName(name);
Header h = this.message.getHeader(nameToSearch);
if(!isParameterable(name)) {
throw new ServletParseException(name + " header is not parameterable !");
}
if(h == null) {
return null;
}
return createParameterable(h, getFullHeaderName(name), message instanceof Request);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getParameterableHeaders(java.lang.String)
*/
public ListIterator getParameterableHeaders(String name)
throws ServletParseException {
ListIterator headers = this.message
.getHeaders(getCorrectHeaderName(name));
ArrayList result = new ArrayList();
while (headers != null && headers.hasNext())
result.add(createParameterable(headers.next(),
getFullHeaderName(name), message instanceof Request));
if(!isParameterable(name)) {
throw new ServletParseException(name + " header is not parameterable !");
}
return result.listIterator();
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getProtocol()
*/
public String getProtocol() {
// For this version of the SIP Servlet API this is always "SIP/2.0"
return "SIP/2.0";
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getRawContent()
*/
public byte[] getRawContent() throws IOException {
if (message != null)
return message.getRawContent();
else
return null;
}
/**
* {@inheritDoc}
*/
public String getInitialRemoteAddr() {
return transactionApplicationData.getInitialRemoteHostAddress();
}
/**
* {@inheritDoc}
*/
public int getInitialRemotePort() {
return transactionApplicationData.getInitialRemotePort();
}
/**
* {@inheritDoc}
*/
public String getInitialTransport() {
return transactionApplicationData.getInitialRemoteTransport();
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getRemoteAddr()
*/
public String getRemoteAddr() {
if(getTransaction() != null) {
if(((SIPTransaction)getTransaction()).getPeerPacketSourceAddress() != null &&
((SIPTransaction)getTransaction()).getPeerPacketSourceAddress().getHostAddress() != null) {
return ((SIPTransaction)getTransaction()).getPeerPacketSourceAddress().getHostAddress();
} else {
return ((SIPTransaction)getTransaction()).getPeerAddress();
}
} else {
ViaHeader via = (ViaHeader) message.getHeader(ViaHeader.NAME);
// https://code.google.com/p/sipservlets/issues/detail?id=137
boolean isExternal = sipFactoryImpl.getSipApplicationDispatcher().isViaHeaderExternal(via);
if(message instanceof Request && !isExternal) {
// locally generated messages should return null as per Javadoc
return null;
}
if(via == null) {
return null;
} else {
return via.getHost();
}
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getRemotePort()
*/
public int getRemotePort() {
int port = -1;
if(getTransaction() != null) {
if(((SIPTransaction)getTransaction()).getPeerPacketSourceAddress() != null) {
port = ((SIPTransaction)getTransaction()).getPeerPacketSourcePort();
} else {
port = ((SIPTransaction)getTransaction()).getPeerPort();
}
} else {
ViaHeader via = (ViaHeader) message.getHeader(ViaHeader.NAME);
// https://code.google.com/p/sipservlets/issues/detail?id=137
boolean isExternal = sipFactoryImpl.getSipApplicationDispatcher().isViaHeaderExternal(via);
if(message instanceof Request && !isExternal) {
// locally generated messages should return -1 as per Javadoc
return -1;
}
if(via != null) {
port = via.getPort();
}
}
if(port<=0) {
return 5060;
}
else {
return port;
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getTransport()
*/
public String getTransport() {
if(getTransaction() != null) {
return ((SIPTransaction)getTransaction()).getTransport();
} else {
return null;
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getRemoteUser()
*/
public String getRemoteUser() {
// This method returns non-null only if the user is authenticated
if(this.userPrincipal != null)
return this.userPrincipal.getName();
return null;
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletMessage#getSession()
*/
public SipSession getSession() {
return getSession(true);
}
/*
* (non-Javadoc)
*
* @see javax.servlet.sip.SipServletMessage#getSession(boolean)
*/
public SipSession getSession(boolean create) {
MobicentsSipSession session = getSipSession();
if (session == null && create) {
MobicentsSipApplicationSession sipApplicationSessionImpl = (MobicentsSipApplicationSession)getSipApplicationSession(create);
MobicentsSipSessionKey sessionKey = SessionManagerUtil.getSipSessionKey(sipApplicationSessionImpl.getKey().getId(), currentApplicationName, message, false);
session = sipApplicationSessionImpl.getSipContext().getSipManager().getSipSession(sessionKey, create,
sipFactoryImpl, sipApplicationSessionImpl);
session.setSessionCreatingTransactionRequest(this);
session.setOrphan(isOrphan());
sessionKey = session.getKey();
}
if(session != null) {
return session.getFacade();
}
return null;
}
/**
* Retrieve the sip session implementation
* @return the sip session implementation
*/
public final MobicentsSipSession getSipSession() {
if(sipSession == null && sessionKey == null) {
sessionKey = getSipSessionKey();
if(logger.isDebugEnabled()) {
logger.debug("sessionKey is " + sessionKey);
}
if(sessionKey == null) {
if(sipSession == null) {
if(transactionApplicationData != null) {
this.sessionKey = transactionApplicationData.getSipSessionKey();
if(logger.isDebugEnabled()) {
logger.debug("session Key is " + sessionKey + ", retrieved from the txAppData " + transactionApplicationData);
}
} else if (transaction != null && transaction.getApplicationData() != null) {
this.sessionKey = ((TransactionApplicationData)transaction.getApplicationData()).getSipSessionKey();
if(logger.isDebugEnabled()) {
logger.debug("session Key is " + sessionKey + ", retrieved from the transaction txAppData " +
(TransactionApplicationData)transaction.getApplicationData());
}
} else {
if(logger.isDebugEnabled()) {
logger.debug("txAppData and transaction txAppData are both null, there is no wya to retrieve the sessionKey anymore");
}
}
} else {
this.sessionKey = sipSession.getKey();
}
}
if(logger.isDebugEnabled()) {
logger.debug("sessionKey is " + sessionKey);
}
}
if(sipSession == null && sessionKey != null) {
if(logger.isDebugEnabled()) {
logger.debug("session is null, trying to load the session from the sessionKey " + sessionKey);
}
final String applicationName = sessionKey.getApplicationName();
final SipContext sipContext = sipFactoryImpl.getSipApplicationDispatcher().findSipApplication(applicationName);
SipApplicationSessionKey sipApplicationSessionKey = new SipApplicationSessionKey(sessionKey.getApplicationSessionId(), sessionKey.getApplicationName(), null);
MobicentsSipApplicationSession sipApplicationSession = sipContext.getSipManager().getSipApplicationSession(sipApplicationSessionKey, false);
sipSession = sipContext.getSipManager().getSipSession(sessionKey, false, sipFactoryImpl, sipApplicationSession);
if(logger.isDebugEnabled()) {
if(sipSession == null) {
logger.debug("couldn't find any session with sessionKey " + sessionKey);
} else {
logger.debug("reloaded session session " + sipSession + " with sessionKey " + sessionKey);
}
}
}
return sipSession;
}
/**
* @param session the session to set
*/
public void setSipSession(MobicentsSipSession session) {
// we store the session in JVM to cope with race conditions on session invalidation
// See Issue 1294 http://code.google.com/p/mobicents/issues/detail?id=1294
// but it will not be persisted to avoid unecessary replication if the message is persisted
this.sipSession = session;
if (session != null){
this.sessionKey = session.getKey();
} else {
this.sessionKey = null;
}
}
/**
* @param session the session to set
*/
public MobicentsSipSessionKey getSipSessionKey() {
return this.sessionKey;
}
public void setSipSessionKey(MobicentsSipSessionKey sessionKey) {
this.sessionKey = sessionKey;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getTo()
*/
public Address getTo() {
ToHeader to = (ToHeader) this.message
.getHeader(getCorrectHeaderName(ToHeader.NAME));
// return new AddressImpl(to.getAddress(), AddressImpl.getParameters((Parameters)to), ModifiableRule.To);
// https://code.google.com/p/sipservlets/issues/detail?id=245
try {
return new AddressImpl(to, ModifiableRule.From);
} catch (ParseException e) {
throw new IllegalArgumentException("Couldn't parse From Header " + to, e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getUserPrincipal()
*/
public SipPrincipal getUserPrincipal() {
if(this.userPrincipal == null) {
if(this.getSipSession() != null) {
this.userPrincipal = this.getSipSession().getUserPrincipal();
}
}
return this.userPrincipal;
}
public void setUserPrincipal(SipPrincipal principal) {
this.userPrincipal = principal;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#isSecure()
*/
public boolean isSecure() {
return ListeningPoint.TLS.equalsIgnoreCase(JainSipUtils.findTransport(message));
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#isUserInRole(java.lang.String)
*/
public boolean isUserInRole(String role) {
if(this.userPrincipal != null) {
return this.userPrincipal.isUserInRole(role);
}
return false;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#removeAttribute(java.lang.String)
*/
public void removeAttribute(String name) {
if(attributes != null) {
this.attributes.remove(name);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#removeHeader(java.lang.String)
*/
public void removeHeader(String name) {
checkCommitted();
String hName = getFullHeaderName(name);
if(hName.trim().equalsIgnoreCase(ContactHeader.NAME)) {
String header = getHeader(hName);
if (isSystemHeaderAndNotGruu(getModifiableRule(hName), header)) {
throw new IllegalArgumentException("Cant remove system header["
+ hName + "]");
}
} else {
if (isSystemHeader(getModifiableRule(hName))) {
throw new IllegalArgumentException("Cant remove system header["
+ hName + "]");
}
}
if (hName.equalsIgnoreCase("From") || hName.equalsIgnoreCase("To")) {
logger.error("Error, can't remove From or To header [" + hName + "]");
throw new IllegalArgumentException(
"Cant remove From or To header, see JSR 289 Section 4.1.2");
}
String nameToSearch = getCorrectHeaderName(hName);
this.message.removeHeader(nameToSearch);
}
public void removeHeaderInternal(String name, boolean bypassSystemHeaderCheck) {
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled())
logger.debug("Removing header under name [" + hName + "]");
if (!bypassSystemHeaderCheck && isSystemHeader(getModifiableRule(hName))) {
logger.error("Cant remove system header [" + hName + "]");
throw new IllegalArgumentException("Header[" + hName
+ "] is system header, can't remove it!!!");
}
String nameToRemove = getCorrectHeaderName(hName);
try {
message.removeHeader(nameToRemove);
} catch (Exception ex) {
throw new IllegalArgumentException("Illegal args supplied ", ex);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#send()
*/
public abstract void send() throws IOException;
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setAcceptLanguage(java.util.Locale)
*/
public void setAcceptLanguage(Locale locale) {
checkCommitted();
AcceptLanguageHeader alh = SipFactoryImpl.headerFactory
.createAcceptLanguageHeader(locale);
this.message.setHeader(alh);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setAddressHeader(java.lang.String, javax.servlet.sip.Address)
*/
public void setAddressHeader(String name, Address addr) {
checkCommitted();
String hName = getFullHeaderName(name);
if (logger.isDebugEnabled())
logger.debug("Setting address header [" + name + "] to value ["
+ addr + "]");
if (isSystemHeaderAndNotGruu(getModifiableRule(hName), (Parameterable)addr.getURI())) {
logger.error("Error, can't set system header [" + hName + "]");
throw new IllegalArgumentException(
"Cant set system header, it is maintained by container!!");
}
if (hName.equalsIgnoreCase("From") || hName.equalsIgnoreCase("To")) {
logger.error("Error, can't set From or To header [" + hName + "]");
throw new IllegalArgumentException(
"Cant set From or To header, see JSR 289 Section 4.1.2");
}
// if (!isAddressTypeHeader(hName)) {
// logger.error("Error, set header, its not address type header ["
// + hName + "]!!");
// throw new IllegalArgumentException(
// "This header is not address type header");
// }
Header h;
String headerNameToAdd = getCorrectHeaderName(hName);
try {
h = SipFactoryImpl.headerFactory.createHeader(headerNameToAdd, addr
.toString());
this.message.setHeader(h);
} catch (ParseException e) {
logger.error("Parsing problem while setting address header with name "
+ name + " and address "+ addr, e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String name, Object o) {
if (name == null)
throw new NullPointerException("Attribute name can not be null.");
this.getAttributeMap().put(name, o);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setCharacterEncoding(java.lang.String)
*/
public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
new String("testEncoding".getBytes(),enc);
checkCommitted();
try {
this.message.setContentEncoding(SipFactoryImpl.headerFactory
.createContentEncodingHeader(enc));
} catch (Exception ex) {
throw new UnsupportedEncodingException(enc);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setContent(java.lang.Object, java.lang.String)
*/
public void setContent(Object content, String contentType)
throws UnsupportedEncodingException {
// https://code.google.com/p/sipservlets/issues/detail?id=202
if(getSipSession().getProxy() == null) {
checkMessageState();
}
checkContentType(contentType);
checkCommitted();
if(contentType != null && contentType.length() > 0) {
this.addHeader(ContentTypeHeader.NAME, contentType);
ContentTypeHeader contentTypeHeader = (ContentTypeHeader)this.message.getHeader(ContentTypeHeader.NAME);
String charset = this.getCharacterEncoding();
try {
// https://code.google.com/p/sipservlets/issues/detail?id=169
if(contentType.contains(CONTENT_TYPE_MULTIPART) && content instanceof Multipart) {
// Fix for Issue 2667 : Correct Handling of MimeMultipart
Multipart multipart = (Multipart) content;
OutputStream os = new ByteArrayOutputStream();
multipart.writeTo(os);
this.message.setContent(os.toString(), contentTypeHeader);
} else {
Object tmpContent = content;
if(tmpContent instanceof String && charset != null) {
//test for unsupportedencoding exception
new String("testEncoding".getBytes(charset));
tmpContent = new String(((String)tmpContent).getBytes());
}
this.message.setContent(content, contentTypeHeader);
}
} catch (UnsupportedEncodingException uee) {
throw uee;
} catch (Exception e) {
throw new IllegalArgumentException("Parse error reading content " + content + " with content type " + contentType, e);
}
}
}
protected abstract void checkMessageState();
/**
* Check the content type against the list defined by the iana
* http://www.iana.org/assignments/media-types/
* @param contentType
*/
private void checkContentType(String contentType) {
if(contentType == null) {
throw new IllegalArgumentException("the content type cannot be null");
}
int indexOfSlash = contentType.indexOf("/");
if(indexOfSlash != -1) {
if(!JainSipUtils.IANA_ALLOWED_CONTENT_TYPES.contains(contentType.substring(0, indexOfSlash))) {
throw new IllegalArgumentException("the given content type " + contentType + " is not allowed");
}
} else if(!JainSipUtils.IANA_ALLOWED_CONTENT_TYPES.contains(contentType.toLowerCase())) {
throw new IllegalArgumentException("the given content type " + contentType + " is not allowed");
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setContentLanguage(java.util.Locale)
*/
public void setContentLanguage(Locale locale) {
checkCommitted();
ContentLanguageHeader contentLanguageHeader =
SipFactoryImpl.headerFactory.createContentLanguageHeader(locale);
this.message.setContentLanguage(contentLanguageHeader);
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setContentLength(int)
*/
public void setContentLength(int len) {
checkMessageState();
checkCommitted();
try {
ContentLengthHeader h = SipFactoryImpl.headerFactory.createContentLengthHeader(len);
this.message.setHeader(h);
} catch (InvalidArgumentException e) {
throw new IllegalStateException("Impossible to set a content length lower than 0", e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setContentType(java.lang.String)
*/
public void setContentType(String type) {
checkContentType(type);
checkCommitted();
String name = getCorrectHeaderName(ContentTypeHeader.NAME);
try {
Header h = SipFactoryImpl.headerFactory.createHeader(name, type);
this.message
.removeHeader(getCorrectHeaderName(ContentTypeHeader.NAME));
this.message.addHeader(h);
} catch (ParseException e) {
logger.error("Error while setting content type header !!!", e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setExpires(int)
*/
public void setExpires(int seconds) {
try {
ExpiresHeader expiresHeader =
SipFactoryImpl.headerFactory.createExpiresHeader(seconds);
expiresHeader.setExpires(seconds);
this.message.setExpires(expiresHeader);
} catch (Exception e) {
throw new IllegalArgumentException("Error setting expiration header!", e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setHeader(java.lang.String, java.lang.String)
*/
public void setHeader(String name, String value) {
if(name == null) {
throw new NullPointerException ("name parameter is null");
}
if(value == null) {
throw new NullPointerException ("value parameter is null");
}
if (isSystemHeader(getModifiableRule(name))) {
throw new IllegalArgumentException(name + " is a system header !");
}
checkCommitted();
try {
// Dealing with Allow:INVITE, ACK, CANCEL, OPTIONS, BYE kind of headers
if(JainSipUtils.LIST_HEADER_NAMES.contains(name)) {
this.message.removeHeader(name);
List headers = SipFactory.getInstance().createHeaderFactory()
.createHeaders(name + ":" + value);
for (Header header : headers) {
this.message.addHeader(header);
}
} else {
// dealing with non list headers and extension header
Header header = SipFactory.getInstance().createHeaderFactory()
.createHeader(name, value);
this.message.setHeader(header);
}
} catch (Exception e) {
throw new IllegalArgumentException("Error creating header!", e);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setHeaderForm(javax.servlet.sip.SipServletMessage.HeaderForm)
*/
public void setHeaderForm(HeaderForm form) {
this.headerForm = form;
// When form changes to HeaderForm.COMPACT or HeaderForm.LONG - all
// names must transition, if it is changed to HeaderForm.DEFAULT, no
// action is performed
if(form == HeaderForm.DEFAULT)
return;
// if(form == HeaderForm.COMPACT) {
// for(String fullName : headerFull2CompactNamesMappings.keySet()) {
// if(message.getHeader(fullName) != null) {
// try {
//Handle the case where mutliple headers for the same header name
// Header header = SipFactoryImpl.headerFactory.createHeader(headerCompact2FullNamesMappings.get(fullName), ((SIPHeader)message.getHeader(fullName)).getHeaderValue());
// message.removeHeader(fullName);
// message.addHeader(header);
// } catch (ParseException e) {
// logger.error("Impossible to parse the header " + fullName + " to its compact form");
// }
// }
// }
// }
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#setParameterableHeader(java.lang.String, javax.servlet.sip.Parameterable)
*/
public void setParameterableHeader(String name, Parameterable param) {
checkCommitted();
if(isSystemHeader(getModifiableRule(name))) {
throw new IllegalArgumentException(name + " is a system header !");
}
try {
this.message.setHeader(SipFactoryImpl.headerFactory.createHeader(name, param.toString()));
} catch (ParseException e) {
throw new IllegalArgumentException("Impossible to set this parameterable header", e);
}
}
/**
* Applications must not add, delete, or modify so-called "system" headers.
* These are header fields that the servlet container manages: From, To,
* Call-ID, CSeq, Via, Route (except through pushRoute), Record-Route.
* Contact is a system header field in messages other than REGISTER requests
* and responses, 3xx and 485 responses, and 200/OPTIONS responses.
* Additionally, for containers implementing the reliable provisional
* responses extension, RAck and RSeq are considered system headers also.
*
* This method should return true if passed name - full or compact is name
* of system header in context of this message. Each subclass has to
* implement it in the manner that it conforms to semantics of wrapping
* class
*
* @param headerName -
* either long or compact header name
* @return
*/
public abstract ModifiableRule getModifiableRule(String headerName);
static final String SYS_HDR_MOD_OVERRIDE ="org.restcomm.servlets.sip.OVERRIDE_SYSTEM_HEADER_MODIFICATION";
/**
* Allows to override the System header modifiable rule assignment.
*
* @return if the InitParameter is present at servletContext, or null
* otherwise
*/
protected ModifiableRule retrieveModifiableOverriden() {
ModifiableRule overridenRule = null;
//use local var to prevent potential concurent cleanup
SipSession session = getSession();
if (session != null && session.getServletContext() != null) {
String overridenRuleStr = session.getServletContext().getInitParameter(SYS_HDR_MOD_OVERRIDE);
if (overridenRuleStr != null)
{
overridenRule = ModifiableRule.valueOf(overridenRuleStr);
}
}
return overridenRule;
}
/**
* Applications must not add, delete, or modify so-called "system" headers.
* These are header fields that the servlet container manages: From, To,
* Call-ID, CSeq, Via, Route (except through pushRoute), Record-Route.
* Contact is a system header field in messages other than REGISTER requests
* and responses, 3xx and 485 responses, and 200/OPTIONS responses.
* Additionally, for containers implementing the reliable provisional
* responses extension, RAck and RSeq are considered system headers also.
*
* This method should return true if passed name - full or compact is name
* of system header in context of this message. Each subclass has to
* implement it in the manner that it conforms to semantics of wrapping
* class
*
* @param headerName -
* either long or compact header name
* @return
*/
public static boolean isSystemHeader(ModifiableRule modifiableRule) {
if (modifiableRule == ModifiableRule.NotModifiable || modifiableRule == ModifiableRule.ContactSystem || modifiableRule == ModifiableRule.ProxyRecordRouteNotModifiable) {
return true;
} else {
return false;
}
}
/**
* Support for GRUU https://github.com/Mobicents/sip-servlets/issues/51
* if the header contains one of the gruu, gr, temp-gruu or pub-gruu, it is allowed to set the Contact Header
* as per RFC 5627
*/
public static boolean isSystemHeaderAndNotGruu(ModifiableRule modifiableRule, Parameterable parameterable) {
boolean isSettingGruu = false;
if(modifiableRule == ModifiableRule.ContactSystem &&
(parameterable.getParameter("gruu") != null ||
parameterable.getParameter("gr") != null)) {
isSettingGruu = true;
if (logger.isDebugEnabled())
logger.debug("Setting gruu so modifying contact header address is allowed");
}
return !isSettingGruu && isSystemHeader(modifiableRule);
}
public static boolean isSystemHeaderAndNotGruu(ModifiableRule modifiableRule, String value) {
boolean isSettingGruu = false;
if(modifiableRule == ModifiableRule.ContactSystem &&
(value.indexOf("gruu") != -1 ||
value.indexOf("gr=") != -1)) {
isSettingGruu = true;
if (logger.isDebugEnabled())
logger.debug("Setting gruu so modifying contact header address is allowed");
}
return !isSettingGruu && isSystemHeader(modifiableRule);
}
/**
* This method checks if passed name is name of address type header -
* according to rfc 3261
*
* @param headerName -
* name of header - either full or compact
* @return
*/
public static boolean isAddressTypeHeader(String headerName) {
return JainSipUtils.ADDRESS_HEADER_NAMES.contains(getFullHeaderName(headerName));
}
/**
* This method tries to resolve header name - meaning if it is compact - it
* returns full name, if its not, it returns passed value.
*
* @param headerName
* @return
*/
protected static String getFullHeaderName(String headerName) {
String fullName = null;
if (JainSipUtils.HEADER_COMPACT_2_FULL_NAMES_MAPPINGS.containsKey(headerName)) {
fullName = JainSipUtils.HEADER_COMPACT_2_FULL_NAMES_MAPPINGS.get(headerName);
} else {
fullName = headerName;
}
if (logger.isDebugEnabled())
logger.debug("Fetching full header name for [" + headerName
+ "] returning [" + fullName + "]");
return fullName;
}
/**
* This method tries to determine compact header name - if passed value is
* compact form it is returned, otherwise method tries to find compact name -
* if it is found, string rpresenting compact name is returned, otherwise
* null!!!
*
* @param headerName
* @return
*/
public static String getCompactName(String headerName) {
String compactName = null;
if (JainSipUtils.HEADER_COMPACT_2_FULL_NAMES_MAPPINGS.containsKey(headerName)) {
compactName = JainSipUtils.HEADER_COMPACT_2_FULL_NAMES_MAPPINGS.get(headerName);
} else {
// This can be null if there is no mapping!!!
compactName = JainSipUtils.HEADER_FULL_TO_COMPACT_NAMES_MAPPINGS.get(headerName);
}
if (logger.isDebugEnabled())
logger.debug("Fetching compact header name for [" + headerName
+ "] returning [" + compactName + "]");
return compactName;
}
public String getCorrectHeaderName(String name) {
return getCorrectHeaderName(name, this.headerForm);
}
protected static String getCorrectHeaderName(String name, HeaderForm form) {
if (form == HeaderForm.DEFAULT) {
return name;
} else if (form == HeaderForm.COMPACT) {
String compact = getCompactName(name);
if (compact != null)
return compact;
else
return name;
} else if (form == HeaderForm.LONG) {
return getFullHeaderName(name);
} else {
// ERROR ? - this shouldnt happen
throw new IllegalStateException(
"No default form of a header set!!!");
}
}
public Transaction getTransaction() {
if (logger.isDebugEnabled()) {
logger.debug("transaction " + transaction + " transactionId = " + transactionId + " transactionType " + transactionType);
}
if(transaction == null && transactionId != null) {
// used for early dialog failover purposes, lazily load the transaction
setTransaction(((ClusteredSipStack)StaticServiceHolder.sipStandardService.getSipStack()).findTransaction(transactionId, transactionType));
if(transaction != null) {
transactionApplicationData = (TransactionApplicationData) transaction.getApplicationData();
}
}
return this.transaction;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.message.toString();
}
public TransactionApplicationData getTransactionApplicationData() {
return this.transactionApplicationData;
}
public Message getMessage() {
return message;
}
public Dialog getDialog() {
if (this.dialog != null) {
return dialog;
}
if (this.getTransaction() != null) {
return this.getTransaction().getDialog();
}
return null;
}
/**
* @param transaction
* the transaction to set
*/
public void setTransaction(Transaction transaction) {
if (logger.isDebugEnabled())
logger.debug("Setting transaction " + transaction + " on message " + this);
this.transaction = transaction;
}
public void setTransport(String transport) {
this.transport = transport;
}
protected static int countChars(String string, char c) {
int count = 0;
for(int w=0; w")
// || !isParameterable(getFullHeaderName(hName))) {
Map paramMap = new HashMap();
String value = stringHeader;
String displayName = null;
// Issue 2201 : javax.servlet.sip.ServletParseException: Impossible to parse the following header Remote-Party-ID as an address.
// Need to handle the display name
if(stringHeader.trim().indexOf("\"") == 0) {
String displayNameString = stringHeader.substring(1);
int nextIndexOfDoubleQuote = displayNameString.indexOf("\"");
displayName = stringHeader.substring(0, nextIndexOfDoubleQuote + 2);
stringHeader = stringHeader.substring(nextIndexOfDoubleQuote + 2).trim();
}
boolean hasLaRaQuotes = false;
if(stringHeader.trim().indexOf("<") == 0) {
stringHeader = stringHeader.substring(1);
int indexOfBracket = stringHeader.indexOf(">");
if(indexOfBracket != -1) {
hasLaRaQuotes = true;
}
//String[] split = stringHeader.split(">");
value = stringHeader.substring(0, indexOfBracket);//split[0];
String restOfHeader = stringHeader.substring(indexOfBracket+1) ;
if (restOfHeader.length() > 1 && restOfHeader.contains(";")) {
// repleace first ";" with "" because it separates us from the URI that we romved
restOfHeader = restOfHeader.replaceFirst(";", "");
String[] split = restOfHeader.split(";");
// Now concatenate the items that have quotes that represent nested parameters http://code.google.com/p/sipservlets/issues/detail?id=105
// example ;expires=500;+sip.instance="";gruu="sip:[email protected];opaque=user:epid:xxxxxxxxxxxxxxxxxxxxxxxx;gruu"
ArrayList resplitListWithQuotes = new ArrayList(); // here we collect the items and append related entries that are part of the same quotation zone
int resplitIndex = 0;
boolean addToPrevious = false;
for(int q=0; qpair.length()) {
val = "";
} else {
val = pair.substring(indexOfEq+1);
}
}
// Fix to Issue 1010 (http://code.google.com/p/mobicents/issues/detail?id=1010) : Unable to set flag parameter to parameterable header
// from Alexander Kozlov from Codeminders
paramMap.put(key, val);
}
}
} else {
if (value.length() > 1 && value.contains(";")) {
// repleace first ";" with ""
String parameters = value.substring(value.indexOf(";") + 1);
value = value.substring(0, value.indexOf(";"));
String[] split = parameters.split(";");
for (String pair : split) {
String[] vals = pair.split("=");
if (vals.length > 2) {
logger
.error("Wrong parameter format, expected value and name, got ["
+ pair + "]");
throw new ServletParseException(
"Wrong parameter format, expected value or name["
+ pair + "]");
}
// Fix to Issue 1010 (http://code.google.com/p/mobicents/issues/detail?id=1010) : Unable to set flag parameter to parameterable header
// from Alexander Kozlov from Codeminders
paramMap.put(vals[0], vals.length == 2 ? vals[1] : "");
}
} else if (value.length() > 1 && value.contains(",")) {
// Deals with https://code.google.com/p/sipservlets/issues/detail?id=239
// repleace first ";" with ""
// String parameters = value.substring(value.indexOf(",") + 1);
// value = value.substring(0, value.indexOf(","));
String[] split = value.split(",");
for (String pair : split) {
String[] vals = pair.split("=");
if (vals.length > 2) {
logger
.error("Wrong parameter format, expected value and name, got ["
+ pair + "]");
throw new ServletParseException(
"Wrong parameter format, expected value or name["
+ pair + "]");
}
String paramValue = vals[1];
if(vals.length < 2) {
paramValue ="";
} else if(vals.length == 2 && vals[1].indexOf('\"') == 0 && vals[1].lastIndexOf('\"') == vals[1].length()-1) {
paramValue = vals[1].substring(1, vals[1].length()-1);
}
paramMap.put(vals[0], paramValue);
}
}
}
// quotes are parts of the value as well as the display Name
if(hasLaRaQuotes) {
value = "<" + value + ">";
}
// if a display name is present then we need add the quote back
if(displayName != null) {
value = displayName.concat(value);
}
final String headerName = header.getName();
final boolean isNotModifiable = JainSipUtils.SYSTEM_HEADERS.contains(headerName);
ModifiableRule modifiableRule = isNotModifiable ? ModifiableRule.NotModifiable : ModifiableRule.Modifiable;
if(headerName.equalsIgnoreCase(FromHeader.NAME)) {
if(isRequest) {
modifiableRule = ModifiableRule.From;
} else {
modifiableRule = ModifiableRule.NotModifiable;
}
}
if(headerName.equalsIgnoreCase(ToHeader.NAME)) {
if(isRequest) {
modifiableRule = ModifiableRule.To;
} else {
modifiableRule = ModifiableRule.NotModifiable;
}
}
if(headerName.equalsIgnoreCase(ViaHeader.NAME)) {
if(isRequest) {
modifiableRule = ModifiableRule.Via;
} else {
modifiableRule = ModifiableRule.NotModifiable;
}
}
if (logger.isDebugEnabled())
logger.debug("modifiableRule for [" + hName + "] from ["
+ whole + "] is " + modifiableRule);
ParameterableHeaderImpl parameterable = new ParameterableHeaderImpl(
header, value, paramMap, modifiableRule);
return parameterable;
}
public static boolean isParameterable(String header) {
if(JainSipUtils.PARAMETERABLE_HEADER_NAMES.contains(header)) {
return true;
}
return false;
}
/**
* @return the currentApplicationName
*/
public String getCurrentApplicationName() {
return currentApplicationName;
}
/**
* @param currentApplicationName the currentApplicationName to set
*/
public void setCurrentApplicationName(String currentApplicationName) {
this.currentApplicationName = currentApplicationName;
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getLocalAddr()
*/
public String getLocalAddr() {
final SIPTransaction sipTransaction = (SIPTransaction)getTransaction();
if(sipTransaction != null) {
return sipTransaction.getHost();
} else {
final String transport = JainSipUtils.findTransport(message);
final MobicentsExtendedListeningPoint listeningPoint = sipFactoryImpl.getSipNetworkInterfaceManager().findMatchingListeningPoint(transport, false);
return listeningPoint.getHost(true);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.sip.SipServletMessage#getLocalPort()
*/
public int getLocalPort() {
final SIPTransaction sipTransaction = (SIPTransaction)getTransaction();
if(sipTransaction != null) {
return sipTransaction.getPort();
} else {
final String transport = JainSipUtils.findTransport(message);
final MobicentsExtendedListeningPoint listeningPoint = sipFactoryImpl.getSipNetworkInterfaceManager().findMatchingListeningPoint(transport, false);
return listeningPoint.getPort();
}
}
// Fix for Issue 1552 http://code.google.com/p/mobicents/issues/detail?id=1552
// Container does not recognise 100rel if there are other extensions on the Require or Supported line
// we check all the values of Require and Supported headers to make sure the 100rel is present
protected boolean containsRel100(Message message) {
ListIterator requireHeaders = message.getHeaders(RequireHeader.NAME);
if(requireHeaders != null) {
while (requireHeaders.hasNext()) {
if(REL100_OPTION_TAG.equals(requireHeaders.next().getValue())) {
return true;
}
}
}
ListIterator supportedHeaders = message.getHeaders(SupportedHeader.NAME);
if(supportedHeaders != null) {
while (supportedHeaders.hasNext()) {
if(REL100_OPTION_TAG.equals(supportedHeaders.next().getValue())) {
return true;
}
}
}
return false;
}
public abstract void cleanUp();
protected Map getAttributeMap() {
if(this.attributes == null) {
this.attributes = new ConcurrentHashMap();
}
return this.attributes;
}
// Issue 2354
protected void setAttributeMap(Map atttributes) {
this.attributes = atttributes;
}
/*
* (non-Javadoc)
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
sipFactoryImpl = (SipFactoryImpl) in.readObject();
String sessionKeyString = in.readUTF();
if (sessionKeyString.length() > 0) {
try {
sessionKey = SessionManagerUtil.parseSipSessionKey(sessionKeyString);
} catch (ParseException e) {
throw new IllegalArgumentException("SIP Sesion Key " + sessionKeyString + " previously serialized could not be reparsed", e);
}
}
int attributesSize = in.readInt();
if(attributesSize > 0) {
Object[][] attributesArray = (Object[][] )in.readObject();
attributes = new ConcurrentHashMap();
for (int i = 0; i < attributesSize; i++) {
String key = (String) attributesArray[0][i];
Object value = attributesArray[1][i];
attributes.put(key, value);
}
}
if(in.readBoolean()) {
transactionApplicationData = (TransactionApplicationData) in.readObject();
}
headerForm = HeaderForm.valueOf(in.readUTF());
currentApplicationName = in.readUTF();
if(currentApplicationName.equals("")) {
currentApplicationName = null;
}
isMessageSent = in.readBoolean();
if(ReplicationStrategy.EarlyDialog == StaticServiceHolder.sipStandardService.getReplicationStrategy()) {
transactionId = in.readUTF();
if (logger.isDebugEnabled()) {
logger.debug("readExternal transactionId = " + transactionId);
}
if(transactionId != null) {
if(transactionId.equals("")) {
transactionId = null;
} else {
transactionType = in.readBoolean();
if (logger.isDebugEnabled()) {
logger.debug("readExternal transactionType = " + transactionType);
}
}
}
}
}
/*
* (non-Javadoc)
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(sipFactoryImpl);
if(sessionKey != null) {
out.writeUTF(sessionKey.toString());
} else {
if (null == sipSession) {
out.writeUTF("");
} else {
out.writeUTF(sipSession.getId());
}
}
if(attributes != null && attributes.size() > 0) {
out.writeInt(attributes.size());
Object[][] attributesArray = new Object[2][attributes.size()];
int i = 0;
for (Entry entry : attributes.entrySet()) {
attributesArray [0][i] = entry.getKey();
attributesArray [1][i] = entry.getValue();
i++;
}
out.writeObject(attributesArray);
} else {
out.writeInt(0);
}
if(transactionApplicationData != null) {
out.writeBoolean(true);
out.writeObject(transactionApplicationData);
} else {
out.writeBoolean(false);
}
out.writeUTF(headerForm.toString());
if(currentApplicationName != null) {
out.writeUTF(currentApplicationName);
} else {
out.writeUTF("");
}
out.writeBoolean(isMessageSent);
if(ReplicationStrategy.EarlyDialog == StaticServiceHolder.sipStandardService.getReplicationStrategy()) {
if (logger.isDebugEnabled()) {
logger.debug("writeExternal transaction = " + transaction);
}
if(transaction == null) {
out.writeUTF("");
} else {
if (logger.isDebugEnabled()) {
logger.debug("writeExternal transactionId = " + transaction.getBranchId() + " transactionType " + (transaction instanceof ServerTransaction));
}
out.writeUTF(transaction.getBranchId());
out.writeBoolean(transaction instanceof ServerTransaction);
}
}
out.writeUTF(message.toString());
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((message == null) ? 0 : message.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SipServletMessageImpl other = (SipServletMessageImpl) obj;
if (message == null) {
if (other.message != null)
return false;
} else if (!message.equals(other.message))
return false;
return true;
}
// public void cleanUp() {
// if(logger.isDebugEnabled()) {
// logger.debug("cleaning up the message " + message);
// }
// if(attributes != null) {
// attributes.clear();
// attributes = null;
// }
// currentApplicationName = null;
// dialog = null;
// headerForm= null;
// message= null;
//
// remoteAddr = null;
//// sessionKey = null;
//// sipFactoryImpl = null;
// sipSession = null;
// method = null;
//
//// if(transactionApplicationData != null) {
//// transactionApplicationData.cleanUp(false);
// transactionApplicationData = null;
//// }
// transaction = null;
// transport= null;
// userPrincipal= null;
// }
public void setOrphan(boolean orphan) {
this.orphan = orphan;
}
public boolean isOrphan() {
return orphan;
}
public String getAppSessionId() {
return appSessionId;
}
public void setAppSessionId(String appSessionId) {
this.appSessionId = appSessionId;
}
public boolean isMessageSent() {
return isMessageSent;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy