/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2001 by Sun Microsystems, Inc.
* All rights reserved.
*
*/
// SLPHeaderV2.java: Base class for Service Location Messages
// Author: James Kempf
// Created On: Thu Oct 9 08:50:31 1997
// Last Modified By: James Kempf
// Last Modified On: Wed Jan 6 15:24:26 1999
// Update Count: 472
//
package com.sun.slp;
import java.util.*;
import java.net.*;
import java.io.*;
/**
* The SLPHeaderV2 class serves as the header class for all SLPv2 messages.
* It contains instance variables for SLP message header contents,
* and implements the SLPHeaderV2 interface.
*
* @author James Kempf
*/
class SLPHeaderV2 extends SrvLocHeader implements Cloneable {
// Support for options.
private int optOff = 0;
Hashtable optTable = new Hashtable();
// Maximum message size (24 bits).
private final static int MAX_MESSAGE_LENGTH = 0xffffff;
// Location of flag byte.
static final int FLAG_BYTE = 4;
// Various header flags.
protected static final int NOFLAG = 0x00;
static final int OVERFLOW = 0x80; // needed by Transact.
protected static final int FRESH = 0x40;
protected static final int MCAST = 0x20;
// Header sizes. Note that this doesn't include the language tag,
// which is variable.
protected static final int REST_HEADER_BYTES = 12;
protected static final int HEADER_BYTES =
VERSION_FUNCTION_BYTES + REST_HEADER_BYTES;
// Maximum protected scopes allowed.
protected static final int MAX_PROTECTED_SCOPES = 255;
// Registered option classes.
protected static Hashtable optClasses = new Hashtable();
// Manditory option range.
protected static int MANDATORY_OPTION_LOW = 0x4000;
protected static int MANDATORY_OPTION_HIGH = 0x7fff;
// Sizes of option id and extension fields (in bytes).
protected static int OPT_ID_SIZE = 2;
protected static int OPT_OFF_SIZE = 2;
// Interfaces for options to use.
interface OptionParser {
// Parse the option from the data stream. We include the header also,
// in case it is needed.
abstract SLPOption parse(SLPHeaderV2 hdr, DataInputStream dsr)
throws ServiceLocationException, IOException;
}
interface SLPOption {
// Externalize the option to the byte array stream. We include the
// header also, in case it is needed.
abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)
throws ServiceLocationException;
}
// Register an option parsing class.
static void registerOptionClass(int id, Class optClass) {
Integer key = new Integer(id);
// We should probably check if it implements SLPOption.OptionParser,
// but...
optClasses.put(key, optClass);
}
//
// Header instance variables.
//
// For the incoming message side.
SLPHeaderV2() {
super();
version = Defaults.version;
}
// Initialize the new SLPHeaderV2 from the input stream. Version and
// function code have already been removed from the stream.
void parseHeader(int functionCode, DataInputStream dis)
throws ServiceLocationException, IOException {
this.functionCode = functionCode;
nbytes += 2; // for version and function code...
// Get length.
length = getInt24(dis);
// Get flags.
byte[] b = new byte[2];
dis.readFully(b, 0, 2);
nbytes += 2;
byte flags = (byte) ((char)b[0] & 0xFF);
overflow = ((flags & OVERFLOW) != NOFLAG);
fresh = ((flags & FRESH) != NOFLAG);
mcast = ((flags & MCAST) != NOFLAG);
// We could check for null on reserved part of flags field, but
// in the spirit of "be liberal in what you receive" we don't.
// Get option offset.
optOff = getInt24(dis);
// Check option offset for sanity.
if (optOff > length) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"option_error",
new Object[] {
new Integer(optOff), new Integer(length)});
}
// Get transaction id.
xid = (short)getInt(dis);
// Get language code.
StringBuffer buf = new StringBuffer();
getString(buf, dis);
locale = SLPConfig.langTagToLocale(buf.toString());
// Everything went OK coming in, so set the error code.
errCode = ServiceLocationException.OK;
}
// By default, the header parses the client side message. A server
// side subclass must replace this. We do this so that the amount of code
// in the client is minimized, since this class must be in both.
SrvLocMsg parseMsg(DataInputStream dis)
throws ServiceLocationException,
IOException,
IllegalArgumentException {
SrvLocMsg rply = null;
// Get the error code, if not SAAdvert.
if (functionCode != SrvLocHeader.SAAdvert) {
errCode = (short)getInt(dis);
}
// Switch and convert according to function code.
switch (functionCode) {
case SrvLocHeader.SrvRply:
rply = new CSrvMsg(this, dis);
break;
case SrvLocHeader.AttrRply:
rply = new CAttrMsg(this, dis);
break;
case SrvLocHeader.SrvTypeRply:
rply = new CSrvTypeMsg(this, dis);
break;
case SrvLocHeader.DAAdvert:
rply = new CDAAdvert(this, dis);
break;
case SrvLocHeader.SrvAck:
// We act as a SrvAck.
rply = this;
iNumReplies = 1;
break;
case SrvLocHeader.SAAdvert:
rply = new CSAAdvert(this, dis);
break;
default:
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"function_code_error",
new Object[] {
new Integer(functionCode)});
}
// Check for size overflow.
if (nbytes > length) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"length_overflow",
new Object[] {
new Integer(nbytes), new Integer(length)});
}
return rply;
}
// Construct a header for output. Used by the client side code to
// construct an initial request and the server side code to construct
// a reply.
SLPHeaderV2(int functionCode, boolean fresh, Locale locale)
throws ServiceLocationException {
// Check for proper function code and nonnull locale.
Assert.slpassert(((functionCode <= SAAdvert) &&
(functionCode >= SrvReq)),
"function_code_error",
new Object[] {new Integer(functionCode)});
Assert.slpassert((locale != null),
"null_locale_error",
new Object[0]);
this.version = Defaults.version;
this.functionCode = functionCode;
this.locale = locale;
this.xid = getUniqueXID(); // client can change it later if they want.
this.fresh = fresh;
// If there's not enough for the error code (if any), then signal
// an error. The assumption here is that the message is going
// via UDP or multicast.
byte[] ltag =
getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
int headerLen = ltag.length + HEADER_BYTES;
int payLen = packetLength - headerLen;
if (payLen < SHORT_SIZE) {
throw
new ServiceLocationException(
ServiceLocationException.BUFFER_OVERFLOW,
"buffer_overflow",
new Object[] {
new Integer(headerLen + SHORT_SIZE),
new Integer(packetLength)});
}
}
// Externalize the message by converting it to bytes and writing
// it to the output stream.
public void
externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)
throws ServiceLocationException {
// Convert the locale to a tag. We need the length.
byte[] ltagBytes =
getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
int ltagLen = ltagBytes.length;
// Set the multicast flag.
this.mcast = mcast;
// We need to get stuff into another stream first, so we can correctly
// calculate the length.
ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
// Need to put in the error code. There will only be an error code
// if error codes are applicable for this message type. Note
// that room for the error code was reserved in the initial
// calculation of the header, so there should always be room
// for it, even if the packet overflowed otherwise.
if (functionCode == SrvLocHeader.SrvAck ||
functionCode == SrvLocHeader.SrvTypeRply ||
functionCode == SrvLocHeader.SrvRply ||
functionCode == SrvLocHeader.AttrRply ||
functionCode == SrvLocHeader.DAAdvert) {
putInt(errCode, bbaos);
}
// Put in the previous responders, if there are any. Note that
// there may be only when the error code is not put out.
// We check against the packet size during parsing so that
// we don't overflow the packet and throw a special exception
// if an overflow happens. We only put out the previous
// responders list if the request is going by multicast, but
// we need to put out an empty vector for unicast requests.
int prevResLen =
packetLength - (payload.length + HEADER_BYTES + ltagLen);
Vector resp = previousResponders;
if (resp != null) {
resp = (mcast ? resp:new Vector());
parsePreviousRespondersOut(resp, bbaos, prevResLen);
}
// If the error code is OK, then insert the rest of the message
// and parse the options. If there was an error,
// this step is skipped because the data isn't relevant.
if (errCode == ServiceLocationException.OK) {
bbaos.write(payload, 0, payload.length);
// Externalize any options.
optOff = externalizeOptions(bbaos, ltagLen);
}
byte[] payloadBytes = bbaos.toByteArray();
// Set the length here to the actual length of the packet.
length = HEADER_BYTES + ltagLen + payloadBytes.length;
// If we exceed the 24 bit length size, we are hosed.
if (length > MAX_MESSAGE_LENGTH) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"max_msg_size_exceeded",
new Object[0]);
}
// Truncate if necessary. We will always have room for a header
// and error code because we check when creating the object.
// Note that no URL block will be truncated because the spec
// says it can't be.
if (!isTCP && (length > packetLength)) {
overflow = true;
length = packetLength;
byte[] newBytes = new byte[packetLength];
System.arraycopy(payloadBytes, 0, newBytes, 0,
length - (HEADER_BYTES + ltagLen));
payloadBytes = newBytes;
}
//
// Write out the header.
//
// Write version and function code.
baos.write((byte) (0xFF & version));
baos.write((byte) (0xFF & functionCode));
// Put length in.
putInt24(length, baos);
// Put in flags.
byte flags = (byte)NOFLAG;
if (overflow) {
flags = (byte)(flags | OVERFLOW);
} else {
flags = (byte)(flags & ~OVERFLOW);
}
if (fresh) {
flags = (byte)((flags | FRESH) & 0xFF);
} else {
flags = (byte)((flags & ~FRESH) & 0xFF);
}
if (mcast) {
flags = (byte)((flags | MCAST) & 0xFF);
} else {
flags = (byte)((flags & ~MCAST) & 0xFF);
}
// Write out flags.
baos.write((byte) (0xFF & flags));
baos.write((byte)0);
putInt24(optOff, baos); // write option offset, if any.
putInt(xid, baos); // write xid.
putInt(ltagLen, baos); // write lang size.
baos.write(ltagBytes, 0, ltagBytes.length); // write lang tag.
//
// Write the body.
//
baos.write(payloadBytes, 0, payloadBytes.length);
}
//
// Option handling.
//
// Parse any options.
void parseOptions(DataInputStream dsr)
throws ServiceLocationException,
IOException,
IllegalArgumentException {
// If no options return.
if (optOff == 0) {
return;
}
int optNext = 0;
// Parse any options in the data stream.
do {
// Parse extension id.
int optId = getInt(dsr);
// Parse extension offset.
optNext = getInt(dsr);
// Lookup an option parser.
Integer key = new Integer(optId);
Class optClass = (Class)optClasses.get(key);
// May be an exception if class is null.
if (optClass == null) {
// In mandatory range. Throw an exception.
if ((optId >= MANDATORY_OPTION_LOW) &&
(optId <= MANDATORY_OPTION_HIGH)) {
throw
new ServiceLocationException(
ServiceLocationException.OPTION_NOT_SUPPORTED,
"v2_unsup_option",
new Object[] {key});
}
// Skip the rest of the option.
int skipStart = length;
if (optNext != 0) {
skipStart = optNext;
}
dsr.skipBytes(skipStart - nbytes);
} else {
try {
// Parse the option.
OptionParser optParser =
(OptionParser)optClass.newInstance();
SLPOption opt = optParser.parse(this, dsr);
// Insert option into option table.
optTable.put(key, opt);
} catch (InstantiationException ex) {
throw
new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"v2_option_inst",
new Object[] {key, ex});
} catch (IllegalAccessException ex) {
throw
new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"v2_option_sec",
new Object[] {key, ex});
}
}
} while (optNext != 0);
}
// Externalize any options.
private int externalizeOptions(ByteArrayOutputStream baos, int langTagLen)
throws ServiceLocationException {
// Calculate offset to options, if any.
int toOpt = 0;
if (optTable.size() <= 0) {
return toOpt;
}
toOpt = HEADER_BYTES + langTagLen + baos.size();
// For all options in the table, parse them out.
Enumeration en = optTable.keys();
int nextOpt = toOpt;
while (en.hasMoreElements()) {
Integer id = (Integer)en.nextElement();
SLPOption opt = (SLPOption)optTable.get(id);
ByteArrayOutputStream obaos = new ByteArrayOutputStream();
// Linearize option object.
opt.externalize(this, obaos);
// Calculate offset to next options.
nextOpt += obaos.size() + OPT_ID_SIZE + OPT_OFF_SIZE;
// Plop it into the output stream.
putInt(id.intValue(), baos);
// Check whether there are more options first. If so, then
// the next offset is zero.
if (en.hasMoreElements()) {
putInt(nextOpt, baos);
} else {
putInt(0, baos);
}
byte[] bytes = obaos.toByteArray();
baos.write(bytes, 0, bytes.length);
}
return toOpt;
}
// Parse the previous responder list out, being sure to truncate
// so the list is syntactically correct if there is an overflow.
// This duplicates the comma separated list code to a certain
// extent.
private void
parsePreviousRespondersOut(Vector resp,
ByteArrayOutputStream baos,
int available)
throws ServiceLocationException {
ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
int i, n = resp.size();
for (i = 0; i < n; i++) {
String address = (String)resp.elementAt(i);
// Add comma if necessary.
if (i > 0) {
address = "," + address;
}
// Convert to UTF8 bytes.
byte[] bytes = getStringBytes(address, Defaults.UTF8);
// Write bytes to stream if there's room.
if (bytes.length <= available) {
bbaos.write(bytes, 0, bytes.length);
available = available - bytes.length;
} else {
// Throw exception, upper layers need to break off multicast.
// This exception should *never* be surfaced.
throw
new ServiceLocationException(
ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW,
"v2_prev_resp_overflow",
new Object[] {});
}
}
// Now write to the real stream.
byte[] out = bbaos.toByteArray();
putInt(out.length, baos);
baos.write(out, 0, out.length);
nbytes += out.length;
}
//
// Utilities for parsing service URL's and attribute lists, with
// authentication information.
//
// Parse in a service URL including lifetime if necessary.
ServiceURL
parseServiceURLIn(DataInputStream dis,
Hashtable authTable,
short err)
throws ServiceLocationException, IOException {
// Ignore reserved byte.
byte[] b = new byte[1];
dis.readFully(b, 0, 1);
nbytes += 1;
// Get URL lifetime.
int lifetime = getInt(dis);
// Get URL.
StringBuffer buf = new StringBuffer();
byte[] rawBytes = getString(buf, dis);
// Get auth block, if any.
Hashtable auth = null;
// Get number of auth blocks.
b = new byte[1];
dis.readFully(b, 0, 1);
nbytes += 1;
byte nauths = (byte)(b[0] & 0xFF);
if (nauths > 0) {
ByteArrayOutputStream abaos = new ByteArrayOutputStream();
putInteger(rawBytes.length, abaos);
Object[] message = new Object[2];
message[0] = abaos.toByteArray();
message[1] = rawBytes;
auth = getCheckedAuthBlockList(message, nauths, dis);
lifetime = AuthBlock.getShortestLifetime(auth);
}
String ssurl = buf.toString();
ServiceURL url = null;
try {
url = new ServiceURL(ssurl, lifetime);
} catch (IllegalArgumentException ex) {
throw
new ServiceLocationException(err,
"malformed_url",
new Object[] {ex.getMessage()});
}
if (auth != null) {
// Put it in the auth block for this URL.
authTable.put(url, auth);
}
return url;
}
// Parse out a service URL, create authentication blocks if necessary.
// Return true if the URL was output. Check that we don't overflow
// packet size in the middle.
boolean
parseServiceURLOut(ServiceURL surl,
boolean urlAuth,
Hashtable auth,
ByteArrayOutputStream baos,
boolean checkOverflow)
throws ServiceLocationException {
// We need to keep track of size, so we don't overflow packet length.
ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
int mbytes = nbytes;
// Convert the URL to bytes.
byte[] bytes = getStringBytes(surl.toString(), Defaults.UTF8);
// Parse out reserved.
bbaos.write((byte)(0xFF & 0));
nbytes += 1;
// Parse out the lifetime.
putInt(surl.getLifetime(), bbaos);
byte bs = (byte)0;
// Process auth block list if required.
if (urlAuth) {
// Create an auth block if necessary.
if (auth == null) {
ByteArrayOutputStream abaos = new ByteArrayOutputStream();
putInteger(bytes.length, abaos);
Object[] message = new Object[2];
message[0] = abaos.toByteArray();
message[1] = bytes;
auth = getCheckedAuthBlockList(message, surl.getLifetime());
}
bs = (byte) auth.size();
Object[] bytesArray = AuthBlock.getContents(auth);
bytes = (byte[]) bytesArray[1];
}
// Put out the URL bytes.
putInt(bytes.length, bbaos);
bbaos.write(bytes, 0, bytes.length);
nbytes += bytes.length;
// Write auth block size.
bbaos.write((byte)(0xFF & bs));
nbytes += 1;
// If there are auth blocks required, put them out now.
if (bs > (byte)0) {
AuthBlock.externalizeAll(this, auth, bbaos);
}
// If we can, write it out.
bytes = bbaos.toByteArray();
if (!checkOverflow || nbytes <= packetLength) {
baos.write(bytes, 0, bytes.length);
return true; // nbytes already set...
} else {
nbytes = mbytes; // truncate...
return false;
}
}
// Parse in a potentially authenticated attribute list.
Hashtable
parseAuthenticatedAttributeVectorIn(Vector attrs,
DataInputStream dis,
boolean allowMultiValuedBooleans)
throws ServiceLocationException, IOException {
// First, parse in the attribute vector.
byte[] rawBytes =
parseAttributeVectorIn(attrs, dis, allowMultiValuedBooleans);
ByteArrayOutputStream abaos = new ByteArrayOutputStream();
putInteger(rawBytes.length, abaos);
Object[] message = new Object[2];
message[0] = abaos.toByteArray();
message[1] = rawBytes;
// Get the attribute list signature, if necessary.
return parseSignatureIn(message, dis);
}
// Parse in a list of attributes into attrs, returing raw bytes.
// ServiceLocationAttribute objects. Clients take care of auth blocks.
byte[]
parseAttributeVectorIn(Vector attrs,
DataInputStream dis,
boolean allowMultiValuedBooleans)
throws ServiceLocationException, IOException {
StringBuffer buf = new StringBuffer();
byte[] rawBytes = getString(buf, dis);
// Parse the list into ServiceLocationAttribute objects.
Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false);
int i, n = attrForms.size();
for (i = 0; i < n; i++) {
String attrForm =
(String)attrForms.elementAt(i);
attrs.addElement(
new ServiceLocationAttribute(
attrForm, allowMultiValuedBooleans));
}
return rawBytes;
}
// Parse out a vector of ServiceLocationAttributes. Includes escaping
// characters.
byte[]
parseAttributeVectorOut(Vector v,
int lifetime,
boolean attrAuth,
Hashtable auth,
ByteArrayOutputStream baos,
boolean writeAuthCount)
throws ServiceLocationException {
byte[] bytes = null;
int nBlocks = 0;
// Convert attribute vector to comma separated list.
if (!attrAuth || auth == null) {
Vector strings = new Vector();
Enumeration en = v.elements();
// Convert the attributes to strings, escaping characters to
// escape.
while (en.hasMoreElements()) {
ServiceLocationAttribute attr =
(ServiceLocationAttribute)en.nextElement();
strings.addElement(attr.externalize());
}
// Create the comma separated list.
String clist = vectorToCommaSeparatedList(strings);
bytes = getStringBytes(clist, Defaults.UTF8);
if (attrAuth) {
ByteArrayOutputStream abaos = new ByteArrayOutputStream();
putInteger(bytes.length, abaos);
Object[] message = new Object[2];
message[0] = abaos.toByteArray();
message[1] = bytes;
auth = getCheckedAuthBlockList(message, lifetime);
}
} else {
Object[] bytesArray = AuthBlock.getContents(auth);
bytes = (byte[]) bytesArray[1];
}
// Get number of blocks if authentication.
if (auth != null) {
nBlocks = auth.size();
}
// Write out the bytes.
putInt(bytes.length, baos);
baos.write(bytes, 0, bytes.length);
nbytes += bytes.length;
// Write out number of auth blocks.
if (writeAuthCount) {
baos.write((byte)(nBlocks & 0xFF));
nbytes += 1;
}
// Write out the attribute authentication blocks.
if (attrAuth && nBlocks > 0) {
AuthBlock.externalizeAll(this, auth, baos);
}
return bytes;
}
// Get an auth block list, checking first for security.
Hashtable getCheckedAuthBlockList(Object[] message, int lifetime)
throws ServiceLocationException {
if (!SLPConfig.getSLPConfig().getHasSecurity()) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_ABSENT,
"auth_classes_missing",
new Object[0]);
}
return AuthBlock.makeAuthBlocks(message, lifetime);
}
// Get an SLPAuthBlockList, checking first if security is enabled.
Hashtable getCheckedAuthBlockList(Object[] message,
byte nauth,
DataInputStream dis)
throws ServiceLocationException, IOException {
if (!SLPConfig.getSLPConfig().getHasSecurity()) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_ABSENT,
"auth_classes_missing",
new Object[0]);
}
return AuthBlock.makeAuthBlocks(this, message, dis, nauth);
}
// Parse in an attribute signature.
Hashtable parseSignatureIn(Object[] message, DataInputStream dis)
throws ServiceLocationException, IOException {
Hashtable auth = null;
byte[] b = new byte[1];
dis.readFully(b, 0, 1);
nbytes += 1;
byte nauths = (byte)(b[0] & 0xFF);
if (nauths > 0) {
auth = getCheckedAuthBlockList(message, nauths, dis);
}
return auth;
}
//
// Utility functions to help with verification of data.
//
// Escape tags, check for strings. Trim trailing, leading whitespace,
// since it is ignored for matching purposes and tags are only
// used for matching.
void escapeTags(Vector t)
throws ServiceLocationException {
int i, n = t.size();
for (i = 0; i < n; i++) {
Object o = t.elementAt(i);
if (o instanceof String) {
// Escape tag.
String tag =
ServiceLocationAttribute.escapeAttributeString((String)o,
false);
t.setElementAt(tag.trim(), i);
} else {
throw
new IllegalArgumentException(
SLPConfig.getSLPConfig().formatMessage("nonstring_tag",
new Object[0]));
}
}
}
// Unescape tags. Trim trailing and leading whitespace since it is
// ignored for matching purposes and tags are only used for matching.
void unescapeTags(Vector t)
throws ServiceLocationException {
int i, n = t.size();
for (i = 0; i < n; i++) {
String tag = (String)t.elementAt(i);
tag =
ServiceLocationAttribute.unescapeAttributeString(tag, false);
t.setElementAt(tag.trim(), i);
}
}
// Escape vector of scope strings.
static void escapeScopeStrings(Vector scopes)
throws ServiceLocationException {
int i, n = scopes.size();
Vector ret = new Vector();
for (i = 0; i < n; i++) {
String scope = (String)scopes.elementAt(i);
scopes.setElementAt(
ServiceLocationAttribute.escapeAttributeString(scope, false),
i);
}
}
// Unescape vector of scope strings.
static void unescapeScopeStrings(Vector scopes)
throws ServiceLocationException {
int i, n = scopes.size();
Vector ret = new Vector();
for (i = 0; i < n; i++) {
String scope = (String)scopes.elementAt(i);
scopes.setElementAt(
ServiceLocationAttribute.unescapeAttributeString(scope, false),
i);
}
}
// Error if somebody tries to do this client side.
SDAAdvert
getDAAdvert(short xid,
long timestamp,
ServiceURL url,
Vector scopes,
Vector attrs)
throws ServiceLocationException {
Assert.slpassert(false,
"v2_daadvert_client_side",
new Object[0]);
return null; // never get here...
}
// Reimplement clone() to get the header size right.
public Object clone()
throws CloneNotSupportedException {
SLPHeaderV2 hdr = (SLPHeaderV2)super.clone();
byte[] langBytes = getStringBytes(locale.toString(),
Defaults.UTF8);
hdr.nbytes = HEADER_BYTES + langBytes.length + 2; // for error code...
return hdr;
}
}