package org.limewire.xmpp.client.impl.messages.connectrequest; import java.io.IOException; import java.text.MessageFormat; import org.apache.commons.codec.binary.Base64; import org.jivesoftware.smack.packet.IQ; import org.limewire.io.Connectable; import org.limewire.io.GUID; import org.limewire.io.NetworkUtils; import org.limewire.net.ConnectBackRequest; import org.limewire.net.address.ConnectableSerializer; import org.limewire.util.Objects; import org.limewire.util.StringUtils; import org.limewire.xmpp.client.impl.messages.InvalidIQException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; /** * IQ to be send to request the other peer to open a connection back to this peer. * <p> * The connection can be of two types: * <pre> * 1) a regular TCP connection, in this case {@link #getSupportedFWTVersion()} is 0 * 2) a reliable udp connection, in this case {@link #getSupportedFWTVersion()} conveys * the supported protocol version * In both cases a valid address for connecting needs to be provided. * </pre> */ public class ConnectBackRequestIQ extends IQ { // private final Log LOG = LogFactory.getLog(ConnectRequestIQ.class); private final ConnectBackRequest request; public static final String ELEMENT_NAME = "connect-back-request"; public static final String NAME_SPACE = "jabber:iq:lw-connect-request"; /** * Only constructs valid connect request iqs, otherwise throws {@link InvalidIQException}. */ public ConnectBackRequestIQ(XmlPullParser parser) throws IOException, XmlPullParserException, InvalidIQException { int eventType = parser.getEventType(); GUID guid = null; int fwtVersion = -1; Connectable connectable = null; for (; eventType != XmlPullParser.END_DOCUMENT; eventType = parser.next()) { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals(ELEMENT_NAME)) { String value = parser.getAttributeValue(null, "client-guid"); if (value == null) { throw new InvalidIQException("no guid provided"); } try { guid = new GUID(value); } catch (IllegalArgumentException iae) { throw new InvalidIQException("invalid guid: " + value, iae); } value = parser.getAttributeValue(null, "supported-fwt-version"); if (value == null) { throw new InvalidIQException("no fwt version provided"); } try { fwtVersion = Integer.parseInt(value); } catch (NumberFormatException nfe) { throw new InvalidIQException("fwt version no a valid number: " + value, nfe); } } else if (parser.getName().equals("address")) { String type = parser.getAttributeValue(null, "type"); ConnectableSerializer serializer = new ConnectableSerializer(); if (type == null || !type.equals(serializer.getAddressType())) { throw new InvalidIQException("no address type provided or invalid: " + type); } String value = parser.getAttributeValue(null, "value"); if (value == null) { throw new InvalidIQException("no address value found"); } connectable = serializer.deserialize(Base64.decodeBase64(StringUtils.toUTF8Bytes(value))); if (!NetworkUtils.isValidIpPort(connectable)) { throw new InvalidIQException("invalid address: " + connectable); } } } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(ELEMENT_NAME)) { // exit loop when we see end tag for connect-request break; } } if (guid == null || fwtVersion == -1 || connectable == null) { throw new InvalidIQException(MessageFormat.format("incomplete connect request, {0}, {1}, {2}", guid, fwtVersion, connectable)); } request = new ConnectBackRequest(connectable, guid, fwtVersion); } public ConnectBackRequestIQ(ConnectBackRequest request) { this.request = Objects.nonNull(request, "request"); } @Override public String getChildElementXML() { ConnectableSerializer serializer = new ConnectableSerializer(); String message = "<{0} xmlns=\"{1}\" client-guid=\"{2}\" supported-fwt-version=\"{3}\"><address type=\"{4}\" value=\"{5}\"/></{6}>"; try { return MessageFormat.format(message, ELEMENT_NAME, NAME_SPACE, request.getClientGuid().toHexString(), String.valueOf(request.getSupportedFWTVersion()), serializer.getAddressType(), StringUtils.getUTF8String(Base64.encodeBase64(serializer.serialize(request.getAddress()))), ELEMENT_NAME); } catch (IOException e) { throw new RuntimeException(e); } } public ConnectBackRequest getConnectBackRequest() { return request; } @Override public String toString() { return StringUtils.toString(this); } }