package com.limegroup.gnutella.altlocs;
import java.io.IOException;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.io.Address;
import org.limewire.io.Connectable;
import org.limewire.io.ConnectableImpl;
import org.limewire.io.IP;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortForSelf;
import org.limewire.io.NetworkInstanceUtils;
import org.limewire.io.NetworkUtils;
import org.limewire.service.ErrorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.limegroup.gnutella.ApplicationServices;
import com.limegroup.gnutella.ConnectionServices;
import com.limegroup.gnutella.NetworkManager;
import com.limegroup.gnutella.PushEndpoint;
import com.limegroup.gnutella.PushEndpointFactory;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.URN;
@Singleton
public class AlternateLocationFactoryImpl implements AlternateLocationFactory {
private final NetworkManager networkManager;
private final PushEndpointFactory pushEndpointFactory;
private final ApplicationServices applicationServices;
private final ConnectionServices connectionServices;
private final NetworkInstanceUtils networkInstanceUtils;
private final IpPortForSelf ipPortForSelf;
@Inject
public AlternateLocationFactoryImpl(NetworkManager networkManager,
PushEndpointFactory pushEndpointFactory,
ApplicationServices applicationServices,
ConnectionServices connectionServices,
NetworkInstanceUtils networkInstanceUtils,
IpPortForSelf ipPortForSelf) {
this.networkManager = networkManager;
this.pushEndpointFactory = pushEndpointFactory;
this.applicationServices = applicationServices;
this.connectionServices = connectionServices;
this.networkInstanceUtils = networkInstanceUtils;
this.ipPortForSelf = ipPortForSelf;
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#create(com.limegroup.gnutella.URN)
*/
public AlternateLocation create(URN urn) {
if(urn == null) throw new NullPointerException("null sha1");
try {
// We try to guess whether we are firewalled or not. If the node
// has just started up and has not yet received an incoming connection
// our best bet is to see if we have received a connection in the past.
//
// However it is entirely possible that we have received connection in
// the past but are firewalled this session, so if we are connected
// we see if we received a conn this session only.
boolean open;
if (connectionServices.isConnected())
open = networkManager.acceptedIncomingConnection();
else
open = ConnectionSettings.EVER_ACCEPTED_INCOMING.getValue();
if (open && networkInstanceUtils.isValidExternalIpPort(ipPortForSelf)) {
return new DirectAltLoc(new ConnectableImpl(
NetworkUtils.ip2string(networkManager.getAddress()),
networkManager.getPort(),
networkManager.isIncomingTLSEnabled())
, urn, networkInstanceUtils);
} else {
return new PushAltLoc(pushEndpointFactory.createForSelf(), urn, applicationServices);
}
}catch(IOException bad) {
ErrorService.error(bad);
return null;
}
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#create(com.limegroup.gnutella.RemoteFileDesc)
*/
public AlternateLocation create(final RemoteFileDesc rfd)
throws IOException {
if(rfd == null)
throw new NullPointerException("cannot accept null RFD");
URN urn = rfd.getSHA1Urn();
if(urn == null)
throw new NullPointerException("cannot accept null URN");
Address address = rfd.getAddress();
if (address instanceof Connectable) {
return new DirectAltLoc((Connectable)address, urn, networkInstanceUtils);
} else {
PushEndpoint copy;
if (address instanceof PushEndpoint) {
copy = (PushEndpoint)address;
} else {
throw new IllegalArgumentException(address.getClass() + " should not have become an alternate location: " + rfd.getCreationTime());
// this is the old code, that would fail silently
// copy = pushEndpointFactory.createPushEndpoint(rfd.getClientGUID(), IpPort.EMPTY_SET, PushEndpoint.PLAIN, 0, null);
}
return new PushAltLoc(copy,urn, applicationServices);
}
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#createPushAltLoc(com.limegroup.gnutella.PushEndpoint, com.limegroup.gnutella.URN)
*/
public AlternateLocation createPushAltLoc(PushEndpoint pe, URN urn) {
return new PushAltLoc(pe, urn, applicationServices);
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#createDirectDHTAltLoc(org.limewire.io.IpPort, com.limegroup.gnutella.URN, long, byte[])
*/
public AlternateLocation createDirectDHTAltLoc(IpPort ipp, URN urn,
long fileSize, byte[] ttroot) throws IOException {
return new DirectDHTAltLoc(ipp, urn, fileSize, ttroot, networkInstanceUtils);
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#createDirectAltLoc(org.limewire.io.IpPort, com.limegroup.gnutella.URN)
*/
public AlternateLocation createDirectAltLoc(IpPort ipp, URN urn) throws IOException {
return new DirectAltLoc(ipp, urn, networkInstanceUtils);
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#create(java.lang.String, com.limegroup.gnutella.URN, boolean)
*/
public AlternateLocation create(String location,
URN urn,
boolean tlsCapable) throws IOException {
if(location == null || location.equals(""))
throw new IOException("null or empty location");
if(urn == null)
throw new IOException("null URN.");
// Case 1. Direct Alt Loc
if (location.indexOf(";")==-1) {
IpPort addr = createUrlFromMini(location, urn, tlsCapable);
return new DirectAltLoc(addr, urn, networkInstanceUtils);
}
//Case 2. Push Alt loc
PushEndpoint pe = pushEndpointFactory.createPushEndpoint(location);
return new PushAltLoc(pe,urn, applicationServices);
}
/* (non-Javadoc)
* @see com.limegroup.gnutella.altlocs.AlternateLocationFactory#create(java.lang.String, com.limegroup.gnutella.URN)
*/
public AlternateLocation create(final String location,
final URN urn) throws IOException {
return create(location, urn, false);
}
/**
* Creates a new <tt>URL</tt> based on the IP and port in the location
* The location MUST be a dotted IP address.
*/
private IpPort createUrlFromMini(final String location, URN urn, boolean tlsCapable)
throws IOException {
int port = location.indexOf(':');
final String loc =
(port == -1 ? location : location.substring(0, port));
//Use the IP class as a quick test to make sure it numeric
try {
new IP(loc);
} catch(IllegalArgumentException iae) {
throw new IOException("invalid location: " + location);
}
//But, IP still could have passed if it thought there was a submask
if( loc.indexOf('/') != -1 )
throw new IOException("invalid location: " + location);
//Then make sure it's a valid IP addr.
if(!NetworkUtils.isValidAddress(loc))
throw new IOException("invalid location: " + location);
if( port == -1 )
port = 6346; // default port if not included.
else {
// Not enough room for a port.
if(location.length() < port+1)
throw new IOException("invalid location: " + location);
try {
port = Integer.parseInt(location.substring(port+1));
} catch(NumberFormatException nfe) {
throw new IOException("invalid location: " + location);
}
}
if(!NetworkUtils.isValidPort(port))
throw new IOException("invalid port: " + port);
return new ConnectableImpl(loc,port, tlsCapable);
}
}