com.frostwire.gui.bittorrent.TorrentUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.frostwire.gui.bittorrent.TorrentUtil.java

Source

/*
 * Created on 9 Jul 2007
 * Created by Allan Crooks
 * Copyright (C) 2007 Aelitis, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * AELITIS, SAS au capital de 46,603.30 euros
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 */
/*
 * File    : ManagerUtils.java
 * Created : 7 dc. 2003}
 * By      : Olivier
 *
 * Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * 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 General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * AELITIS, SAS au capital de 46,603.30 euros,
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 */
package com.frostwire.gui.bittorrent;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfoSet;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.global.GlobalManagerDownloadRemovalVetoException;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLGroup;
import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLSet;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.AsyncDispatcher;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.UrlUtils;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.download.Download;
import org.gudy.azureus2.plugins.sharing.ShareManager;
import org.gudy.azureus2.plugins.sharing.ShareResource;
import org.gudy.azureus2.plugins.sharing.ShareResourceDir;
import org.gudy.azureus2.plugins.sharing.ShareResourceFile;
import org.gudy.azureus2.plugins.tracker.Tracker;
import org.gudy.azureus2.plugins.tracker.TrackerTorrent;
import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils;
import org.limewire.util.FileUtils;
import org.limewire.util.NetworkUtils;
import org.limewire.util.StringUtils;

import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.networkmanager.impl.tcp.TCPNetworkManager;
import com.aelitis.azureus.ui.UIFunctions;
import com.aelitis.azureus.ui.UIFunctionsManager;
import com.aelitis.azureus.ui.selectedcontent.SelectedContent;
import com.frostwire.AzureusStarter;
import com.limegroup.gnutella.settings.iTunesImportSettings;

public final class TorrentUtil {

    private static final Log LOG = LogFactory.getLog(TorrentUtil.class);

    private static AsyncDispatcher async = new AsyncDispatcher(2000);

    public static boolean shouldStopGroup(Object[] datasources) {
        DownloadManager[] dms = toDMS(datasources);
        if (dms.length == 0) {
            return true;
        }
        for (DownloadManager dm : dms) {
            int state = dm.getState();
            boolean stopped = state == DownloadManager.STATE_STOPPED || state == DownloadManager.STATE_STOPPING;
            if (!stopped) {
                return true;
            }
        }
        return false;
    }

    public static void stopOrStartDataSources(Object[] datasources) {
        DownloadManager[] dms = toDMS(datasources);
        if (dms.length == 0) {
            return;
        }
        boolean doStop = shouldStopGroup(dms);
        if (doStop) {
            stopDataSources(datasources);
        } else {
            queueDataSources(datasources, true);
        }
    }

    public static void stopDataSources(Object[] datasources) {
        DownloadManager[] dms = toDMS(datasources);
        for (DownloadManager dm : dms) {
            stop(dm);
        }
    }

    public static void queueDataSources(Object[] datasources, boolean startStoppedParents) {
        DownloadManager[] dms = toDMS(datasources);
        for (DownloadManager dm : dms) {
            queue(dm);
        }
    }

    public static void removeDownload(DownloadManager downloadManager, boolean deleteTorrent, boolean deleteData) {
        removeDownload(downloadManager, deleteTorrent, deleteData, true);
    }

    public static void removeDownload(DownloadManager downloadManager, boolean deleteTorrent, boolean deleteData,
            boolean async) {
        if (async) {
            asyncStopDelete(downloadManager, DownloadManager.STATE_STOPPED, deleteTorrent, deleteData, null);
        } else {
            blockingStopDelete(downloadManager, DownloadManager.STATE_STOPPED, deleteTorrent, deleteData, null);
        }
    }

