/*
* 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 <http://www.gnu.org/licenses/>
*
*/
package org.restcomm.media.rtp.sdp;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.restcomm.media.spi.utils.Text;
/**
* Represents an ICEv19 candidate field as defined on
* http://tools.ietf.org/html/rfc5245
* <p>
* a=candidate:1995739850 1 udp 2113937151 192.168.1.65 54550 typ host
* generation 0<br>
* a=candidate:2162486046 1 udp 1845501695 85.241.121.60 60495 typ srflx raddr
* 192.168.1.65 rport 54550 generation 0<br>
* a=candidate:2564697628 1 udp 33562367 75.126.93.124 53056 typ relay raddr
* 85.241.121.60 rport 55027 generation 0
* </p>
*
* @author Henrique Rosa
* @deprecated Use new /network/io/sdp library
*/
@Deprecated
public class CandidateField implements Comparable<CandidateField>{
public static final Text CANDIDATE_FIELD = new Text("a=candidate");
/**
* According to draft-ietf-mmusic-ice-19, the foundation is used to optimize
* ICE performance in the Frozen algorithm
*/
private Text foundation;
/**
* An indicator for (S)RTP or (S)RTCP
*/
private Text componentId;
/**
* When the user agents perform address allocations to gather TCP-based
* candidates, two types of candidates can be obtained.<br>
* These are active candidates (TCP-ACT) or passive candidates (TCP-PASS).<br>
* An active candidate is one for which the agent will attempt to open an
* outbound connection, but will not receive incoming connection requests.<br>
* A passive candidate is one for which the agent will receive incoming
* connection attempts, but not attempt a connection.
*/
private Text protocol;
/**
* This is the weight used to prioritize single candidates.<br>
* Higher numbers are preferred over lower numbers, in case a connection can
* be established to this IP, port and protocol combination.<br>
* Each candidate for a media stream must have a unique priority (positive
* integer up to 2^31-1).
*/
private Text weight;
/**
* The IP address the second party can connect to.
*/
private Text address;
/**
* The port number the second party can connect to. The port for TCP RTP and
* RTCP gets multiplexed, whereas UDP ports for RTP and RTCP always differ.
*/
private Text port;
/**
* This is type information, describing the type of "advertised" address.<br>
* <b>host:</b> This is a local address<br>
* <b>relay:</b> This is the IP address from a relay (TURN) server<br>
* <b>srflx:</b> Server reflexive address is the NATed IP address<br>
*/
private Text type;
private Text relatedAddress;
private Text relatedPort;
private Text generation;
public CandidateField(Text line) {
parseCandidateLine(line);
}
public Text getFoundation() {
return foundation;
}
public Text getComponentId() {
return componentId;
}
public Text getProtocol() {
return protocol;
}
public Text getWeight() {
return weight;
}
public Text getAddress() {
return address;
}
public Text getPort() {
return port;
}
public Text getType() {
return type;
}
public Text getRelatedAddress() {
return relatedAddress;
}
public boolean hasRelatedAddress() {
return this.relatedAddress != null;
}
public Text getRelatedPort() {
return relatedPort;
}
public Text getGeneration() {
return generation;
}
private void parseCandidateLine(Text line) {
if (!line.startsWith(CANDIDATE_FIELD)) {
throw new IllegalArgumentException(
"Not a valid candidate attribute" + line);
}
try {
Text data = (Text) line.subSequence(CANDIDATE_FIELD.length() + 1, line.length());
Collection<Text> tokens = data.split(' ');
Iterator<Text> iterator = tokens.iterator();
// Foundation
this.foundation = iterator.next();
this.componentId = iterator.next();
this.protocol = iterator.next();
this.weight = iterator.next();
this.address = iterator.next();
this.port = iterator.next();
// skip 'typ'
iterator.next();
this.type = iterator.next();
AddressType addressType = AddressType.fromDescription(type);
if (addressType == null) {
throw new IllegalArgumentException(
"Unrecognized address type: " + type);
}
switch (addressType) {
case RELAY:
case SRFLX:
// skip raddr
iterator.next();
this.relatedAddress = iterator.next();
// skip rport
iterator.next();
this.relatedPort = iterator.next();
break;
default:
break;
}
// Generation attribute is present on Chrome SDP but not on FireFox
if(iterator.hasNext()) {
// skip 'generation'
iterator.next();
this.generation = iterator.next();
}
} catch (NoSuchElementException e) {
throw new IllegalArgumentException("Candidate line is badly formated: "+ e.getMessage(), e);
}
}
public enum AddressType {
HOST("host"), RELAY("relay"), SRFLX("srflx");
private Text description;
private AddressType(String description) {
this.description = new Text(description);
}
public Text getDescription() {
return description;
}
public static AddressType fromDescription(Text value) {
for (AddressType type : values()) {
if (type.getDescription().equals(value)) {
return type;
}
}
return null;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(CANDIDATE_FIELD).append(":");
builder.append(this.foundation).append(" ");
builder.append(this.componentId).append(" ");
builder.append(this.protocol).append(" ");
builder.append(this.weight).append(" ");
builder.append(this.address).append(" ");
builder.append(this.port).append(" ");
builder.append("typ ");
builder.append(this.type).append(" ");
switch (AddressType.fromDescription(this.type)) {
case RELAY:
case SRFLX:
builder.append("raddr ");
builder.append(this.relatedAddress).append(" ");
builder.append("rport ");
builder.append(this.relatedPort).append(" ");
break;
default:
break;
}
builder.append("generation ");
builder.append(this.generation);
return builder.toString();
}
/**
* Compares two {@link CandidateField} by weight.
*/
public int compareTo(CandidateField o) {
if (o == null) {
return 1;
}
int myWeight = this.weight == null ? 0 : this.weight.toInteger();
int otherWeight = o.getWeight() == null ? 0 : o.getWeight().toInteger();
if(myWeight > otherWeight) {
return 1;
}
if(myWeight < otherWeight) {
return -1;
}
return 0;
}
}