package org.limewire.libtorrent;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.limewire.bittorrent.TorrentException;
import org.limewire.bittorrent.TorrentFileEntry;
import org.limewire.bittorrent.TorrentInfo;
import org.limewire.bittorrent.TorrentManagerSettings;
import org.limewire.bittorrent.TorrentPiecesInfo;
import org.limewire.bittorrent.TorrentStatus;
import org.limewire.inject.LazySingleton;
import org.limewire.libtorrent.callback.AlertCallback;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import org.limewire.util.ExceptionUtils;
import org.limewire.util.OSUtils;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.ptr.IntByReference;
/**
* Wrapper class for the LibTorrent c interface. Provides library loading logic,
* and handles rethrowing c++ exceptions as java exceptions.
*/
@LazySingleton
class LibTorrentWrapper {
private static final Log LOG = LogFactory.getLog(LibTorrentWrapper.class);
private final AtomicBoolean loaded = new AtomicBoolean(false);
private LibTorrent libTorrent;
/**
* Initializes the LibTorrent library. Finding necessary dependencies first,
* then loading the libtorrent library as a jna lib.
*/
void initialize(TorrentManagerSettings torrentSettings) {
String torrentWrapperLibName = getLibraryName();
try {
this.libTorrent = (LibTorrent) Native.loadLibrary(torrentWrapperLibName, LibTorrent.class);
NativeLibrary lib = NativeLibrary.getInstance(torrentWrapperLibName);
validate(lib);
init(torrentSettings);
// TODO add get_version method to the wrapper that can be checked
// here as well.
loaded.set(true);
} catch (Throwable e) {
LOG.error("Failure loading the libtorrent libraries.", e);
if (torrentSettings.isReportingLibraryLoadFailture()) {
ExceptionUtils.reportOrReturn(e);
}
}
}
/**
* @return the name of the shared library containing the libtorrentwrapper code.
* Should match the file name of the library on disk.
*/
private static String getLibraryName() {
if (OSUtils.isLinux()) {
String arch = OSUtils.getOSArch();
// Linux requires binaries for every arch.
if (arch.equals("x86_64") || arch.equals("amd64")) {
return "torrent-wrapper64";
}
}
return "torrent-wrapper";
}
/**
* Validates that all of the expected functions exist in the library.
*/
private void validate(NativeLibrary lib) {
for (Method method : LibTorrent.class.getMethods()) {
// throws exception if there is an error finding the function
lib.getFunction(method.getName());
}
}
/**
* Returns true if the native library was found and was able to load
* successfully.
*/
public boolean isLoaded() {
return loaded.get();
}
private void init(TorrentManagerSettings torrentSettings) {
LOG.debugf("before init");
catchWrapperException(libTorrent.init(new LibTorrentSettings(torrentSettings)));
LOG.debugf("after init");
}
public void add_torrent(String sha1, String trackerURI, String torrentPath, String savePath,
String fastResumePath) {
LOG.debugf("before add_torrent: {0}", sha1);
catchWrapperException(libTorrent.add_torrent(sha1, trackerURI,
torrentPath != null ? new WString(torrentPath) : null,
new WString(savePath),
new WString(fastResumePath)));
LOG.debugf("after add_torrent: {0}", sha1);
}
public void freeze_and_save_all_fast_resume_data(AlertCallback alertCallback) {
LOG.debug("before get_alerts");
catchWrapperException(libTorrent.freeze_and_save_all_fast_resume_data(alertCallback));
LOG.debug("after get_alerts");
}
public void get_alerts(AlertCallback alertCallback) {
LOG.debug("before get_alerts");
catchWrapperException(libTorrent.get_alerts(alertCallback));
LOG.debug("after get_alerts");
}
public void set_ip_filter(IpFilterCallback ipFilterCallback) {
LOG.debug("before set_ip_filter");
catchWrapperException(libTorrent.set_ip_filter(ipFilterCallback));
LOG.debug("after set_ip_filter");
}
public void pause_torrent(String id) {
LOG.debugf("before pause_torrent: {0}", id);
catchWrapperException(libTorrent.pause_torrent(id));
LOG.debugf("after pause_torrent: {0}", id);
}
public void resume_torrent(String id) {
LOG.debugf("before resume_torrent: {0}", id);
catchWrapperException(libTorrent.resume_torrent(id));
LOG.debugf("after resume_torrent: {0}", id);
}
public void scrape_tracker(String id) {
LOG.debugf("before scrape_tracker: {0}", id);
catchWrapperException(libTorrent.scrape_tracker(id));
LOG.debugf("after scrape_tracker: {0}", id);
}
public void force_reannounce(String id) {
LOG.debugf("before force_reannounce: {0}", id);
catchWrapperException(libTorrent.force_reannounce(id));
LOG.debugf("after force_reannounce: {0}", id);
}
public void get_torrent_status(String id, TorrentStatus status) {
LOG.debugf("before get_torrent_status: {0}", id);
catchWrapperException(libTorrent.get_torrent_status(id, status));
LOG.debugf("after get_torrent_status: {0}", id);
}
public void remove_torrent(String id) {
LOG.debugf("before remove_torrent: {0}", id);
catchWrapperException(libTorrent.remove_torrent(id));
LOG.debugf("after remove_torrent: {0}", id);
}
public LibTorrentPeer[] get_peers(String id) {
LOG.debugf("before get_num_peers: {0}", id);
IntByReference numPeersReference = new IntByReference();
catchWrapperException(libTorrent.get_num_peers(id, numPeersReference));
LOG.debugf("after get_num_peers: {0} - {1}", id, numPeersReference);
int numPeers = numPeersReference.getValue();
if (numPeers == 0) {
return new LibTorrentPeer[0];
}
LibTorrentPeer[] torrentPeers = new LibTorrentPeer[numPeers];
Pointer[] torrentPeersPointers = new Pointer[numPeers];
for (int i = 0; i < torrentPeersPointers.length; i++) {
LibTorrentPeer torrentPeer = new LibTorrentPeer();
torrentPeers[i] = torrentPeer;
torrentPeersPointers[i] = torrentPeer.getPointer();
}
LOG.debugf("before get_peers: {0}", id);
catchWrapperException(libTorrent.get_peers(id, torrentPeersPointers,
torrentPeersPointers.length));
// Trim any empty peers if the list was not filled
List<LibTorrentPeer> remainingTorrentPeers = new ArrayList<LibTorrentPeer>(torrentPeers.length);
for (int i = 0; i < torrentPeers.length; i++) {
torrentPeers[i].read();
if (torrentPeers[i].getIPAddress() != null && !torrentPeers[i].getIPAddress().isEmpty()) {
remainingTorrentPeers.add(torrentPeers[i]);
}
}
LOG.debugf("before free_peers: {0}", id);
free_peers(torrentPeersPointers);
LOG.debugf("after free_peers: {0}", id);
LOG.debugf("after get_peers: {0}", id);
return remainingTorrentPeers.toArray(new LibTorrentPeer[remainingTorrentPeers.size()]);
}
private void free_peers(Pointer[] torrentPeersPointers) {
catchWrapperException(libTorrent.free_peers(torrentPeersPointers,
torrentPeersPointers.length));
}
public void signal_fast_resume_data_request(String id) {
LOG.debugf("before print signal_fast_resume_data_request: {0}", id);
catchWrapperException(libTorrent.signal_fast_resume_data_request(id));
LOG.debugf("after print signal_fast_resume_data_request: {0}", id);
}
public void clear_error_and_retry(String id) {
LOG.debugf("before print clear_error_and_retry: {0}", id);
catchWrapperException(libTorrent.clear_error_and_retry(id));
LOG.debugf("after print clear_error_and_retry: {0}", id);
}
public void move_torrent(String id, String absolutePath) {
LOG.debugf("before move_torrent: {0} - {1}", id, absolutePath);
catchWrapperException(libTorrent.move_torrent(id, new WString(absolutePath)));
LOG.debugf("after move_torrent: {0} - {1}", id, absolutePath);
}
public void abort_torrents() {
LOG.debug("before abort");
catchWrapperException(libTorrent.abort_torrents());
LOG.debug("after abort");
}
public void free_torrent_status(LibTorrentStatus status) {
LOG.debugf("before free_torrent_status: {0}", status);
catchWrapperException(libTorrent.free_torrent_status(status.getPointer()));
LOG.debugf("after free_torrent_status: {0}", status);
}
private void catchWrapperException(WrapperStatus status) {
if (status != null) {
throw new TorrentException(status.message, status.type);
}
}
public void update_settings(TorrentManagerSettings torrentSettings) {
LOG.debugf("before update_settings: {0}", torrentSettings);
catchWrapperException(libTorrent.update_settings(new LibTorrentSettings(torrentSettings)));
LOG.debugf("after update_settings: {0}", torrentSettings);
}
public void start_dht() {
start_dht(null);
}
public void start_dht(File dhtStateFile) {
LOG.debugf("before start_dht");
WString dhtStateFilePath = dhtStateFile != null ? new WString(dhtStateFile
.getAbsolutePath()) : null;
catchWrapperException(libTorrent.start_dht(dhtStateFilePath));
LOG.debugf("after start_dht");
}
public void stop_dht() {
LOG.debugf("before stop_dht");
catchWrapperException(libTorrent.stop_dht());
LOG.debugf("after stop_dht");
}
public void save_dht_state(File dhtStateFile) {
LOG.debugf("before save_dht_state");
if (dhtStateFile != null) {
dhtStateFile.getParentFile().mkdirs();
}
WString dhtStateFilePath = dhtStateFile != null ? new WString(dhtStateFile
.getAbsolutePath()) : null;
catchWrapperException(libTorrent.save_dht_state(dhtStateFilePath));
LOG.debugf("after save_dht_state");
}
public void add_dht_router(String address, int port) {
LOG.debugf("before add_dht_router: address={0}, port={1}", address, port);
catchWrapperException(libTorrent.add_dht_router(address, port));
LOG.debugf("after add_dht_router: address={0}, port={1}", address, port);
}
public void add_dht_node(String address, int port) {
LOG.debugf("before add_dht_node: address={0}, port={1}", address, port);
catchWrapperException(libTorrent.add_dht_node(address, port));
LOG.debugf("after add_dht_node: address={0}, port={1}", address, port);
}
public void start_upnp() {
LOG.debugf("before start_upnp");
catchWrapperException(libTorrent.start_upnp());
LOG.debugf("after start_upnp");
}
public void stop_upnp() {
LOG.debugf("before stop_upnp");
catchWrapperException(libTorrent.stop_upnp());
LOG.debugf("after stop_upnp");
}
public void start_lsd() {
LOG.debugf("before start_lsd");
catchWrapperException(libTorrent.start_lsd());
LOG.debugf("after start_lsd");
}
public void stop_lsd() {
LOG.debugf("before stop_lsd");
catchWrapperException(libTorrent.stop_lsd());
LOG.debugf("after stop_lsd");
}
public void start_natpmp() {
LOG.debugf("before start_natpmp");
catchWrapperException(libTorrent.start_natpmp());
LOG.debugf("after start_natpmp");
}
public void stop_natpmp() {
LOG.debugf("before stop_natpmp");
catchWrapperException(libTorrent.stop_natpmp());
LOG.debugf("after stop_natpmp");
}
/**
* Set the target seed ratio for this torrent.
*/
// TODO: disable until talking to everrettt
public void set_seed_ratio(String id, float seed_ratio) {
LOG.debugf("before set_seed_ratio: {0} - {1}", id, seed_ratio);
catchWrapperException(libTorrent.set_seed_ratio(id, seed_ratio));
LOG.debugf("after set_seed_ratio");
}
/**
* Sets the upload limit for this particular Torrent.
*/
public void set_upload_limit(String id, int limit) {
LOG.debugf("before set_upload_limit: {0} - {1}", id, limit);
catchWrapperException(libTorrent.set_upload_limit(id, limit));
LOG.debugf("after set_upload_limit");
}
/**
* Returns the upload limit for this particular Torrent. If
* the global upload limit is being used, this will return 0.
*/
public int get_upload_limit(String id) {
LOG.debugf("before get_upload_limit");
IntByReference limit = new IntByReference();
catchWrapperException(libTorrent.get_upload_limit(id, limit));
LOG.debugf("after get_upload_limit");
return limit.getValue();
}
/**
* Sets the download limit for this particular Torrent.
*/
public void set_download_limit(String id, int limit) {
LOG.debugf("before set_download_limit");
catchWrapperException(libTorrent.set_download_limit(id, limit));
LOG.debugf("after set_download_limit");
}
/**
* Returns the download limit for this particular Torrent. If
* the global download limit is being used, this will return 0.
*/
public int get_download_limit(String id) {
LOG.debugf("before get_download_limit");
IntByReference limit = new IntByReference();
catchWrapperException(libTorrent.get_download_limit(id, limit));
LOG.debugf("after get_download_limit");
return limit.getValue();
}
/**
* Sets the file priority for the given index.
*/
public void set_file_priorities(String id, int[] priorities) {
LOG.debugf("before set_file_priorities");
catchWrapperException(libTorrent.set_file_priorities(id, priorities, priorities.length));
LOG.debugf("after set_file_priorities");
}
/**
* Returns the number of files for the given torrent.
*/
public int get_num_files(String id) {
LOG.debugf("before get_num_files");
IntByReference numFiles = new IntByReference();
catchWrapperException(libTorrent.get_num_files(id, numFiles));
LOG.debugf("after get_num_files");
return numFiles.getValue();
}
/**
* Returns the files for the given torrent.
*/
public LibTorrentFileEntry[] get_files(String id) {
LOG.debugf("before get_files");
int numFiles = get_num_files(id);
LibTorrentFileEntry[] fileEntries = new LibTorrentFileEntry[numFiles];
Pointer[] filePointers = new Pointer[numFiles];
for (int i = 0; i < fileEntries.length; i++) {
LibTorrentFileEntry fileEntry = new LibTorrentFileEntry();
fileEntries[i] = fileEntry;
filePointers[i] = fileEntry.getPointer();
}
catchWrapperException(libTorrent.get_files(id, filePointers));
for (int i = 0; i < fileEntries.length; i++) {
fileEntries[i].read();
}
LOG.debugf("after get_files");
return fileEntries;
}
public void set_auto_managed_torrent(String sha1, boolean auto_managed) {
LOG.debugf("before set_auto_managed_torrent: {0} - {1}", sha1, auto_managed);
catchWrapperException(libTorrent.set_auto_managed_torrent(sha1, auto_managed));
LOG.debugf("after set_auto_managed_torrent: {0} - {1}", sha1, auto_managed);
}
public void set_file_priority(String sha1, int index, int priority) {
LOG.debugf("before set_file_priority: {0} - index: {1} - priority: {2}", sha1, index,
priority);
catchWrapperException(libTorrent.set_file_priority(sha1, index, priority));
LOG.debugf("after set_file_priority: {0} - index: {1} - priority: {2}", sha1, index,
priority);
}
public boolean has_metadata(String id) {
LOG.debugf("before has_metadata: {0}", id);
IntByReference has_metadata = new IntByReference(0);
catchWrapperException(libTorrent.has_metadata(id, has_metadata));
LOG.debugf("after has_metadata: {0}", id);
return has_metadata.getValue() != 0;
}
public boolean is_valid(String id) {
LOG.debugf("before is_valid: {0}", id);
IntByReference is_valid = new IntByReference(0);
catchWrapperException(libTorrent.is_valid(id, is_valid));
LOG.debugf("after is_valid: {0}", id);
return is_valid.getValue() != 0;
}
public TorrentInfo get_torrent_info(String id) {
LOG.debugf("before get_torrent_info: {0}", id);
LibTorrentInfo info = new LibTorrentInfo();
catchWrapperException(libTorrent.get_torrent_info(id, info));
free_torrent_info(info);
TorrentFileEntry[] files = get_files(id);
LOG.debugf("after get_torrent_info: {0}", id);
return new TorrentInfoImpl(info, files);
}
public void free_torrent_info(LibTorrentInfo info) {
LOG.debugf("before free_torrent_info: {0}", info);
catchWrapperException(libTorrent.free_torrent_info(info.getPointer()));
LOG.debugf("after free_torrent_info: {0}", info);
}
/**
* Saves the fast resume data for the given alert.
*/
public void save_fast_resume_data(LibTorrentAlert alert, String filePath) {
LOG.debugf("before save_fast_resume_data: {0} - {1}", alert, filePath);
catchWrapperException(libTorrent.save_fast_resume_data(alert, new WString(filePath)));
LOG.debugf("after save_fast_resume_data: {0} - {1}", alert, filePath);
}
public void set_peer_proxy(LibTorrentProxySetting proxySetting) {
LOG.debugf("before set_peer_proxy: {0}", proxySetting);
catchWrapperException(libTorrent.set_peer_proxy(proxySetting));
LOG.debugf("after set_peer_proxy: {0}", proxySetting);
}
public void set_dht_proxy(LibTorrentProxySetting proxySetting) {
LOG.debugf("before set_dht_proxy: {0}", proxySetting);
catchWrapperException(libTorrent.set_dht_proxy(proxySetting));
LOG.debugf("after set_dht_proxy: {0}", proxySetting);
}
public void set_tracker_proxy(LibTorrentProxySetting proxySetting) {
LOG.debugf("before set_tracker_proxy: {0}", proxySetting);
catchWrapperException(libTorrent.set_tracker_proxy(proxySetting));
LOG.debugf("after set_tracker_proxy: {0}", proxySetting);
}
public void set_web_seed_proxy(LibTorrentProxySetting proxySetting) {
LOG.debugf("before set_web_seed_proxy: {0}", proxySetting);
catchWrapperException(libTorrent.set_web_seed_proxy(proxySetting));
LOG.debugf("after set_web_seed_proxy: {0}", proxySetting);
}
public TorrentPiecesInfo get_pieces_status(String sha1) {
LibTorrentPiecesInfoContainer info = new LibTorrentPiecesInfoContainer();
LOG.debugf("before get_pieces_status: {0}", sha1);
catchWrapperException(libTorrent.get_pieces_status(sha1, info));
LOG.debugf("after get_pieces_status: {0}", sha1);
TorrentPiecesInfo exportInfo = new LibTorrentPiecesInfo(info);
LOG.debugf("before free_pieces_info: {0}", sha1);
catchWrapperException(libTorrent.free_pieces_info(info.getPointer()));
LOG.debugf("after free_pieces_info: {0}", sha1);
return exportInfo;
}
public void add_tracker(String sha1, String url, int tier) {
LOG.debugf("before add_tracker: {0}", sha1);
catchWrapperException(libTorrent.add_tracker(sha1, url, tier));
LOG.debugf("after add_tracker: {0}", sha1);
}
public void remove_tracker(String sha1, String url, int tier) {
LOG.debugf("before remove_tracker: {0}", sha1);
catchWrapperException(libTorrent.remove_tracker(sha1, url, tier));
LOG.debugf("after remove_tracker: {0}", sha1);
}
public LibTorrentAnnounceEntry[] get_trackers(String sha1) {
int numTrackers = get_num_trackers(sha1);
if (numTrackers == 0) {
return new LibTorrentAnnounceEntry[0];
}
LibTorrentAnnounceEntry[] torrentTrackers = new LibTorrentAnnounceEntry[numTrackers];
Pointer[] torrentTrackersPointers = new Pointer[numTrackers];
for ( int i=0 ; i < torrentTrackersPointers.length ; i++ ) {
LibTorrentAnnounceEntry torrentTracker = new LibTorrentAnnounceEntry();
torrentTrackers[i] = torrentTracker;
torrentTrackersPointers[i] = torrentTracker.getPointer();
}
LOG.debugf("before get_trackers: {0}", sha1);
catchWrapperException(libTorrent.get_trackers(sha1, torrentTrackersPointers, numTrackers));
LOG.debugf("after get_trackers: {0}", sha1);
for ( LibTorrentAnnounceEntry tracker : torrentTrackers ) {
tracker.read();
}
LOG.debugf("before free_trackers: {0}", sha1);
free_trackers(torrentTrackersPointers);
LOG.debugf("after free_trackers: {0}", sha1);
return torrentTrackers;
}
private int get_num_trackers(String sha1) {
IntByReference numTrackersReference = new IntByReference();
LOG.debugf("before get_num_trackers: {0}", sha1);
catchWrapperException(libTorrent.get_num_trackers(sha1, numTrackersReference));
LOG.debugf("after get_num_trackers: {0}", sha1);
return numTrackersReference.getValue();
}
private void free_trackers(Pointer[] torrentTrackerPointers) {
catchWrapperException(libTorrent.free_trackers(torrentTrackerPointers,
torrentTrackerPointers.length));
}
public void queue_tracker_scrape_request(String sha1String, String trackerUri, TrackerScrapeRequestCallback callback) {
//catchWrapperException(libTorrent.queue_tracker_scrape_request(sha1String, trackerUri, callback));
}
}