    /** Deletes incomplete files and the save location from the itunes import settings */
    private static void finalCleanup(DownloadManager downloadManager) {
        Set<File> filesToDelete = getSkippedFiles(downloadManager);

        for (File f : filesToDelete) {
            try {
                if (isSkippedFileComplete(f, downloadManager)) {
                    //don't delete this one, it's probably downloaded from
                    //a previous session that got removed from the transfer manager.
                    continue;
                }
                if (f.exists() && !f.delete()) {
                    System.out.println("Can't delete file: " + f);
                }
            } catch (Exception e) {
                System.out.println("Can't delete file: " + f + ", ex: " + e.getMessage());
            }
        }
        FileUtils.deleteEmptyDirectoryRecursive(downloadManager.getSaveLocation());
        iTunesImportSettings.IMPORT_FILES.remove(downloadManager.getSaveLocation());
    }

    /**
     * Check if the given file, even though marked as skipped was downloaded in its entirety,
     * so that we don't delete it by accident during finalCleanup.
     * @param file
     * @param downloadManager
     * @return
     */
    private static boolean isSkippedFileComplete(File file, DownloadManager downloadManager) {
        DiskManagerFileInfoSet infoSet = downloadManager.getDiskManagerFileInfoSet();

        //search for the given file on the disk manager for this download manager
        for (DiskManagerFileInfo fileInfo : infoSet.getFiles()) {
            if (fileInfo.isSkipped()) {
                File f = fileInfo.getFile(false);

                //1. found the path of our file in here.
                if (f.getAbsolutePath().equalsIgnoreCase(file.getAbsolutePath())) {
                    return fileInfo.getLength() == file.length();
                }
            }
        }
        return false;
    }

    public static Set<File> getSkipedFiles() {
        Set<File> set = new HashSet<File>();
        if (AzureusStarter.isAzureusCoreStarted()) {
            List<?> dms = AzureusStarter.getAzureusCore().getGlobalManager().getDownloadManagers();
            for (Object obj : dms) {
                DownloadManager dm = (DownloadManager) obj;
                set.addAll(getSkippedFiles(dm));
            }
        }
        return set;
    }

    public static Set<File> getSkippedFiles(DownloadManager dm) {
        Set<File> set = new HashSet<File>();
        DiskManagerFileInfoSet infoSet = dm.getDiskManagerFileInfoSet();
        for (DiskManagerFileInfo fileInfo : infoSet.getFiles()) {
            try {
                if (fileInfo.isSkipped()) {
                    set.add(fileInfo.getFile(false));
                }
            } catch (Throwable e) {
                LOG.error("Error getting file information", e);
            }
        }
        return set;
    }

    public static Set<DiskManagerFileInfo> getNoSkippedFileInfoSet(DownloadManager dm) {
        Set<DiskManagerFileInfo> set = new HashSet<DiskManagerFileInfo>();
        DiskManagerFileInfoSet infoSet = dm.getDiskManagerFileInfoSet();
        for (DiskManagerFileInfo fileInfo : infoSet.getFiles()) {
            if (!fileInfo.isSkipped()) {
                set.add(fileInfo);
            }
        }
        return set;
    }

    public static Set<File> getIncompleteFiles() {
        Set<File> set = new HashSet<File>();

        if (AzureusStarter.isAzureusCoreStarted()) {
            List<?> dms = AzureusStarter.getAzureusCore().getGlobalManager().getDownloadManagers();
            for (Object obj : dms) {
                DownloadManager dm = (DownloadManager) obj;

                DiskManagerFileInfoSet infoSet = dm.getDiskManagerFileInfoSet();
                for (DiskManagerFileInfo fileInfo : infoSet.getFiles()) {
                    try {
                        if (getDownloadPercent(fileInfo) < 100) {
                            set.add(fileInfo.getFile(false));
                        }
                    } catch (Throwable e) {
                        LOG.error("Error getting file information", e);
                    }
                }
            }
        }

        return set;
    }

    public static int getDownloadPercent(DiskManagerFileInfo fileInfo) {
        long length = fileInfo.getLength();
        if (length == 0 || fileInfo.getDownloaded() == length) {
            return 100;
        } else {
            return (int) (fileInfo.getDownloaded() * 100 / length);
        }
    }

