All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.pkts.packet.sip.address.Address Maven / Gradle / Ivy

/**
 * 
 */
package io.pkts.packet.sip.address;

import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.buffer.ByteNotFoundException;
import io.pkts.packet.sip.SipParseException;
import io.pkts.packet.sip.address.impl.AddressImpl;
import io.pkts.packet.sip.impl.SipParser;

import java.io.IOException;

import static io.pkts.packet.sip.impl.PreConditions.assertArgument;
import static io.pkts.packet.sip.impl.PreConditions.assertNotNull;
import static io.pkts.packet.sip.impl.PreConditions.ifNull;

/**
 * Represents an address and like everything else, it is an immutable class.
 *
 * @author [email protected]
 */
public interface Address {

    /**
     * Get the display name of this {@link Address} or an empty buffer if it is
     * not set.
     * 
     * @return
     */
    Buffer getDisplayName();

    /**
     * Get the {@link URI} of this {@link Address}.
     * 
     * @return the {@link URI}
     * @throws SipParseException
     */
    URI getURI() throws SipParseException;

    /**
     * Get the {@link Address} as a raw buffer.
     * 
     * @return
     */
    Buffer toBuffer();

    void getBytes(Buffer dst);

    /**
     * An {@link Address} is an immutable object so if you wish to change something
     * you have to create a copy of it.
     *
     * @return
     */
    Builder copy();

    static Address frame(final String buffer) throws SipParseException, IndexOutOfBoundsException, IOException {
        return frame(Buffers.wrap(buffer));
    }

