/* * Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights * Reserved. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ /* */ package gov.nist.siplite.header; import gov.nist.core.*; import java.util.Calendar; import java.util.Vector; /** * Generic SipHeader class * All the Headers inherit of this class * * @version JAIN-SIP-1.1 * * * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a> * */ public abstract class Header extends GenericObject { /** * Constant ERROR_INFO field. */ public static final String ERROR_INFO = "Error-Info"; /** * Constant MIME_VERSION field. */ public static final String MIME_VERSION = "Mime-Version"; /** * Constant IN_REPLY_TO field. */ public static final String IN_REPLY_TO = "In-Reply-To"; /** * Constant ALLOW field. */ public static final String ALLOW = "Allow"; /** * Constant ALLOW_EVENTS field. */ public static final String ALLOW_EVENTS = "Allow-Events"; /** * Constant CONTENT_LANGUAGE field. */ public static final String CONTENT_LANGUAGE = "Content-Language"; /** * Constant CALL_INFO field. */ public static final String CALL_INFO = "Call-Info"; /** * Constant CSEQ field. */ public static final String CSEQ = "CSeq"; /** * Constant ALERT_INFO field. */ public static final String ALERT_INFO = "Alert-Info"; /** * Constant ACCEPT_ENCODING field. */ public static final String ACCEPT_ENCODING = "Accept-Encoding"; /** * Constant ACCEPT field. */ public static final String ACCEPT = "Accept"; /** * Constant ENCRYPTION field. */ public static final String ENCRYPTION = "Encryption"; /** * Constant ACCEPT_LANGUAGE field. */ public static final String ACCEPT_LANGUAGE = "Accept-Language"; /** * Constant ACCEPT_CONTACT field. */ public static final String ACCEPT_CONTACT = "Accept-Contact"; /** * Constant RECORD_ROUTE field. */ public static final String RECORD_ROUTE = "Record-Route"; /** * Constant TIMESTAMP field. */ public static final String TIMESTAMP = "Timestamp"; /** * Constant TO field. */ public static final String TO = "To"; /** * Constant VIA field. */ public static final String VIA = "Via"; /** * Constant FROM field. */ public static final String FROM = "From"; /** * Constant CALL_ID field. */ public static final String CALL_ID = "Call-ID"; /** * Constant AUTHENTICATION_INFO field. */ public static final String AUTHENTICATION_INFO = "Authentication-Info"; /** * Constant AUTHORIZATION field. */ public static final String AUTHORIZATION = "Authorization"; /** * Constant PROXY_AUTHENTICATE field. */ public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; /** * Constant SERVER field. */ public static final String SERVER = "Server"; /** * Constant UNSUPPORTED field. */ public static final String UNSUPPORTED = "Unsupported"; /** * Constant RETRY_AFTER field. */ public static final String RETRY_AFTER = "Retry-After"; /** * Constant CONTENT_TYP field. */ public static final String CONTENT_TYPE = "Content-Type"; /** * Constant CONTENT_ENCODING field. */ public static final String CONTENT_ENCODING = "Content-Encoding"; /** * Constant CONTENT_LENGTH field. */ public static final String CONTENT_LENGTH = "Content-Length"; /** * Constant HIDE field. */ public static final String HIDE = "Hide"; /** * Constant ROUTE field. */ public static final String ROUTE = "Route"; /** * Constant CONTACT field. */ public static final String CONTACT = "Contact"; /** * Constant WWW_AUTHENTICATE field. */ public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; /** * Constant MAX_FORWARDS field. */ public static final String MAX_FORWARDS = "Max-Forwards"; /** * Constant ORGANIZATION field. */ public static final String ORGANIZATION = "Organization"; /** * Constant PROXY_AUTHORIZATION field. */ public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; /** * Constant PROXY_REQUIRE field. */ public static final String PROXY_REQUIRE = "Proxy-Require"; /** * Constant REQUIRE field. */ public static final String REQUIRE = "Require"; /** * Constant CONTENT_DISPOSITION field. */ public static final String CONTENT_DISPOSITION = "Content-Disposition"; /** * Constant SUBJECT field. */ public static final String SUBJECT = "Subject"; /** * Constant USER_AGENT field. */ public static final String USER_AGENT = "User-Agent"; /** * Constant WARNING field. */ public static final String WARNING = "Warning"; /** * Constant PRIORITY field. */ public static final String PRIORITY = "Priority"; /** * Constant DATE field. */ public static final String DATE = "Date"; /** * Constant MIN_EXPIRES field. */ public static final String MIN_EXPIRES = "Min-Expires"; /** * Constant EXPIRES field. */ public static final String EXPIRES = "Expires"; /** * Constant RESPONSE_KEY field. */ public static final String RESPONSE_KEY = "Response-Key"; /** * Constant WARN_AGENT field. */ public static final String WARN_AGENT = "Warn-Agent"; /** * Constant SECURITY_SERVER field. */ public static final String SECURITY_SERVER = "Security-Server"; /** * Constant SECURITY_VERIFY field. */ public static final String SECURITY_VERIFY = "Security-Verify"; /** * Constant SERVICE_ROUTE field. */ public static final String SERVICE_ROUTE = "Service-Route"; /** * Constant SUPPORTED field. */ public static final String SUPPORTED = "Supported"; /** Constant EVENT field. */ public static final String EVENT = "Event"; /** Constant EXTENSION field. */ public static final String EXTENSION = "Extension"; /** Constant SUBSCRIPTION_STATE field. */ public static final String SUBSCRIPTION_STATE = "Subscription-State"; /** Constant SIP_ETAG field (RFC 3903, p. 21). */ public static final String SIP_ETAG = "SIP-ETag"; /** Constant SIP_IF_MATCH field (RFC 3903, p. 22). */ public static final String SIP_IF_MATCH = "SIP-If-Match"; /** Constant Refer-To field (RFC 3515, p. 3). */ public static final String REFER_TO = "Refer-To"; /** Constant RSEQ field (RFC 3262, p. 11). */ public static final String RSEQ = "RSeq"; /** Constant RAck field (RFC 3262, p. 11). */ public static final String RACK = "RAck"; /* Inaccessible headers */ public static Vector inaccessibleHeaders = new Vector(); static { String[] headers = { AUTHENTICATION_INFO, AUTHORIZATION, MAX_FORWARDS, MIN_EXPIRES, PROXY_AUTHORIZATION, RECORD_ROUTE, SECURITY_SERVER, SECURITY_VERIFY, SERVICE_ROUTE, VIA, CALL_ID, CSEQ, PROXY_AUTHENTICATE, WWW_AUTHENTICATE }; for (int i = 0; i < headers.length; i++) { inaccessibleHeaders.addElement(headers[i].toLowerCase()); } } /** * Name of the header. */ public String headerName; /** * Value of the header. */ public String headerValue; /** * Array of the headers that can not have any parameters. * * NOTE: If the size of this array is changed, * getStringHash() method also must be changed! */ public static final String[] parameterLessHeaders = { AUTHENTICATION_INFO, // hash = 0 null, ALLOW, // 2 null, null, null, null, null, IN_REPLY_TO, // 8 PRIORITY, // 9 null, null, MIME_VERSION, // 12 SERVER, // 13 TIMESTAMP, // 14 USER_AGENT, // 15 null, MIN_EXPIRES, // 17 SUBJECT, // 18 null, null, // We can't create an instance of ParameterLessHeader class for // Content-Length header, because there is a check like // "header instanceof ContentLengthHeader" in Message.encodeAsBytes(). null, // CONTENT_LENGTH, // 21 CONTENT_LANGUAGE, // 22 null, WARNING, // 24 CONTENT_ENCODING, // 25 ORGANIZATION, // 26 UNSUPPORTED, // 27 null, null, null, REQUIRE, // 31 SUPPORTED, // 32 null, null, null, PROXY_REQUIRE, // 36 null, null }; /** * Default constructor. */ public Header() { } /** * Constructor given the name. * @param headerName the initial header field name */ public Header(String headerName) { this.headerName = headerName; } /** * Constructor given the name and value. * @param headerName is the header name. * @param headerValue is the header value. */ public Header(String headerName, String headerValue) { this.headerName = headerName; this.headerValue = headerValue; } /** * Sets the header name field. * @param name is the header name to set. */ public void setHeaderName(String name) { this.headerName = name; } /** * Sets the header value field. * @param value is the value field to set. */ public void setHeaderValue(String value) { this.headerValue = value; } /** * Gets the header name. * @return headerName field */ public String getHeaderName() { return this.headerName; } /** * Alias for getHeaderName. * @return headerName field */ public String getName() { return this.headerName; } /** * Gets the header value. * @return headerValue field */ public String getHeaderValue() { return this.encodeBody(); } /** * Encodes the header into a String. * @return String */ public String encode() { if (headerName == null) { return ""; } else { return headerName + Separators.COLON + Separators.SP + encodeBody() + Separators.NEWLINE; } } /** * A place holder -- this should be overriden with an actual * clone method. * @return need revisit copy of the current object */ public Object clone() { return this; } /** * A utility for encoding dates. * @param date the object to encode * @return the encode date string */ public static String encodeCalendar(Calendar date) { StringBuffer sbuf = new StringBuffer(); int wkday = date.get(Calendar.DAY_OF_WEEK); switch (wkday) { case Calendar.MONDAY: sbuf.append("Mon"); break; case Calendar.TUESDAY: sbuf.append("Tue"); break; case Calendar.WEDNESDAY: sbuf.append("Wed"); break; case Calendar.THURSDAY: sbuf.append("Thu"); break; case Calendar.FRIDAY: sbuf.append("Fri"); break; case Calendar.SATURDAY: sbuf.append("Sat"); break; case Calendar.SUNDAY: sbuf.append("Sun"); break; default: new Exception ("bad day of week?? Huh?? " + wkday).printStackTrace(); return null; } int day = date.get(Calendar.DAY_OF_MONTH); if (day < 10) sbuf.append(", 0" + day); else sbuf.append(", " + day); sbuf.append(" "); int month = date.get(Calendar.MONTH); switch (month) { case Calendar.JANUARY: sbuf.append("Jan"); break; case Calendar.FEBRUARY: sbuf.append("Feb"); break; case Calendar.MARCH: sbuf.append("Mar"); break; case Calendar.APRIL: sbuf.append("Apr"); break; case Calendar.MAY: sbuf.append("May"); break; case Calendar.JUNE: sbuf.append("Jun"); break; case Calendar.JULY: sbuf.append("Jul"); break; case Calendar.AUGUST: sbuf.append("Aug"); break; case Calendar.SEPTEMBER: sbuf.append("Sep"); break; case Calendar.OCTOBER: sbuf.append("Oct"); break; case Calendar.NOVEMBER: sbuf.append("Nov"); break; case Calendar.DECEMBER: sbuf.append("Dec"); break; default: return null; } sbuf.append(" "); int year = date.get(Calendar.YEAR); sbuf.append(year); sbuf.append(" "); int hour = date.get(Calendar.HOUR_OF_DAY); if (hour < 10) sbuf.append("0"+hour); else sbuf.append(hour); sbuf.append(":"); int min = date.get(Calendar.MINUTE); if (min < 10) sbuf.append("0"+min); else sbuf.append(min); sbuf.append(":"); int sec = date.get(Calendar.SECOND); if (sec < 10) sbuf.append("0"+sec); else sbuf.append(sec); sbuf.append(" GMT"); return sbuf.toString(); } /** * Gets the parameters for the header as a nameValue list. * @return the name value list of header field paramaters */ public abstract NameValueList getParameters(); /** * Gets the value for the header as opaque object (returned value * will depend upon the header. Note that this is not the same as * the getHeaderValue above. * @return the header field value */ public abstract Object getValue(); /** * Gets the stuff that follows the headerName. * @return a string representation of the stuff that follows the * headerName */ protected abstract String encodeBody(); /** * Returns the encoded text contents. * @return encode string of object contents */ public String toString() { return this.encode(); } /** * Calculates a signle-byte hash code of the string. * @param s is a string for which a hash code must be calculated * @return hash code (from 0 to parameterLessHeaders.length) of the string */ public static byte getStringHash(String s) { String sl = s.toLowerCase(); int len = sl.length(), headersCount = parameterLessHeaders.length; int hash = len; for (int i = 0; i < len; i++) { hash += sl.charAt(i); } hash = (byte)(hash % headersCount); if (hash == 6) { hash = (hash + 9 + sl.charAt(0)) % headersCount; } else if (hash == 7) { hash = (hash + 13 + sl.charAt(0)) % headersCount; } // System.out.println("hash('" + s + "') = " + hash); return (byte)hash; } /** * Checks if the specified header can have some parameters. * @param name the name of the header * @return true if the header can not have any parameters, false otherwise */ public static boolean isParameterLess(String name) { byte hash; // IMPL_NOTE: remove!!! // for (int i = 0; i < parameterLessHeaders.length; i++) { // if (parameterLessHeaders[i] != null) // getStringHash(parameterLessHeaders[i]); // } hash = getStringHash(name); return (parameterLessHeaders[hash] != null && parameterLessHeaders[hash].equalsIgnoreCase(name)); } /** * Checks if a header with the given name is an Authorization * or Authentication header. * @param name the name of the header * @return true if the header is an Authorization or Authentication header */ public static boolean isAuthorization(String name) { if (name == null) { return false; } return (name.equalsIgnoreCase(Header.AUTHORIZATION) || name.equalsIgnoreCase(Header.PROXY_AUTHORIZATION) || name.equalsIgnoreCase(Header.PROXY_AUTHENTICATE) || name.equalsIgnoreCase(Header.WWW_AUTHENTICATE)); } /** * Search for the reliable tag, "100rel" in a "Require" Header value. * This method is internally used to identify if the response * received at UAC is reliable provisional response * @param strOptionTags List of option tags that is actually a value * of Require Header * @return true if "100rel" tags is found; else false */ public static boolean isReliableTagPresent(String strOptionTags) { if (Utils.equalsIgnoreCase(strOptionTags, "100rel")) { return true; } else { String tag = null; // int tagsLength = strOptionTags.length(); int delimIndex = strOptionTags.indexOf(Separators.COMMA); while (delimIndex > 0) { tag = strOptionTags.substring(0, delimIndex).trim(); strOptionTags = strOptionTags.substring(delimIndex + 1, strOptionTags.length()).trim(); if (Utils.equalsIgnoreCase(tag, "100rel") || Utils.equalsIgnoreCase(strOptionTags, "100rel")) { return true; } delimIndex = strOptionTags.indexOf(Separators.COMMA); } } return false; } }