    public static boolean isStartable(DownloadManager dm) {
        if (dm == null)
            return false;
        int state = dm.getState();
        if (state != DownloadManager.STATE_STOPPED) {
            return false;
        }
        return true;
    }

    public static boolean isStopable(DownloadManager dm) {
        if (dm == null)
            return false;
        int state = dm.getState();
        if (state == DownloadManager.STATE_STOPPED || state == DownloadManager.STATE_STOPPING) {
            return false;
        }
        return true;
    }

    public static boolean isStopped(DownloadManager dm) {
        if (dm == null)
            return false;
        int state = dm.getState();
        if (state == DownloadManager.STATE_STOPPED || state == DownloadManager.STATE_ERROR) {
            return true;
        }
        return false;
    }

    public static boolean isForceStartable(DownloadManager dm) {
        if (dm == null) {
            return false;
        }

        int state = dm.getState();

        if (state != DownloadManager.STATE_STOPPED && state != DownloadManager.STATE_QUEUED
                && state != DownloadManager.STATE_SEEDING && state != DownloadManager.STATE_DOWNLOADING) {

            return (false);
        }

        return (true);
    }

    public static void asyncStopDelete(final DownloadManager dm, final int stateAfterStopped,
            final boolean bDeleteTorrent, final boolean bDeleteData, final AERunnable deleteFailed) {

        async.dispatch(new AERunnable() {
            public void runSupport() {

                try {
                    // I would move the FLAG_DO_NOT_DELETE_DATA_ON_REMOVE even deeper
                    // but I fear what could possibly go wrong.
                    boolean reallyDeleteData = bDeleteData
                            && !dm.getDownloadState().getFlag(Download.FLAG_DO_NOT_DELETE_DATA_ON_REMOVE);

                    dm.getGlobalManager().removeDownloadManager(dm, bDeleteTorrent, reallyDeleteData);
                } catch (GlobalManagerDownloadRemovalVetoException f) {

                    // see if we can delete a corresponding share as users frequently share
                    // stuff by mistake and then don't understand how to delete the share
                    // properly

                    try {
                        PluginInterface pi = AzureusCoreFactory.getSingleton().getPluginManager()
                                .getDefaultPluginInterface();

                        ShareManager sm = pi.getShareManager();

                        Tracker tracker = pi.getTracker();

                        ShareResource[] shares = sm.getShares();

                        TOTorrent torrent = dm.getTorrent();

                        byte[] target_hash = torrent.getHash();

                        for (ShareResource share : shares) {

                            int type = share.getType();

                            byte[] hash;

                            if (type == ShareResource.ST_DIR) {

                                hash = ((ShareResourceDir) share).getItem().getTorrent().getHash();

                            } else if (type == ShareResource.ST_FILE) {

                                hash = ((ShareResourceFile) share).getItem().getTorrent().getHash();

                            } else {

                                hash = null;
                            }

                            if (hash != null) {

                                if (Arrays.equals(target_hash, hash)) {

                                    try {
                                        dm.stopIt(DownloadManager.STATE_STOPPED, false, false);

                                    } catch (Throwable e) {
                                    }

                                    try {
                                        TrackerTorrent tracker_torrent = tracker
                                                .getTorrent(PluginCoreUtils.wrap(torrent));

                                        if (tracker_torrent != null) {

                                            tracker_torrent.stop();
                                        }
                                    } catch (Throwable e) {
                                    }

                                    share.delete();

                                    return;
                                }
                            }
                        }

                    } catch (Throwable e) {

                    }

                    if (!f.isSilent()) {
                        UIFunctionsManager.getUIFunctions().forceNotify(UIFunctions.STATUSICON_WARNING,
                                MessageText.getString("globalmanager.download.remove.veto"), f.getMessage(), null,
                                null, -1);

                        //Logger.log(new LogAlert(dm, false,
                        //      "{globalmanager.download.remove.veto}", f));
                    }
                    if (deleteFailed != null) {
                        deleteFailed.runSupport();
                    }
                } catch (Exception ex) {
                    Debug.printStackTrace(ex);
                    if (deleteFailed != null) {
                        deleteFailed.runSupport();
                    }
                }

                finalCleanup(dm);
            }
        });
    }