    /**
     *
     * Parses a SIP "name-addr" as defined by RFC3261 section 25.1:
     *
     * 
     * name-addr      =  [ display-name ] LAQUOT addr-spec RAQUOT
     * addr-spec      =  SIP-URI / SIPS-URI / absoluteURI
     * display-name   =  *(token LWS)/ quoted-string
     * 
* * @param buffer * @return * @throws SipParseException * @throws IOException */ static Address frame(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException, IOException { SipParser.consumeWS(buffer); final Buffer original = buffer.slice(); boolean doubleQuote = false; if (buffer.peekByte() == SipParser.DQUOT) { doubleQuote = true; } final Buffer displayName = SipParser.consumeDisplayName(doubleQuote, buffer); boolean leftAngleBracket = true; // handle the case of an address that looks like: // "" // where the two double quotes is the ones that // caused a problem. This checks for that case and // consumes any potential white space that is left // after the consumption of that weird display name if (doubleQuote && displayName.isEmpty()) { SipParser.consumeWS(buffer); } // if no display name, then there may be a '<' present // and if so, consume it. if (displayName.isEmpty() && buffer.peekByte() == SipParser.LAQUOT) { buffer.readByte(); } else if (!displayName.isEmpty()) { // if display name, we DO expect a '<'. Note, there may or may // not be white space before the < SipParser.consumeWS(buffer); SipParser.expect(buffer, SipParser.LAQUOT); } else { leftAngleBracket = false; } // if there is no angle bracket then we are not protected // by the '<' '>' construct so then we must actually break // when we hit a ';' or a '?' since those would then be part // of the header and not the URI Buffer addrSpec = null; if (!leftAngleBracket) { try { final int index = buffer.indexOf(1024, SipParser.SEMI, SipParser.QUESTIONMARK, SipParser.CR, SipParser.LF); if (index >= 0) { final Buffer temp = buffer.readBytes(index - buffer.getReaderIndex()); addrSpec = SipParser.consumeAddressSpec(temp); } else { // none of the bytes we were looking for was found // so we will just consume the entire buffer addrSpec = SipParser.consumeAddressSpec(buffer); } } catch (final ByteNotFoundException e) { throw new SipParseException(buffer.getReaderIndex(), "Unable to parse the uri (addr-spec) portion of the address"); } } else { addrSpec = SipParser.consumeAddressSpec(true, buffer); } if (addrSpec == null) { throw new SipParseException(buffer.getReaderIndex(), "Unable to find the name-addr portion"); } if (displayName.isEmpty() && buffer.hasReadableBytes() && buffer.peekByte() == SipParser.RAQUOT) { buffer.readByte(); } else if (!displayName.isEmpty()) { // if display name, we DO expect a '>' SipParser.expect(buffer, SipParser.RAQUOT); } final URI uri = URI.frame(addrSpec); return new AddressImpl(original, displayName, uri); } static Builder builder() { return new Builder(); } static Builder withHost(final Buffer host) { final Builder builder = new Builder(); return builder.withHost(assertNotNull(host, "host cannot be null")); } static Builder withHost(final String host) { final Builder builder = new Builder(); return builder.withHost(assertNotNull(host, "host cannot be null")); } static Builder withURI(final URI uri) { final Builder builder = new Builder(); builder.withURI(uri); return builder; } class Builder { private SipURI.Builder uriBuilder; private Buffer displayName; private Builder() { // left empty intentionally } /** * Use this user for the {@link SipURI} that will be part of this {@link Address}. See * {@link #withHost(Buffer)} for more information. * * @param user * @return */ public Builder withUser(final Buffer user) { ensureURIBuilder().withUser(user); return this; } public Builder withUser(final String user) { ensureURIBuilder().withUser(user); return this; } /** * Use this port for the {@link SipURI} that will be part of this {@link Address}. See * {@link #withHost(Buffer)} for more information. * * @param port * @return */ public Builder withPort(final int port) { ensureURIBuilder().withPort(port); return this; } /** * Set a parameter on the URI within the {@link Address} object. * * @param name * @param value * @return * @throws SipParseException * @throws IllegalArgumentException */ public Builder withURIParameter(final Buffer name, final Buffer value) throws SipParseException, IllegalArgumentException { ensureURIBuilder().withParameter(name, value); return this; } /** * Set a parameter on the URI within the {@link Address} object. * * @param name * @param value * @return * @throws SipParseException * @throws IllegalArgumentException */ public Builder withURIParameter(final String name, final String value) throws SipParseException, IllegalArgumentException { ensureURIBuilder().withParameter(name, value); return this; } /** * Set a parameter on the URI within the {@link Address} object. * * @param name * @param value * @return * @throws SipParseException * @throws IllegalArgumentException */ public Builder withURIParameter(final String name, final int value) throws SipParseException, IllegalArgumentException { ensureURIBuilder().withParameter(name, value); return this; } /** * Set a parameter on the URI within the {@link Address} object. * * @param name * @param value * @return * @throws SipParseException * @throws IllegalArgumentException */ public Builder withURIParameter(final Buffer name, final int value) throws SipParseException, IllegalArgumentException { ensureURIBuilder().withParameter(name, value); return this; } /** * Wipe out all parameters on the URI within the {@link Address} object. * * Useful if you e.g. create a copy of a SipURI but you want to remove any potential parameters that may be on the SIP URI. * * @return */ public Builder withNoParameters() { ensureURIBuilder().withNoParameters(); return this; } /** * Set UDP as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportUDP() { ensureURIBuilder().useUDP(); return this; } /** * Set TCP as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportTCP() { ensureURIBuilder().useTCP(); return this; } /** * Set TLS as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportTLS() { ensureURIBuilder().useTLS(); return this; } /** * Set SCTP as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportSCTP() { ensureURIBuilder().useSCTP(); return this; } public Builder withTransport(final Buffer transport) throws SipParseException { ensureURIBuilder().withTransport(transport); return this; } public Builder withTransport(final String transport) throws SipParseException { ensureURIBuilder().withTransport(transport); return this; } /** * Set WS as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportWS() { ensureURIBuilder().useWS(); return this; } /** * Set WSS as the transport on the wrapped SIP URI (assuming this is a SIP URI that this * {@link Address} object is indeed wrapping). * * @return */ public Builder withTransportWSS() { ensureURIBuilder().useWSS(); return this; } /** * Use this host for the URI with this {@link Address}. This is a convenient way of creating * an {@link Address} with a {@link SipURI} and is the same as: * *
         * SipURI uri = SipURI.with().host(host).build();
         * Address.with(uri);
         * 
* * NOTE: you cannot specify a host and also specify a URI since those would conflict and an * exception will occur at the time you try and {@link #build()} the address. * * @param host * @return */ public Builder withHost(final Buffer host) { ensureURIBuilder().withHost(host); return this; } public Builder withHost(final String host) { ensureURIBuilder().withHost(host); return this; } public Builder withDisplayName(final Buffer displayName) { this.displayName = ifNull(displayName, Buffers.EMPTY_BUFFER); return this; } public Builder withDisplayName(final String displayName) { this.displayName = Buffers.wrap(ifNull(displayName, "")); return this; } private SipURI.Builder ensureURIBuilder() { if (uriBuilder == null) { uriBuilder = SipURI.builder(); } return uriBuilder; } public Builder withURI(final URI uri) { assertNotNull(uri, "URI cannot be null"); assertArgument(uri.isSipURI(), "Can only do SIP URIs right now"); uriBuilder = uri.toSipURI().copy(); return this; } public Address build() throws SipParseException { final SipURI uri = uriBuilder.build(); final Buffer uriBuffer = uri.toBuffer(); int size = uriBuffer.capacity(); boolean doubleQuoteIt = false; final boolean yesDisplayName = displayName != null && !displayName.isEmpty(); if (yesDisplayName) { size += displayName.capacity() + 1; // +1 for space if (doubleQuoteIt(displayName)) { doubleQuoteIt = true; size += 2; // because we will surround the display name with double quotes. } } // we will only add the angle brackets if there is a display name and/or there // are parameters on the URI. See RFC3261 section final boolean yesAngleQuoteIt = yesDisplayName || uriBuilder.hasParameters(); if (yesAngleQuoteIt) { size += 2; } final Buffer addressBuf = Buffers.createBuffer(size); if (yesDisplayName) { if (doubleQuoteIt) { addressBuf.write(SipParser.DOUBLE_QOUTE); displayName.getBytes(0, addressBuf); addressBuf.write(SipParser.DOUBLE_QOUTE); } else { displayName.getBytes(0, addressBuf); } addressBuf.write(SipParser.SP); } if (yesAngleQuoteIt) { addressBuf.write(SipParser.LAQUOT); uriBuffer.getBytes(0, addressBuf); addressBuf.write(SipParser.RAQUOT); } else { uriBuffer.getBytes(0, addressBuf); } return new AddressImpl(addressBuf, this.displayName, uri); } private boolean doubleQuoteIt(final Buffer buffer) { try { // Don't think a display name would be longer than 1k but if // it is and there is a space after that 1k then this fails... return buffer.indexOf(1024, SipParser.SP, SipParser.HTAB) != -1 && buffer.getByte(0) != SipParser.DOUBLE_QOUTE; } catch (final IOException e) { // ignore, can't happen return false; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy