package com.limegroup.gnutella.downloader; import static com.limegroup.gnutella.Constants.MAX_FILE_SIZE; import java.util.Arrays; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.auth.Credentials; import org.limewire.core.settings.SearchSettings; import org.limewire.io.Address; import org.limewire.io.GUID; import org.limewire.net.address.AddressFactory; import org.limewire.security.SecureMessage.Status; import org.limewire.util.Objects; import com.limegroup.gnutella.PushEndpoint; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.UrnSet; import com.limegroup.gnutella.downloader.serial.RemoteHostMemento; import com.limegroup.gnutella.http.HTTPConstants; import com.limegroup.gnutella.xml.LimeXMLDocument; /** * A default implementation for {@link RemoteFileDesc}. */ public class RemoteFileDescImpl implements RemoteFileDesc { @SuppressWarnings("unused") private static final Log LOG = LogFactory.getLog(RemoteFileDesc.class); private final String _filename; private final long _index; private final byte[] _clientGUID; private final int _speed; private final int _quality; private final boolean _replyToMulticast; private final LimeXMLDocument _xmlDoc; private final Set<URN> _urns; /** * Boolean indicating whether or not the remote host has browse host * enabled. */ private final boolean _browseHostEnabled; private final String _vendor; public static final String TYPE = "RFD"; /** * Whether or not the remote host supports HTTP/1.1 This is purposely NOT * IMMUTABLE. Before we connect, we can only assume the remote host supports * HTTP/1.1 by looking at the set of URNs. If any exist, we assume HTTP/1.1 * is supported (because URNs were added to Gnutella after HTTP/1.1). Once * we connect, this value is set to be whatever the host reports in the * response line. * <p> * When deserializing, this value may be wrong for older download.dat files. * (Older versions will always set this to false, because the field did not * exist.) To counter that, when deserializing, if this is false, we set it * to true if any URNs are present. */ private boolean _http11; /** True if this host is TLS capable. */ private boolean _tlsCapable; /** * The cached hash code for this RFD. */ private int _hashCode = 0; /** * The creation time of this file. */ private final long _creationTime; /** * the spam rating of this rfd. */ private volatile float _spamRating = 0.f; /** the security of this RemoteFileDesc. */ private Status _secureStatus = Status.INSECURE; private final long _size; private final Address address; private final AddressFactory addressFactory; public RemoteFileDescImpl(Address address, long index, String filename, long size, byte[] clientGUID, int speed, int quality, boolean browseHost, LimeXMLDocument xmlDoc, Set<? extends URN> urns, boolean replyToMulticast, String vendor, long createTime, boolean http11, AddressFactory addressFactory) { this.addressFactory = addressFactory; this.address = Objects.nonNull(address, "address"); if ((speed & 0xFFFFFFFF00000000L) != 0) throw new IllegalArgumentException("invalid speed: " + speed); if (filename.equals("")) throw new IllegalArgumentException("cannot accept empty string file name"); if (size < 0 || size > MAX_FILE_SIZE) throw new IllegalArgumentException("invalid size: " + size); if ((index & 0xFFFFFFFF00000000L) != 0) throw new IllegalArgumentException("invalid index: " + index); _speed = speed; _index = index; _filename = filename; _size = size; _clientGUID = clientGUID; _quality = quality; _browseHostEnabled = browseHost; _replyToMulticast = replyToMulticast; _vendor = vendor; _creationTime = createTime; _xmlDoc = xmlDoc; _http11 = http11; _urns = UrnSet.resolve(urns); } /** Returns true if the host supports TLS. */ public boolean isTLSCapable() { return _tlsCapable; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isHTTP11() */ public boolean isHTTP11() { return _http11; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#setHTTP11(boolean) */ public void setHTTP11(boolean http11) { _http11 = http11; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isMe(byte[]) */ public boolean isMe(byte[] myClientGUID) { return Arrays.equals(_clientGUID, myClientGUID); } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isFromAlternateLocation() */ public boolean isFromAlternateLocation() { return "ALT".equals(_vendor); } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getCreationTime() */ public long getCreationTime() { return _creationTime; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getIndex() */ public final long getIndex() { return _index; } public final long getSize() { return _size; } /** * Accessor for the file name for this file, which can be <tt>null</tt>. * * @return the file name for this file, which can be <tt>null</tt> */ public final String getFileName() { return _filename; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getClientGUID() */ public final byte[] getClientGUID() { return _clientGUID; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getSpeed() */ public final int getSpeed() { return _speed; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getVendor() */ public final String getVendor() { return _vendor; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isBrowseHostEnabled() */ public final boolean isBrowseHostEnabled() { return _browseHostEnabled; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getQuality() */ public final int getQuality() { return _quality; } /** * Returns the <tt>LimeXMLDocument</tt> for this <tt>RemoteFileDesc</tt>, * which can be <tt>null</tt>. * * @return the <tt>LimeXMLDocument</tt> for this <tt>RemoteFileDesc</tt>, * which can be <tt>null</tt>. */ public final LimeXMLDocument getXMLDocument() { return _xmlDoc; } /** * Accessor for the <tt>Set</tt> of URNs for this <tt>RemoteFileDesc</tt>. * * @return the <tt>Set</tt> of URNs for this <tt>RemoteFileDesc</tt> */ public final Set<URN> getUrns() { return _urns; } /** * Accessor for the SHA1 URN for this <tt>RemoteFileDesc</tt>. * * @return the SHA1 <tt>URN</tt> for this <tt>RemoteFileDesc</tt>, or * <tt>null</tt> if there is none */ public final URN getSHA1Urn() { return UrnSet.getSha1(_urns); } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getUrl() */ public String getUrlPath() { URN urn = getSHA1Urn(); if (urn == null) { return "/get/" + _index + "/" + _filename; } else { return HTTPConstants.URI_RES_N2R + urn.httpStringValue(); } } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isReplyToMulticast() */ public final boolean isReplyToMulticast() { return _replyToMulticast; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#isAltLocCapable() */ public final boolean isAltLocCapable() { if (address instanceof PushEndpoint) { if (((PushEndpoint) address).getProxies().isEmpty()) { return false; } } if (getSHA1Urn() != null && !_replyToMulticast) { return true; } return false; } /** * Overrides <tt>Object.equals</tt> to return instance equality based on the * equality of all <tt>RemoteFileDesc</tt> fields. * * @return <tt>true</tt> if all of fields of this <tt>RemoteFileDesc</tt> * instance are equal to all of the fields of the specified object, * and <tt>false</tt> if this is not the case, or if the specified * object is not a <tt>RemoteFileDesc</tt>. * * Dynamic values such as _http11, and _availableSources are not * checked here, as they can change and still be considered the same * "remote file". * * The _host field may be equal for many firewalled locations; * therefore it is necessary that we distinguish those by their * client GUIDs */ @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof RemoteFileDesc)) return false; RemoteFileDesc other = (RemoteFileDesc) o; if (!address.equals(other.getAddress())) { return false; } if (_size != other.getSize()) return false; if ((_clientGUID == null) != (other.getClientGUID() == null)) return false; if (_clientGUID != null && !(Arrays.equals(_clientGUID, other.getClientGUID()))) return false; if (_urns.isEmpty() && other.getUrns().isEmpty()) return Objects.equalOrNull(_filename, other.getFileName()); else return _urns.equals(other.getUrns()); } /** * Overrides the hashCode method of Object to meet the contract of hashCode. * Since we override equals, it is necessary to also override hashcode to * ensure that two "equal" RemoteFileDescs return the same hashCode, less we * unleash unknown havoc on the hash-based collections. * * @return a hash code value for this object */ @Override public int hashCode() { if (_hashCode == 0) { int result = 17; result = (37 * result) + address.hashCode(); result = (int) ((37 * result) + _size); result = (37 * result) + _urns.hashCode(); if (_clientGUID != null) result = (37 * result) + (new GUID(_clientGUID)).hashCode(); _hashCode = result; } return _hashCode; } @Override public String toString() { return address.toString(); } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#setSpamRating(float) */ public void setSpamRating(float rating) { _spamRating = rating; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getSpamRating() */ public float getSpamRating() { return _spamRating; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#getSecureStatus() */ public Status getSecureStatus() { return _secureStatus; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#setSecureStatus(int) */ public void setSecureStatus(Status secureStatus) { this._secureStatus = secureStatus; } /* * (non-Javadoc) * * @see com.limegroup.gnutella.RemoteFileDesc#toMemento() */ public RemoteHostMemento toMemento() { return new RemoteHostMemento(address, _filename, _index, _clientGUID, _speed, _size, _quality, _replyToMulticast, xmlString(), _urns, _browseHostEnabled, _vendor, _http11, TYPE, addressFactory); } private String xmlString() { if (_xmlDoc == null) return null; else return _xmlDoc.getXMLString(); } @Override public boolean isSpam() { return getSpamRating() >= SearchSettings.FILTER_SPAM_RESULTS.getValue(); } @Override public Address getAddress() { return address; } @Override public Credentials getCredentials() { return null; } }