    public static void blockingStopDelete(final DownloadManager dm, final int stateAfterStopped,
            final boolean bDeleteTorrent, final boolean bDeleteData, final AERunnable deleteFailed) {

        try {
            // I would move the FLAG_DO_NOT_DELETE_DATA_ON_REMOVE even deeper
            // but I fear what could possibly go wrong.
            boolean reallyDeleteData = bDeleteData
                    && !dm.getDownloadState().getFlag(Download.FLAG_DO_NOT_DELETE_DATA_ON_REMOVE);

            dm.getGlobalManager().removeDownloadManager(dm, bDeleteTorrent, reallyDeleteData);
        } catch (GlobalManagerDownloadRemovalVetoException f) {

            // see if we can delete a corresponding share as users frequently
            // share
            // stuff by mistake and then don't understand how to delete the
            // share
            // properly

            try {
                PluginInterface pi = AzureusCoreFactory.getSingleton().getPluginManager()
                        .getDefaultPluginInterface();

                ShareManager sm = pi.getShareManager();

                Tracker tracker = pi.getTracker();

                ShareResource[] shares = sm.getShares();

                TOTorrent torrent = dm.getTorrent();

                byte[] target_hash = torrent.getHash();

                for (ShareResource share : shares) {

                    int type = share.getType();

                    byte[] hash;

                    if (type == ShareResource.ST_DIR) {

                        hash = ((ShareResourceDir) share).getItem().getTorrent().getHash();

                    } else if (type == ShareResource.ST_FILE) {

                        hash = ((ShareResourceFile) share).getItem().getTorrent().getHash();

                    } else {

                        hash = null;
                    }

                    if (hash != null) {

                        if (Arrays.equals(target_hash, hash)) {

                            try {
                                dm.stopIt(DownloadManager.STATE_STOPPED, false, false);

                            } catch (Throwable e) {
                            }

                            try {
                                TrackerTorrent tracker_torrent = tracker.getTorrent(PluginCoreUtils.wrap(torrent));

                                if (tracker_torrent != null) {

                                    tracker_torrent.stop();
                                }
                            } catch (Throwable e) {
                            }

                            share.delete();

                            return;
                        }
                    }
                }

            } catch (Throwable e) {

            }

            if (!f.isSilent()) {
                UIFunctionsManager.getUIFunctions().forceNotify(UIFunctions.STATUSICON_WARNING,
                        MessageText.getString("globalmanager.download.remove.veto"), f.getMessage(), null, null,
                        -1);

                // Logger.log(new LogAlert(dm, false,
                // "{globalmanager.download.remove.veto}", f));
            }
            if (deleteFailed != null) {
                deleteFailed.runSupport();
            }
        } catch (Exception ex) {
            Debug.printStackTrace(ex);
            if (deleteFailed != null) {
                deleteFailed.runSupport();
            }
        }

        finalCleanup(dm);
    }

    private static DownloadManager[] toDMS(Object[] objects) {
        int count = 0;
        DownloadManager[] result = new DownloadManager[objects.length];
        for (Object object : objects) {
            if (object instanceof DownloadManager) {
                DownloadManager dm = (DownloadManager) object;
                result[count++] = dm;
            } else if (object instanceof SelectedContent) {
                SelectedContent sc = (SelectedContent) object;
                if (sc.getFileIndex() == -1 && sc.getDownloadManager() != null) {
                    result[count++] = sc.getDownloadManager();
                }
            }
        }
        DownloadManager[] resultTrim = new DownloadManager[count];
        System.arraycopy(result, 0, resultTrim, 0, count);
        return resultTrim;
    }

