/* * Created on 06-Jul-2004 * Created by Paul Gardner * Copyright (C) 2004, 2005, 2006 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. * */ package com.aelitis.azureus.plugins.removerules; /** * @author parg * */ import java.net.InetAddress; import java.util.*; import org.gudy.azureus2.plugins.torrent.*; import org.gudy.azureus2.plugins.download.*; import org.gudy.azureus2.plugins.logging.LoggerChannel; import org.gudy.azureus2.plugins.ui.config.*; import org.gudy.azureus2.plugins.ui.model.BasicPluginConfigModel; import org.gudy.azureus2.plugins.*; import org.gudy.azureus2.core3.download.DownloadManagerState; import org.gudy.azureus2.core3.util.*; public class DownloadRemoveRulesPlugin implements Plugin, DownloadManagerListener, HostNameToIPResolverListener { public static final int INITIAL_DELAY = 60*1000; public static final int DELAYED_REMOVAL_PERIOD = 60*1000; public static final int AELITIS_BIG_TORRENT_SEED_LIMIT = 10000; public static final int AELITIS_SMALL_TORRENT_SEED_LIMIT = 1000; public static final int MAX_SEED_TO_PEER_RATIO = 10; // 10 to 1 public static final String AELITIS_HOST_CORE = "aelitis.com"; // needs to be lowercase public static final String AELITIS_TRACKER = "tracker.aelitis.com"; // needs to be lowercase protected String aelitis_ip; protected PluginInterface plugin_interface; protected boolean closing; protected Map dm_listener_map = new HashMap(10); protected List monitored_downloads = new ArrayList(); protected LoggerChannel log; protected BooleanParameter remove_unauthorised; protected BooleanParameter remove_unauthorised_seeding_only; protected BooleanParameter remove_update_torrents; public void initialize( PluginInterface _plugin_interface ) { plugin_interface = _plugin_interface; HostNameToIPResolver.addResolverRequest( AELITIS_TRACKER, this ); plugin_interface.getPluginProperties().setProperty( "plugin.version", "1.0" ); plugin_interface.getPluginProperties().setProperty( "plugin.name", "Download Remove Rules" ); log = plugin_interface.getLogger().getChannel("DLRemRules"); BasicPluginConfigModel config = plugin_interface.getUIManager().createBasicPluginConfigModel( "torrents", "download.removerules.name" ); config.addLabelParameter2( "download.removerules.unauthorised.info" ); remove_unauthorised = config.addBooleanParameter2( "download.removerules.unauthorised", "download.removerules.unauthorised", false ); remove_unauthorised_seeding_only = config.addBooleanParameter2( "download.removerules.unauthorised.seedingonly", "download.removerules.unauthorised.seedingonly", true ); remove_unauthorised.addEnabledOnSelection( remove_unauthorised_seeding_only ); remove_update_torrents = config.addBooleanParameter2( "download.removerules.updatetorrents", "download.removerules.updatetorrents", true ); new DelayedEvent( "DownloadRemovalRules", INITIAL_DELAY, new AERunnable() { public void runSupport() { plugin_interface.getDownloadManager().addListener( DownloadRemoveRulesPlugin.this ); } }); } public void hostNameResolutionComplete( InetAddress address ) { // resolution will fail if disconnected from net if ( address != null ){ aelitis_ip = address.getHostAddress(); } } public void downloadAdded( final Download download ) { // we don't auto-remove non-persistent downloads as these are managed // elsewhere (e.g. shares) if ( !download.isPersistent()){ return; } DownloadTrackerListener listener = new DownloadTrackerListener() { public void scrapeResult( DownloadScrapeResult response ) { if ( closing ){ return; } handleScrape( download, response ); } public void announceResult( DownloadAnnounceResult response ) { if ( closing ){ return; } handleAnnounce( download, response ); } }; monitored_downloads.add( download ); dm_listener_map.put( download, listener ); download.addTrackerListener( listener ); } protected void handleScrape( Download download, DownloadScrapeResult response ) { String status = response.getStatus(); if ( status == null ){ status = ""; } handleAnnounceScrapeStatus( download, status ); } protected void handleAnnounce( Download download, DownloadAnnounceResult response ) { String reason = ""; if ( response.getResponseType() == DownloadAnnounceResult.RT_ERROR ){ reason = response.getError(); if ( reason == null ){ reason = ""; } } handleAnnounceScrapeStatus( download, reason ); } protected void handleAnnounceScrapeStatus( Download download, String status ) { if ( !monitored_downloads.contains( download )){ return; } status = status.toLowerCase(); boolean download_completed = download.isComplete(); if ( status.indexOf( "not authori" ) != -1 || status.toLowerCase().indexOf( "unauthori" ) != -1 ){ if ( remove_unauthorised.getValue() && ( (!remove_unauthorised_seeding_only.getValue()) || download_completed )){ log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, "Download '" + download.getName() + "' is unauthorised and removal triggered"); removeDownload( download ); return; } } Torrent torrent = download.getTorrent(); if ( torrent != null && torrent.getAnnounceURL() != null ){ String url_string = torrent.getAnnounceURL().toString().toLowerCase(); if ( url_string.indexOf( AELITIS_HOST_CORE ) != -1 || ( aelitis_ip != null && url_string.indexOf( aelitis_ip ) != -1 )){ // emergency instruction from tracker if ( ( download_completed && status.indexOf( "too many seeds" ) != -1 ) || status.indexOf( "too many peers" ) != -1 ){ log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, "Download '" + download.getName() + "' being removed on instruction from the tracker"); removeDownloadDelayed( download ); }else if ( download_completed && remove_update_torrents.getValue()){ long seeds = download.getLastScrapeResult().getSeedCount(); long peers = download.getLastScrapeResult().getNonSeedCount(); // try to maintain an upper bound on seeds that isn't going to // kill the tracker if ( seeds / ( peers==0?1:peers ) > MAX_SEED_TO_PEER_RATIO ){ log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, "Download '" + download.getName() + "' being removed to reduce swarm size"); removeDownloadDelayed( download ); }else{ long creation_time = download.getCreationTime(); long running_mins = ( SystemTime.getCurrentTime() - creation_time )/(60*1000); if ( running_mins > 15 ){ // big is a relative term here and generally distinguishes between core updates // and plugin updates boolean big_torrent = torrent.getSize() > 1024*1024; if ( ( seeds > AELITIS_BIG_TORRENT_SEED_LIMIT && big_torrent ) || ( seeds > AELITIS_SMALL_TORRENT_SEED_LIMIT && !big_torrent )){ log.log( "Download '" + download.getName() + "' being removed to reduce swarm size" ); removeDownloadDelayed( download ); } } } } } } } protected void removeDownloadDelayed( final Download download ) { monitored_downloads.remove( download ); // we need to delay this because other actions may be being performed // on the download (e.g. completion may trigger update install) plugin_interface.getUtilities().createThread( "delayedRemoval", new AERunnable() { public void runSupport() { try{ Thread.sleep(DELAYED_REMOVAL_PERIOD); removeDownload( download ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } }); } protected void removeDownload( final Download download ) { monitored_downloads.remove( download ); if ( download.getState() == Download.ST_STOPPED ){ try{ download.remove(); }catch( Throwable e ){ log.logAlert( "Automatic removal of download '" + download.getName() + "' failed", e ); } }else{ download.addListener( new DownloadListener() { public void stateChanged( Download download, int old_state, int new_state ) { log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, "download state changed to '" + new_state + "'"); if ( new_state == Download.ST_STOPPED ){ try{ download.remove(); String msg = plugin_interface.getUtilities().getLocaleUtilities().getLocalisedMessageText( "download.removerules.removed.ok", new String[] { download.getName() }); if (download.getFlag(Download.FLAG_LOW_NOISE)) { log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, msg); } else { log.logAlert(LoggerChannel.LT_INFORMATION, msg); } }catch( Throwable e ){ log.logAlert( "Automatic removal of download '" + download.getName() + "' failed", e ); } } } public void positionChanged( Download download, int oldPosition, int newPosition ) { } }); try{ download.stop(); }catch( DownloadException e ){ log.logAlert( "Automatic removal of download '" + download.getName() + "' failed", e ); } } } public void downloadRemoved( Download download ) { monitored_downloads.remove( download ); DownloadTrackerListener listener = (DownloadTrackerListener)dm_listener_map.remove(download); if ( listener != null ){ download.removeTrackerListener( listener ); } } public void destroyInitiated() { closing = true; } public void destroyed() { } }