    public static void stop(DownloadManager dm) {
        stop(dm, DownloadManager.STATE_STOPPED);
    }

    public static void stop(final DownloadManager dm, final int stateAfterStopped) {
        if (dm == null) {
            return;
        }

        int state = dm.getState();

        if (state == DownloadManager.STATE_STOPPED || state == DownloadManager.STATE_STOPPING
                || state == stateAfterStopped) {
            return;
        }

        asyncStop(dm, stateAfterStopped);
    }

    public static void asyncStop(final DownloadManager dm, final int stateAfterStopped) {
        async.dispatch(new AERunnable() {
            public void runSupport() {
                dm.stopIt(stateAfterStopped, false, false);
            }
        });
    }

    public static void queue(DownloadManager dm) {
        if (dm != null) {
            if (dm.getState() == DownloadManager.STATE_STOPPED) {

                dm.setStateQueued();

                /* parg - removed this - why would we want to effectively stop + restart
                 * torrents that are running? This is what happens if the code is left in.
                 * e.g. select two torrents, one stopped and one downloading, then hit "queue"
                     
                 }else if ( dm.getState() == DownloadManager.STATE_DOWNLOADING || 
                    dm.getState() == DownloadManager.STATE_SEEDING) {
                    
                stop(dm,panel,DownloadManager.STATE_QUEUED);
                */
            }
        }
    }

    public static void start(DownloadManager dm) {
        if (dm != null && dm.getState() == DownloadManager.STATE_STOPPED) {

            //dm.setStateWaiting();
            dm.initialize();
        }
    }

    public static String getMagnet(byte[] hash) {
        return "magnet:?xt=urn:btih:" + hashToString(hash);
    }

    public static String getMagnet(String hash) {
        return "magnet:?xt=urn:btih:" + hash;
    }

    public static String getMagnetURLParameters(TOTorrent torrent) {
        StringBuilder sb = new StringBuilder();
        //dn
        if (StringUtils.isNullOrEmpty(torrent.getUTF8Name())) {
            sb.append("dn=" + UrlUtils.encode(new String(torrent.getName())));
        } else {
            sb.append("dn=" + UrlUtils.encode(torrent.getUTF8Name()));
        }

        TOTorrentAnnounceURLGroup announceURLGroup = torrent.getAnnounceURLGroup();
        TOTorrentAnnounceURLSet[] announceURLSets = announceURLGroup.getAnnounceURLSets();

        for (TOTorrentAnnounceURLSet set : announceURLSets) {
            URL[] announceURLs = set.getAnnounceURLs();
            for (URL url : announceURLs) {
                sb.append("&tr=");
                sb.append(UrlUtils.encode(url.toString()));
            }
        }

        if (torrent.getAnnounceURL() != null) {
            sb.append("&tr=");
            sb.append(UrlUtils.encode(torrent.getAnnounceURL().toString()));
        }

        //iipp = internal ip port, for lan
        try {
            String localAddress = NetworkUtils.getLocalAddress().getHostAddress();
            int localPort = TCPNetworkManager.getSingleton().getTCPListeningPortNumber();

            if (localPort != -1) {
                sb.append("&iipp=");
                sb.append(NetworkUtils.convertIPPortToHex(localAddress, localPort));
            }

        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return sb.toString();
    }

    public static String hashToString(byte[] hash) {
        String hex = "";
        for (int i = 0; i < hash.length; i++) {
            String t = Integer.toHexString(hash[i] & 0xFF);
            if (t.length() < 2) {
                t = "0" + t;
            }
            hex += t;
        }

        return hex;
    }

    public static Set<File> getIgnorableFiles() {
        Set<File> set = TorrentUtil.getIncompleteFiles();
        set.addAll(TorrentUtil.getSkipedFiles());
        return set;
    }
}