1 /*
2  * File    : DownloadImpl.java
3  * Created : 06-Jan-2004
4  * By      : parg
5  *
6  * Azureus - a Java Bittorrent client
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details ( see the LICENSE file ).
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 package org.gudy.azureus2.pluginsimpl.local.download;
24 
25 /**
26  * @author parg
27  *
28  */
29 
30 import java.io.File;
31 import java.net.URL;
32 import java.util.*;
33 
34 import org.gudy.azureus2.core3.category.Category;
35 import org.gudy.azureus2.core3.category.CategoryManager;
36 import org.gudy.azureus2.core3.download.*;
37 import org.gudy.azureus2.core3.download.DownloadManager;
38 import org.gudy.azureus2.core3.download.DownloadManagerListener;
39 import org.gudy.azureus2.core3.download.impl.DownloadManagerMoveHandler;
40 import org.gudy.azureus2.core3.global.GlobalManager;
41 import org.gudy.azureus2.core3.global.GlobalManagerDownloadRemovalVetoException;
42 import org.gudy.azureus2.core3.internat.MessageText;
43 import org.gudy.azureus2.core3.logging.LogRelation;
44 import org.gudy.azureus2.core3.peer.PEPeer;
45 import org.gudy.azureus2.core3.peer.PEPeerManager;
46 import org.gudy.azureus2.core3.peer.PEPeerSource;
47 import org.gudy.azureus2.core3.torrent.TOTorrent;
48 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
49 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
50 import org.gudy.azureus2.core3.tracker.client.TRTrackerScraperResponse;
51 import org.gudy.azureus2.core3.util.AEMonitor;
52 import org.gudy.azureus2.core3.util.BEncoder;
53 import org.gudy.azureus2.core3.util.Debug;
54 import org.gudy.azureus2.core3.util.SystemTime;
55 import org.gudy.azureus2.core3.util.TorrentUtils;
56 import org.gudy.azureus2.plugins.ddb.DistributedDatabase;
57 import org.gudy.azureus2.plugins.disk.DiskManager;
58 import org.gudy.azureus2.plugins.disk.DiskManagerFileInfo;
59 import org.gudy.azureus2.plugins.download.*;
60 import org.gudy.azureus2.plugins.download.savelocation.SaveLocationChange;
61 import org.gudy.azureus2.plugins.network.RateLimiter;
62 import org.gudy.azureus2.plugins.peers.PeerManager;
63 import org.gudy.azureus2.plugins.tag.Tag;
64 import org.gudy.azureus2.plugins.torrent.Torrent;
65 import org.gudy.azureus2.plugins.torrent.TorrentAttribute;
66 import org.gudy.azureus2.pluginsimpl.local.ddb.DDBaseImpl;
67 import org.gudy.azureus2.pluginsimpl.local.deprecate.PluginDeprecation;
68 import org.gudy.azureus2.pluginsimpl.local.disk.DiskManagerFileInfoImpl;
69 import org.gudy.azureus2.pluginsimpl.local.peers.PeerManagerImpl;
70 import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentImpl;
71 import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentManagerImpl;
72 import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl;
73 
74 import com.aelitis.azureus.core.peermanager.messaging.bittorrent.BTHandshake;
75 import com.aelitis.azureus.core.tag.TagManagerFactory;
76 import com.aelitis.azureus.core.tracker.TrackerPeerSource;
77 import com.aelitis.azureus.core.tracker.TrackerPeerSourceAdapter;
78 import com.aelitis.azureus.core.util.CopyOnWriteList;
79 import com.aelitis.azureus.core.util.CopyOnWriteMap;
80 
81 public class
82 DownloadImpl
83 	extends LogRelation
84 	implements 	Download, DownloadManagerListener,
85 				DownloadManagerTrackerListener,
86 				DownloadManagerStateListener, DownloadManagerActivationListener,
87 				DownloadManagerStateAttributeListener
88 {
89 	private final DownloadManagerImpl		manager;
90 	private final DownloadManager			download_manager;
91 	private final DownloadStatsImpl			download_stats;
92 
93 	private int			latest_state		= ST_STOPPED;
94 	private boolean 	latest_forcedStart;
95 
96 	private DownloadAnnounceResultImpl		last_announce_result 	= new DownloadAnnounceResultImpl(this,null);
97 	private DownloadScrapeResultImpl		last_scrape_result		= new DownloadScrapeResultImpl( this, null );
98 	private AggregateScrapeResult			last_aggregate_scrape	= new AggregateScrapeResult( this );
99 
100     private TorrentImpl torrent = null;
101 
102 	private List		listeners 				= new ArrayList();
103 	private AEMonitor	listeners_mon			= new AEMonitor( "Download:L");
104 	private List		property_listeners		= new ArrayList();
105 	private List		tracker_listeners		= new ArrayList();
106 	private AEMonitor	tracker_listeners_mon	= new AEMonitor( "Download:TL");
107 	private List		removal_listeners 		= new ArrayList();
108 	private AEMonitor	removal_listeners_mon	= new AEMonitor( "Download:RL");
109 	private Map			peer_listeners			= new HashMap();
110 	private AEMonitor	peer_listeners_mon		= new AEMonitor( "Download:PL");
111 
112 	private CopyOnWriteList completion_listeners     = new CopyOnWriteList();
113 
114 	private CopyOnWriteMap read_attribute_listeners_map_cow  = new CopyOnWriteMap();
115 	private CopyOnWriteMap write_attribute_listeners_map_cow = new CopyOnWriteMap();
116 
117 	private CopyOnWriteList	activation_listeners = new CopyOnWriteList();
118 	private DownloadActivationEvent	activation_state;
119 
120 
121 	private Map<String,int[]>	announce_response_map;
122 
123 	protected
DownloadImpl( DownloadManagerImpl _manager, DownloadManager _dm )124 	DownloadImpl(
125 		DownloadManagerImpl	_manager,
126 		DownloadManager		_dm )
127 	{
128 		manager				= _manager;
129 		download_manager	= _dm;
130 		download_stats		= new DownloadStatsImpl( download_manager );
131 
132 		activation_state =
133 			new DownloadActivationEvent()
134 			{
135 				public Download
136 				getDownload()
137 				{
138 					return( DownloadImpl.this );
139 				}
140 
141 				public int
142 				getActivationCount()
143 				{
144 					return( download_manager.getActivationCount());
145 				}
146 			};
147 
148 		download_manager.addListener( this );
149 
150 		latest_forcedStart = download_manager.isForceStart();
151 	}
152 
153 	// Not available to plugins
154 	public DownloadManager
getDownload()155 	getDownload()
156 	{
157 		return( download_manager );
158 	}
159 
160 	public int
getState()161 	getState()
162 	{
163 		return( convertState( download_manager.getState()) );
164 	}
165 
166 	public int
getSubState()167 	getSubState()
168 	{
169 		int	state = getState();
170 
171 		if ( state == ST_STOPPING ){
172 
173 			int	substate = download_manager.getSubState();
174 
175 			if ( substate == DownloadManager.STATE_QUEUED ){
176 
177 				return( ST_QUEUED );
178 
179 			}else if ( substate == DownloadManager.STATE_STOPPED ){
180 
181 				return( ST_STOPPED );
182 
183 			}else if ( substate == DownloadManager.STATE_ERROR ){
184 
185 				return( ST_ERROR );
186 			}
187 		}
188 
189 		return( state );
190 	}
191 
192 	protected int
convertState( int dm_state )193 	convertState(
194 		int		dm_state )
195 	{
196 		// dm states: waiting -> initialising -> initialized ->
197 		//		disk states: allocating -> checking -> ready ->
198 		// dm states: downloading -> finishing -> seeding -> stopping -> stopped
199 
200 		// "initialize" call takes from waiting -> initialising -> waiting (no port) or initialized (ok)
201 		// if initialized then disk manager runs through to ready
202 		// "startdownload" takes ready -> dl etc.
203 		// "stopIt" takes to stopped which is equiv to ready
204 
205 		int	our_state;
206 
207 		switch( dm_state ){
208 			case DownloadManager.STATE_WAITING:
209 			{
210 				our_state	= ST_WAITING;
211 
212 				break;
213 			}
214 			case DownloadManager.STATE_INITIALIZING:
215 			case DownloadManager.STATE_INITIALIZED:
216 			case DownloadManager.STATE_ALLOCATING:
217 			case DownloadManager.STATE_CHECKING:
218 			{
219 				our_state	= ST_PREPARING;
220 
221 				break;
222 			}
223 			case DownloadManager.STATE_READY:
224 			{
225 				our_state	= ST_READY;
226 
227 				break;
228 			}
229 			case DownloadManager.STATE_DOWNLOADING:
230 			case DownloadManager.STATE_FINISHING:		// finishing download - transit to seeding
231 			{
232 				our_state	= ST_DOWNLOADING;
233 
234 				break;
235 			}
236 			case DownloadManager.STATE_SEEDING:
237 			{
238 				our_state	= ST_SEEDING;
239 
240 				break;
241 			}
242 			case DownloadManager.STATE_STOPPING:
243 			{
244 				our_state	= ST_STOPPING;
245 
246 				break;
247 			}
248 			case DownloadManager.STATE_STOPPED:
249 			{
250 				our_state	= ST_STOPPED;
251 
252 				break;
253 			}
254 			case DownloadManager.STATE_QUEUED:
255 			{
256 				our_state	= ST_QUEUED;
257 
258 				break;
259 			}
260 			case DownloadManager.STATE_ERROR:
261 			{
262 				our_state	= ST_ERROR;
263 
264 				break;
265 			}
266 			default:
267 			{
268 				our_state	= ST_ERROR;
269 			}
270 		}
271 
272 		return( our_state );
273 	}
274 
275 	public String
getErrorStateDetails()276 	getErrorStateDetails()
277 	{
278 		return( download_manager.getErrorDetails());
279 	}
280 
281 	public long
getFlags()282 	getFlags()
283 	{
284 		return( download_manager.getDownloadState().getFlags());
285 	}
286 
287 	public boolean
getFlag( long flag )288 	getFlag(
289 		long		flag )
290 	{
291 		return( download_manager.getDownloadState().getFlag( flag ));
292 	}
293 
setFlag(long flag, boolean set)294 	public void setFlag(long flag, boolean set) {
295 		download_manager.getDownloadState().setFlag(flag, set);
296 	}
297 
298 	public int
getIndex()299 	getIndex()
300 	{
301 		GlobalManager globalManager = download_manager.getGlobalManager();
302 		return globalManager.getIndexOf(download_manager);
303 	}
304 
305     public Torrent
getTorrent()306     getTorrent()
307     {
308     	if (this.torrent != null) {return this.torrent;}
309 
310         TOTorrent torrent = download_manager.getTorrent();
311         if (torrent == null) {return null;}
312         this.torrent = new TorrentImpl(torrent);
313         return this.torrent;
314     }
315 
316 	public void
initialize()317 	initialize()
318 
319 		throws DownloadException
320 	{
321 		int	state = download_manager.getState();
322 
323 		if ( state == DownloadManager.STATE_WAITING ){
324 
325 			download_manager.initialize();
326 
327 		}else{
328 
329 			throw( new DownloadException( "Download::initialize: download not waiting (state=" + state + ")" ));
330 		}
331 	}
332 
333 	public void
start()334 	start()
335 
336 		throws DownloadException
337 	{
338 		int	state = download_manager.getState();
339 
340 		if ( state == DownloadManager.STATE_READY ){
341 
342 			download_manager.startDownload();
343 
344 		}else{
345 
346 			throw( new DownloadException( "Download::start: download not ready (state=" + state + ")" ));
347 		}
348 	}
349 
350 	public void
restart()351 	restart()
352 
353 		throws DownloadException
354 	{
355 		int	state = download_manager.getState();
356 
357 		if ( 	state == DownloadManager.STATE_STOPPED ||
358 				state == DownloadManager.STATE_QUEUED ){
359 
360 			download_manager.setStateWaiting();
361 
362 		}else{
363 
364 			throw( new DownloadException( "Download::restart: download already running (state=" + state + ")" ));
365 		}
366 	}
367 
368 	public void
stop()369 	stop()
370 
371 		throws DownloadException
372 	{
373 		if ( download_manager.getState() != DownloadManager.STATE_STOPPED){
374 
375 			download_manager.stopIt( DownloadManager.STATE_STOPPED, false, false );
376 
377 		}else{
378 
379 			throw( new DownloadException( "Download::stop: download already stopped" ));
380 		}
381 	}
382 
383 	public void
stopAndQueue()384 	stopAndQueue()
385 
386 		throws DownloadException
387 	{
388 		if ( download_manager.getState() != DownloadManager.STATE_QUEUED){
389 
390 			download_manager.stopIt( DownloadManager.STATE_QUEUED, false, false );
391 
392 		}else{
393 
394 			throw( new DownloadException( "Download::stopAndQueue: download already queued" ));
395 		}
396 	}
397 
398 	public void
recheckData()399 	recheckData()
400 
401 		throws DownloadException
402 	{
403 		if ( !download_manager.canForceRecheck()){
404 
405 			throw( new DownloadException( "Download::recheckData: download must be stopped, queued or in error state" ));
406 		}
407 
408 		download_manager.forceRecheck();
409 	}
410 
411 	public boolean
isStartStopLocked()412 	isStartStopLocked()
413 	{
414 		return( download_manager.getState() == DownloadManager.STATE_STOPPED );
415 	}
416 
417 	public boolean
isForceStart()418 	isForceStart()
419 	{
420 		return download_manager.isForceStart();
421 	}
422 
423 	public void
setForceStart(boolean forceStart)424 	setForceStart(boolean forceStart)
425 	{
426 		download_manager.setForceStart(forceStart);
427 	}
428 
429 	public boolean
isPaused()430 	isPaused()
431 	{
432 		return( download_manager.isPaused());
433 	}
434 
435 	public void
pause()436 	pause()
437 	{
438 		download_manager.pause();
439 	}
440 
441 	public void
resume()442 	resume()
443 	{
444 		download_manager.resume();
445 	}
446 
447 	public int
getPosition()448 	getPosition()
449 	{
450 		return download_manager.getPosition();
451 	}
452 
453 	public long
getCreationTime()454 	getCreationTime()
455 	{
456 		return( download_manager.getCreationTime());
457 	}
458 
459 	public void
setPosition(int newPosition)460 	setPosition(int newPosition)
461 	{
462 		download_manager.setPosition(newPosition);
463 	}
464 
465 	public void
moveUp()466 	moveUp()
467 	{
468 		download_manager.getGlobalManager().moveUp(download_manager);
469 	}
470 
471 	public void
moveDown()472 	moveDown()
473 	{
474 		download_manager.getGlobalManager().moveDown(download_manager);
475 	}
476 
477 	public void
moveTo( int pos )478 	moveTo(
479 		int	pos )
480 	{
481 		download_manager.getGlobalManager().moveTo( download_manager, pos );
482 	}
483 
484 	public String
getName()485 	getName()
486 	{
487 		return download_manager.getDisplayName();
488 	}
489 
getTorrentFileName()490   public String getTorrentFileName() {
491     return download_manager.getTorrentFileName();
492   }
493 
getCategoryName()494   public String getCategoryName() {
495     Category category = download_manager.getDownloadState().getCategory();
496     if (category == null)
497       category = CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED);
498 
499     if (category == null)
500       return null;
501     return category.getName();
502   }
503 
getTags()504   public List<Tag> getTags() {
505 	  return( new ArrayList<Tag>( TagManagerFactory.getTagManager().getTagsForTaggable( download_manager )));
506   }
507 
508   public String
getAttribute( TorrentAttribute attribute )509   getAttribute(
510   	TorrentAttribute		attribute )
511   {
512   	String	name = convertAttribute( attribute );
513 
514   	if ( name != null ){
515 
516   		return( download_manager.getDownloadState().getAttribute( name ));
517   	}
518 
519   	return( null );
520   }
521 
522   public String[]
getListAttribute( TorrentAttribute attribute )523   getListAttribute(
524   	TorrentAttribute		attribute )
525   {
526 	  	String	name = convertAttribute( attribute );
527 
528 	  	if ( name != null ){
529 
530 	  		return( download_manager.getDownloadState().getListAttribute( name ));
531 	  	}
532 
533 	  	return( null );
534   }
535 
536   public void
setListAttribute( TorrentAttribute attribute, String[] value)537   setListAttribute(
538 	TorrentAttribute attribute,
539 	String[] value)
540   {
541 	  String name = convertAttribute(attribute);
542 
543 	  if (name != null) {
544 		  download_manager.getDownloadState().setListAttribute(name, value);
545 	  }
546   }
547 
548   public void
setMapAttribute( TorrentAttribute attribute, Map value )549   setMapAttribute(
550 	TorrentAttribute		attribute,
551 	Map						value )
552   {
553 	  	String	name = convertAttribute( attribute );
554 
555 	  	if ( name != null ){
556 
557 	  			// gotta clone before updating in case user has read values and then just
558 	  			// updated them - setter code optimises out sets of the same values...
559 
560 			download_manager.getDownloadState().setMapAttribute( name, BEncoder.cloneMap( value ));
561 	  	}
562   }
563 
564   public Map
getMapAttribute( TorrentAttribute attribute )565   getMapAttribute(
566 	TorrentAttribute		attribute )
567   {
568 	  	String	name = convertAttribute( attribute );
569 
570 	  	if ( name != null ){
571 
572 	  		return( download_manager.getDownloadState().getMapAttribute( name ));
573 	  	}
574 
575 	  	return( null );
576   }
577 
578   public void
setAttribute( TorrentAttribute attribute, String value )579   setAttribute(
580   	TorrentAttribute		attribute,
581 	String					value )
582   {
583  	String	name = convertAttribute( attribute );
584 
585   	if ( name != null ){
586 
587   		download_manager.getDownloadState().setAttribute( name, value );
588   	}
589   }
590 
hasAttribute(TorrentAttribute attribute)591   public boolean hasAttribute(TorrentAttribute attribute) {
592 	  String name = convertAttribute(attribute);
593 	  if (name == null) {return false;}
594 	  return download_manager.getDownloadState().hasAttribute(name);
595   }
596 
getBooleanAttribute(TorrentAttribute attribute)597   public boolean getBooleanAttribute(TorrentAttribute attribute) {
598 	  String name = convertAttribute(attribute);
599 	  if (name == null) {return false;} // Default value
600 	  return download_manager.getDownloadState().getBooleanAttribute(name);
601   }
602 
setBooleanAttribute(TorrentAttribute attribute, boolean value)603   public void setBooleanAttribute(TorrentAttribute attribute, boolean value) {
604 	  String name = convertAttribute(attribute);
605 	  if (name != null) {
606 		  download_manager.getDownloadState().setBooleanAttribute(name, value);
607 	  }
608   }
609 
getIntAttribute(TorrentAttribute attribute)610   public int getIntAttribute(TorrentAttribute attribute) {
611 	  String name = convertAttribute(attribute);
612 	  if (name == null) {return 0;} // Default value
613 	  return download_manager.getDownloadState().getIntAttribute(name);
614   }
615 
setIntAttribute(TorrentAttribute attribute, int value)616   public void setIntAttribute(TorrentAttribute attribute, int value) {
617 	  String name = convertAttribute(attribute);
618 	  if (name != null) {
619 		  download_manager.getDownloadState().setIntAttribute(name, value);
620 	  }
621   }
622 
getLongAttribute(TorrentAttribute attribute)623   public long getLongAttribute(TorrentAttribute attribute) {
624 	  String name = convertAttribute(attribute);
625 	  if (name == null) {return 0L;} // Default value
626 	  return download_manager.getDownloadState().getLongAttribute(name);
627   }
628 
setLongAttribute(TorrentAttribute attribute, long value)629   public void setLongAttribute(TorrentAttribute attribute, long value) {
630 	  String name = convertAttribute(attribute);
631 	  if (name != null) {
632 		  download_manager.getDownloadState().setLongAttribute(name, value);
633 	  }
634   }
635 
636   protected String
convertAttribute( TorrentAttribute attribute )637   convertAttribute(
638   	TorrentAttribute		attribute )
639   {
640  	if ( attribute.getName() == TorrentAttribute.TA_CATEGORY ){
641 
642   		return( DownloadManagerState.AT_CATEGORY );
643 
644  	}else if ( attribute.getName() == TorrentAttribute.TA_NETWORKS ){
645 
646 		return( DownloadManagerState.AT_NETWORKS );
647 
648  	}else if ( attribute.getName() == TorrentAttribute.TA_TRACKER_CLIENT_EXTENSIONS ){
649 
650 		return( DownloadManagerState.AT_TRACKER_CLIENT_EXTENSIONS );
651 
652 	}else if ( attribute.getName() == TorrentAttribute.TA_PEER_SOURCES ){
653 
654 		return( DownloadManagerState.AT_PEER_SOURCES );
655 
656 	}else if ( attribute.getName() == TorrentAttribute.TA_DISPLAY_NAME ){
657 
658 		return( DownloadManagerState.AT_DISPLAY_NAME );
659 
660 	}else if ( attribute.getName() == TorrentAttribute.TA_USER_COMMENT ){
661 
662 		return( DownloadManagerState.AT_USER_COMMENT );
663 
664 	}else if ( attribute.getName() == TorrentAttribute.TA_RELATIVE_SAVE_PATH ){
665 
666 		return( DownloadManagerState.AT_RELATIVE_SAVE_PATH );
667 
668 	}else if ( attribute.getName() == TorrentAttribute.TA_SHARE_PROPERTIES ){
669 
670 			// this is a share-level attribute only, not propagated to individual downloads
671 
672 		return( null );
673 
674 	}else if ( attribute.getName().startsWith( "Plugin." )){
675 
676 		return( attribute.getName());
677 
678   	}else{
679 
680   		Debug.out( "Can't convert attribute '" + attribute.getName() + "'" );
681 
682   		return( null );
683   	}
684   }
685 
686   protected TorrentAttribute
convertAttribute( String name )687   convertAttribute(
688   	String			name )
689   {
690  	if ( name.equals( DownloadManagerState.AT_CATEGORY )){
691 
692   		return( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_CATEGORY ));
693 
694 	}else if ( name.equals( DownloadManagerState.AT_NETWORKS )){
695 
696 	  	return( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_NETWORKS ));
697 
698 	}else if ( name.equals( DownloadManagerState.AT_PEER_SOURCES )){
699 
700 		return( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_PEER_SOURCES ));
701 
702 	}else if ( name.equals( DownloadManagerState.AT_TRACKER_CLIENT_EXTENSIONS )){
703 
704 		return( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_TRACKER_CLIENT_EXTENSIONS ));
705 
706 	}else if ( name.equals ( DownloadManagerState.AT_DISPLAY_NAME)){
707 
708 		return ( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_DISPLAY_NAME ));
709 
710 	}else if ( name.equals ( DownloadManagerState.AT_USER_COMMENT)){
711 
712 		return ( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_USER_COMMENT ));
713 
714 	}else if ( name.equals ( DownloadManagerState.AT_RELATIVE_SAVE_PATH)){
715 
716 		return ( TorrentManagerImpl.getSingleton().getAttribute( TorrentAttribute.TA_RELATIVE_SAVE_PATH ));
717 
718 	}else if ( name.startsWith( "Plugin." )){
719 
720 		return( TorrentManagerImpl.getSingleton().getAttribute( name ));
721 
722   	}else{
723 
724   		return( null );
725   	}
726   }
727 
setCategory(String sName)728   public void setCategory(String sName) {
729     Category category = CategoryManager.getCategory(sName);
730     if (category == null)
731       category = CategoryManager.createCategory(sName);
732     download_manager.getDownloadState().setCategory(category);
733   }
734 
isPersistent()735   public boolean isPersistent() {
736     return download_manager.isPersistent();
737   }
738 
739 	public void
remove()740 	remove()
741 
742 		throws DownloadException, DownloadRemovalVetoException
743 	{
744 		remove( false, false );
745 	}
746 
747 	public void
remove( boolean delete_torrent, boolean delete_data )748 	remove(
749 		boolean	delete_torrent,
750 		boolean	delete_data )
751 
752 		throws DownloadException, DownloadRemovalVetoException
753 	{
754 		int	dl_state = download_manager.getState();
755 
756 		if ( 	dl_state == DownloadManager.STATE_STOPPED 	||
757 				dl_state == DownloadManager.STATE_ERROR 	||
758 				dl_state == DownloadManager.STATE_QUEUED ){
759 
760 			GlobalManager globalManager = download_manager.getGlobalManager();
761 
762 			try{
763 
764 				globalManager.removeDownloadManager(download_manager, delete_torrent, delete_data);
765 
766 			}catch( GlobalManagerDownloadRemovalVetoException e ){
767 
768 				throw( new DownloadRemovalVetoException( e.getMessage()));
769 			}
770 
771 		}else{
772 
773 			throw( new DownloadRemovalVetoException( MessageText.getString("plugin.download.remove.veto.notstopped")));
774 		}
775 	}
776 
777 	public boolean
canBeRemoved()778 	canBeRemoved()
779 
780 		throws DownloadRemovalVetoException
781 	{
782 		int	dl_state = download_manager.getState();
783 
784 		if ( 	dl_state == DownloadManager.STATE_STOPPED 	||
785 				dl_state == DownloadManager.STATE_ERROR 	||
786 				dl_state == DownloadManager.STATE_QUEUED ){
787 
788 			GlobalManager globalManager = download_manager.getGlobalManager();
789 
790 			try{
791 				globalManager.canDownloadManagerBeRemoved(download_manager, false, false);
792 
793 			}catch( GlobalManagerDownloadRemovalVetoException e ){
794 
795 				throw( new DownloadRemovalVetoException( e.getMessage(),e.isSilent()));
796 			}
797 
798 		}else{
799 
800 			throw( new DownloadRemovalVetoException( MessageText.getString("plugin.download.remove.veto.notstopped")));
801 		}
802 
803 		return( true );
804 	}
805 
806 	public DownloadStats
getStats()807 	getStats()
808 	{
809 		return( download_stats );
810 	}
811 
812  	public boolean
isComplete()813 	isComplete()
814  	{
815  		return download_manager.isDownloadComplete(false);
816  	}
817 
isComplete(boolean bIncludeDND)818  	public boolean isComplete(boolean bIncludeDND) {
819  		return download_manager.isDownloadComplete(bIncludeDND);
820  	}
821 
822  	public boolean
isChecking()823  	isChecking()
824  	{
825  		return( download_stats.getCheckingDoneInThousandNotation() != -1 );
826  	}
827 
828  	public boolean
isMoving()829  	isMoving()
830  	{
831  		org.gudy.azureus2.core3.disk.DiskManager dm = download_manager.getDiskManager();
832 
833  		if ( dm != null ){
834 
835  			return( dm.getMoveProgress() != -1 );
836  		}
837 
838  		return( false );
839  	}
840 
841 	protected void
isRemovable()842 	isRemovable()
843 		throws DownloadRemovalVetoException
844 	{
845 			// no sync required, see update code
846 
847 		for (int i=0;i<removal_listeners.size();i++){
848 
849 			try{
850 				((DownloadWillBeRemovedListener)removal_listeners.get(i)).downloadWillBeRemoved(this);
851 
852 			}catch( DownloadRemovalVetoException e ){
853 
854 				throw( e );
855 
856 			}catch( Throwable e ){
857 
858 				Debug.printStackTrace( e );
859 			}
860 		}
861 	}
862 
863 	protected void
destroy()864 	destroy()
865 	{
866 		download_manager.removeListener( this );
867 	}
868 
869 
870 	// DownloadManagerListener methods
871 
872 	public void
stateChanged( DownloadManager manager, int state )873 	stateChanged(
874 		DownloadManager manager,
875 		int				state )
876 	{
877 		int	prev_state 	= latest_state;
878 
879 		latest_state	= convertState(state);
880 
881 		// System.out.println("Plug: dl = " + getName() + ", prev = " + prev_state + ", curr = " + latest_state + ", signalled state = " + state);
882 
883 		boolean curr_forcedStart = isForceStart();
884 
885 		// Copy reference in case any attempts to remove or add listeners are tried.
886 		List listeners_to_use = listeners;
887 
888 		if ( prev_state != latest_state || latest_forcedStart != curr_forcedStart ){
889 
890 			latest_forcedStart = curr_forcedStart;
891 
892 			for (int i=0;i<listeners_to_use.size();i++){
893 
894 				try{
895 					long startTime = SystemTime.getCurrentTime();
896 					DownloadListener listener = (DownloadListener)listeners_to_use.get(i);
897 
898 					listener.stateChanged( this, prev_state, latest_state );
899 
900 					long diff = SystemTime.getCurrentTime() - startTime;
901 					if (diff > 1000) {
902 						System.out.println("Plugin should move long processes (" + diff
903 								+ "ms) off of Download's stateChanged listener trigger. "
904 								+ listener);
905 					}
906 
907 				}catch( Throwable e ){
908 
909 					Debug.printStackTrace( e );
910 				}
911 			}
912 		}
913 	}
914 
915 	public void
downloadComplete(DownloadManager manager)916 	downloadComplete(DownloadManager manager)
917 	{
918 		if (this.completion_listeners.isEmpty()) {return;}
919 		Iterator itr = this.completion_listeners.iterator();
920 		DownloadCompletionListener dcl;
921 		while (itr.hasNext()) {
922 			dcl = (DownloadCompletionListener)itr.next();
923 			long startTime = SystemTime.getCurrentTime();
924 			try {dcl.onCompletion(this);}
925 			catch (Throwable t) {Debug.printStackTrace(t);}
926 			long diff = SystemTime.getCurrentTime() - startTime;
927 			if (diff > 1000) {
928 				System.out.println("Plugin should move long processes (" + diff + "ms) off of Download's onCompletion listener trigger. " + dcl);
929 			}
930 		}
931 	}
932 
933 	public void
completionChanged( DownloadManager manager, boolean bCompleted)934 	completionChanged(
935 		DownloadManager 	manager,
936 		boolean 			bCompleted)
937 	{
938 	}
939 
940 	public void
filePriorityChanged( DownloadManager download, org.gudy.azureus2.core3.disk.DiskManagerFileInfo file )941 	filePriorityChanged( DownloadManager download, org.gudy.azureus2.core3.disk.DiskManagerFileInfo file )
942 	{
943 	}
944 
945   public void
positionChanged( DownloadManager download, int oldPosition, int newPosition)946   positionChanged(
947   	DownloadManager download,
948     int oldPosition,
949 	int newPosition)
950   {
951 	for (int i = 0; i < listeners.size(); i++) {
952 		try {
953 			long startTime = SystemTime.getCurrentTime();
954 			DownloadListener listener = (DownloadListener)listeners.get(i);
955 
956 			listener.positionChanged(this, oldPosition, newPosition);
957 
958 			long diff = SystemTime.getCurrentTime() - startTime;
959 			if (diff > 1000) {
960 				System.out.println("Plugin should move long processes (" + diff
961 						+ "ms) off of Download's positionChanged listener trigger. "
962 						+ listener);
963 			}
964 		} catch (Throwable e) {
965 			Debug.printStackTrace( e );
966 		}
967 	}
968   }
969 
970 	public void
addListener( DownloadListener l )971 	addListener(
972 		DownloadListener	l )
973 	{
974 		try{
975 			listeners_mon.enter();
976 
977 			List	new_listeners = new ArrayList( listeners );
978 
979 			new_listeners.add(l);
980 
981 			listeners	= new_listeners;
982 		}finally{
983 
984 			listeners_mon.exit();
985 		}
986 	}
987 
988 
989 	public void
removeListener( DownloadListener l )990 	removeListener(
991 		DownloadListener	l )
992 	{
993 		try{
994 			listeners_mon.enter();
995 
996 			List	new_listeners	= new ArrayList(listeners);
997 
998 			new_listeners.remove(l);
999 
1000 			listeners	= new_listeners;
1001 		}finally{
1002 
1003 			listeners_mon.exit();
1004 		}
1005 	}
1006 
addAttributeListener(DownloadAttributeListener listener, TorrentAttribute attr, int event_type)1007 	public void addAttributeListener(DownloadAttributeListener listener, TorrentAttribute attr, int event_type) {
1008 		String attribute = convertAttribute(attr);
1009 		if (attribute == null) {return;}
1010 
1011 		CopyOnWriteMap attr_map = this.getAttributeMapForType(event_type);
1012 		CopyOnWriteList listener_list = (CopyOnWriteList)attr_map.get(attribute);
1013 		boolean add_self = false;
1014 
1015 		if (listener_list == null) {
1016 			listener_list = new CopyOnWriteList();
1017 			attr_map.put(attribute, listener_list);
1018 		}
1019 		add_self = listener_list.isEmpty();
1020 
1021 		listener_list.add(listener);
1022 		if (add_self) {
1023 			download_manager.getDownloadState().addListener(this, attribute, event_type);
1024 		}
1025 	}
1026 
removeAttributeListener(DownloadAttributeListener listener, TorrentAttribute attr, int event_type)1027 	public void removeAttributeListener(DownloadAttributeListener listener, TorrentAttribute attr, int event_type) {
1028 		String attribute = convertAttribute(attr);
1029 		if (attribute == null) {return;}
1030 
1031 		CopyOnWriteMap attr_map = this.getAttributeMapForType(event_type);
1032 		CopyOnWriteList listener_list = (CopyOnWriteList)attr_map.get(attribute);
1033 		boolean remove_self = false;
1034 
1035 		if (listener_list != null) {
1036 			listener_list.remove(listener);
1037 			remove_self = listener_list.isEmpty();
1038 		}
1039 
1040 		if (remove_self) {
1041 			download_manager.getDownloadState().removeListener(this, attribute, event_type);
1042 		}
1043 
1044 	}
1045 
1046 	public DownloadAnnounceResult
getLastAnnounceResult()1047 	getLastAnnounceResult()
1048 	{
1049 		TRTrackerAnnouncer tc = download_manager.getTrackerClient();
1050 
1051 		if ( tc != null ){
1052 
1053 			last_announce_result.setContent( tc.getLastResponse());
1054 		}
1055 
1056 		return( last_announce_result );
1057 	}
1058 
1059 	public DownloadScrapeResult
getLastScrapeResult()1060 	getLastScrapeResult()
1061 	{
1062 		TRTrackerScraperResponse response = download_manager.getTrackerScrapeResponse();
1063 
1064 		if ( response != null ){
1065 
1066 				// don't notify plugins of intermediate (initializing, scraping) states as they would be picked up as errors
1067 
1068 			if ( response.getStatus() == TRTrackerScraperResponse.ST_ERROR || response.getStatus() == TRTrackerScraperResponse.ST_ONLINE ){
1069 
1070 				last_scrape_result.setContent( response );
1071 			}
1072 		}
1073 
1074 		return( last_scrape_result );
1075 	}
1076 
1077 	public DownloadScrapeResult
getAggregatedScrapeResult()1078 	getAggregatedScrapeResult()
1079 	{
1080 		DownloadScrapeResult	result = getAggregatedScrapeResultSupport();
1081 
1082 		if ( result != null ){
1083 
1084 			String cache = download_manager.getDownloadState().getAttribute( DownloadManagerState.AT_AGGREGATE_SCRAPE_CACHE );
1085 
1086 			boolean	do_update = true;
1087 
1088 			long	mins = SystemTime.getCurrentTime()/(1000*60);
1089 
1090 			if ( cache != null ){
1091 
1092 				String[]	bits = cache.split(",");
1093 
1094 				if ( bits.length == 3 ){
1095 
1096 					long	updated_mins	= 0;
1097 
1098 					try{
1099 						updated_mins = Long.parseLong( bits[0] );
1100 
1101 					}catch( Throwable e ){
1102 
1103 					}
1104 
1105 					if ( mins - updated_mins < 15 ){
1106 
1107 
1108 						do_update = false;
1109 					}
1110 				}
1111 			}
1112 
1113 			if ( do_update ){
1114 
1115 				String str = mins + "," + result.getSeedCount() + "," + result.getNonSeedCount();
1116 
1117 				download_manager.getDownloadState().setAttribute( DownloadManagerState.AT_AGGREGATE_SCRAPE_CACHE, str  );
1118 			}
1119 		}
1120 
1121 		return( result );
1122 	}
1123 
1124 	private DownloadScrapeResult
getAggregatedScrapeResultSupport()1125 	getAggregatedScrapeResultSupport()
1126 	{
1127 		List<TRTrackerScraperResponse> responses = download_manager.getGoodTrackerScrapeResponses();
1128 
1129 		int	best_peers 	= -1;
1130 		int best_seeds	= -1;
1131 		int	best_time	= -1;
1132 
1133 		TRTrackerScraperResponse	best_resp	= null;
1134 
1135 		if ( responses != null ){
1136 
1137 			for ( TRTrackerScraperResponse response: responses ){
1138 
1139 				int	peers = response.getPeers();
1140 				int seeds = response.getSeeds();
1141 
1142 				if ( 	peers > best_peers ||
1143 						( peers == best_peers && seeds > best_seeds )){
1144 
1145 					best_peers	= peers;
1146 					best_seeds	= seeds;
1147 
1148 					best_resp = response;
1149 				}
1150 			}
1151 		}
1152 
1153 			// if no good real tracker responses then use less reliable DHT ones
1154 
1155 		if ( best_peers == -1 ){
1156 
1157 			try{
1158 				TrackerPeerSource our_dht = null;
1159 
1160 				List<TrackerPeerSource> peer_sources = download_manager.getTrackerPeerSources();
1161 
1162 				for ( TrackerPeerSource ps: peer_sources ){
1163 
1164 					if ( ps.getType() == TrackerPeerSource.TP_DHT ){
1165 
1166 						our_dht = ps;
1167 
1168 						break;
1169 					}
1170 				}
1171 
1172 				peer_listeners_mon.enter();
1173 
1174 				if ( announce_response_map != null ){
1175 
1176 					int	total_seeds = 0;
1177 					int total_peers	= 0;
1178 					int	latest_time	= 0;
1179 
1180 					int	num = 0;
1181 
1182 					if ( our_dht != null && our_dht.getStatus() == TrackerPeerSource.ST_ONLINE ){
1183 
1184 						total_seeds = our_dht.getSeedCount();
1185 						total_peers	= our_dht.getLeecherCount();
1186 						latest_time = our_dht.getLastUpdate();
1187 
1188 						num = 1;
1189 					}
1190 
1191 					for ( int[] entry: announce_response_map.values()){
1192 
1193 						num++;
1194 
1195 						int	seeds 	= entry[0];
1196 						int	peers 	= entry[1];
1197 						int time	= entry[3];
1198 
1199 						total_seeds += seeds;
1200 						total_peers += peers;
1201 
1202 						if ( time > latest_time ){
1203 
1204 							latest_time	= time;
1205 						}
1206 					}
1207 
1208 					if ( total_peers >= 0 ){
1209 
1210 						best_peers	= Math.max( 1, total_peers / num );
1211 						best_seeds	= total_seeds / num;
1212 
1213 						if ( total_seeds > 0 && best_seeds == 0 ){
1214 
1215 							best_seeds = 1;
1216 						}
1217 						best_time	= latest_time;
1218 						best_resp	= null;
1219 					}
1220 				}
1221 
1222 			}finally{
1223 
1224 				peer_listeners_mon.exit();
1225 			}
1226 		}
1227 
1228 		if ( best_peers >= 0 ){
1229 
1230 			// System.out.println( download_manager.getDisplayName() + ": " + best_peers + "/" + best_seeds + "/" + best_resp );
1231 
1232 			last_aggregate_scrape.update( best_resp, best_seeds, best_peers, best_time );
1233 
1234 			return( last_aggregate_scrape );
1235 
1236 		}else{
1237 
1238 			return( getLastScrapeResult());
1239 		}
1240 	}
1241 
1242 	public void
scrapeResult( TRTrackerScraperResponse response )1243 	scrapeResult(
1244 		TRTrackerScraperResponse	response )
1245 	{
1246 		// don't notify plugins of intermediate (initializing, scraping) states as they would be picked up as errors
1247 		if(response.getStatus() != TRTrackerScraperResponse.ST_ERROR && response.getStatus() != TRTrackerScraperResponse.ST_ONLINE)
1248 			return;
1249 
1250 		last_scrape_result.setContent( response );
1251 
1252 		for (int i=0;i<tracker_listeners.size();i++){
1253 
1254 			try{
1255 				((DownloadTrackerListener)tracker_listeners.get(i)).scrapeResult( last_scrape_result );
1256 
1257 			}catch( Throwable e ){
1258 
1259 				Debug.printStackTrace( e );
1260 			}
1261 		}
1262 	}
1263 
1264 	// Used by DownloadEventNotifierImpl.
announceTrackerResultsToListener(DownloadTrackerListener l)1265 	void announceTrackerResultsToListener(DownloadTrackerListener l) {
1266 		l.announceResult(last_announce_result);
1267 		l.scrapeResult(last_scrape_result);
1268 	}
1269 
1270 	public void
announceResult( TRTrackerAnnouncerResponse response )1271 	announceResult(
1272 		TRTrackerAnnouncerResponse			response )
1273 	{
1274 		last_announce_result.setContent( response );
1275 
1276 		List	tracker_listeners_ref = tracker_listeners;
1277 
1278 		for (int i=0;i<tracker_listeners_ref.size();i++){
1279 
1280 			try{
1281 				((DownloadTrackerListener)tracker_listeners_ref.get(i)).announceResult( last_announce_result );
1282 
1283 			}catch( Throwable e ){
1284 
1285 				Debug.printStackTrace( e );
1286 			}
1287 		}
1288 	}
1289 
1290 	public TrackerPeerSource
getTrackerPeerSource()1291 	getTrackerPeerSource()
1292 	{
1293 		return(
1294 			new TrackerPeerSourceAdapter()
1295 			{
1296 				private long	fixup;
1297 				private int		state;
1298 				private String 	details = "";
1299 				private int		seeds;
1300 				private int		leechers;
1301 				private int		peers;
1302 
1303 				private void
1304 				fixup()
1305 				{
1306 					long	now = SystemTime.getCurrentTime();
1307 
1308 					if ( now - fixup > 1000 ){
1309 
1310 						if ( !download_manager.getDownloadState().isPeerSourceEnabled( PEPeerSource.PS_PLUGIN )){
1311 
1312 							state = ST_DISABLED;
1313 
1314 						}else{
1315 
1316 							int s = getState();
1317 
1318 							if ( s == ST_DOWNLOADING || s == ST_SEEDING ){
1319 
1320 								state = ST_ONLINE;
1321 
1322 							}else{
1323 
1324 								state = ST_STOPPED;
1325 							}
1326 						}
1327 
1328 						if ( state == ST_ONLINE ){
1329 
1330 							try{
1331 								peer_listeners_mon.enter();
1332 
1333 								int	s	= 0;
1334 								int	l	= 0;
1335 								int p 	= 0;
1336 
1337 								String	str = "";
1338 
1339 								if ( announce_response_map != null ){
1340 
1341 									for (Map.Entry<String, int[]> entry: announce_response_map.entrySet()){
1342 
1343 										String 	cn 		= entry.getKey();
1344 										int[]	data 	= entry.getValue();
1345 
1346 										str += (str.length()==0?"":", ") + cn;
1347 
1348 										str += " " + data[0] + "/" + data[1] + "/" + data[2];
1349 
1350 										s += data[0];
1351 										l += data[1];
1352 										p += data[2];
1353 									}
1354 								}
1355 
1356 								details 	= str;
1357 
1358 								if ( str.length() == 0 ){
1359 
1360 									seeds		= -1;
1361 									leechers	= -1;
1362 									peers		= -1;
1363 
1364 								}else{
1365 
1366 									seeds		= s;
1367 									leechers	= l;
1368 									peers		= p;
1369 								}
1370 
1371 							}finally{
1372 
1373 								peer_listeners_mon.exit();
1374 							}
1375 						}else{
1376 
1377 							details = "";
1378 						}
1379 
1380 						fixup = now;
1381 					}
1382 				}
1383 
1384 				public int
1385 				getType()
1386 				{
1387 					return( TP_PLUGIN );
1388 				}
1389 
1390 				public int
1391 				getStatus()
1392 				{
1393 					fixup();
1394 
1395 					return( state );
1396 				}
1397 
1398 				public String
1399 				getName()
1400 				{
1401 					fixup();
1402 
1403 					if ( state == ST_ONLINE ){
1404 
1405 						return( details );
1406 					}
1407 
1408 					return( "" );
1409 				}
1410 
1411 				public int
1412 				getSeedCount()
1413 				{
1414 					fixup();
1415 
1416 					if ( state == ST_ONLINE ){
1417 
1418 						return( seeds );
1419 					}
1420 
1421 					return( -1 );
1422 				}
1423 
1424 				public int
1425 				getLeecherCount()
1426 				{
1427 					fixup();
1428 
1429 					if ( state == ST_ONLINE ){
1430 
1431 						return( leechers );
1432 					}
1433 
1434 					return( -1 );
1435 				}
1436 
1437 				public int
1438 				getPeers()
1439 				{
1440 					fixup();
1441 
1442 					if ( state == ST_ONLINE ){
1443 
1444 						return( peers );
1445 					}
1446 
1447 					return( -1 );
1448 				}
1449 			});
1450 	}
1451 
1452 	private String
getTrackingName( Object obj )1453 	getTrackingName(
1454 		Object		obj )
1455 	{
1456 		String	name = obj.getClass().getName();
1457 
1458 		int	pos = name.lastIndexOf( '.' );
1459 
1460 		name = name.substring( pos+1 );
1461 
1462 		pos = name.indexOf( '$' );
1463 
1464 		if ( pos != -1 ){
1465 
1466 			name = name.substring( 0, pos );
1467 		}
1468 
1469 			// hack alert - could use classloader to find plugin I guess
1470 
1471 		pos = name.indexOf( "DHTTrackerPlugin" );
1472 
1473 		if ( pos == 0 ){
1474 
1475 				// built in
1476 
1477 			name = null;
1478 
1479 		}else if ( pos > 0 ){
1480 
1481 			name = name.substring( 0, pos );
1482 
1483 		}else if ( name.equals( "DHTAnnounceResult")){
1484 
1485 			name = "mlDHT";
1486 		}
1487 
1488 		return( name );
1489 	}
1490 
1491 	public void
setAnnounceResult( DownloadAnnounceResult result )1492 	setAnnounceResult(
1493 		DownloadAnnounceResult	result )
1494 	{
1495 		String class_name = getTrackingName( result );
1496 
1497 		if ( class_name != null ){
1498 
1499 			int	seeds 		= result.getSeedCount();
1500 			int	leechers 	= result.getNonSeedCount();
1501 
1502 			DownloadAnnounceResultPeer[] peers = result.getPeers();
1503 
1504 			int	peer_count = peers==null?0:peers.length;
1505 
1506 			try{
1507 				peer_listeners_mon.enter();
1508 
1509 				if ( announce_response_map == null ){
1510 
1511 					announce_response_map = new HashMap<String, int[]>();
1512 
1513 				}else{
1514 
1515 					if ( announce_response_map.size() > 32 ){
1516 
1517 						Debug.out( "eh?" );
1518 
1519 						announce_response_map.clear();
1520 					}
1521 				}
1522 
1523 				int[]	data = (int[])announce_response_map.get( class_name );
1524 
1525 				if ( data == null ){
1526 
1527 					data = new int[4];
1528 
1529 					announce_response_map.put( class_name, data );
1530 				}
1531 
1532 				data[0]	= seeds;
1533 				data[1]	= leechers;
1534 				data[2]	= peer_count;
1535 				data[3] = (int)(SystemTime.getCurrentTime()/1000);
1536 
1537 			}finally{
1538 
1539 				peer_listeners_mon.exit();
1540 			}
1541 		}
1542 
1543 		download_manager.setAnnounceResult( result );
1544 	}
1545 
1546 	public void
setScrapeResult( DownloadScrapeResult result )1547 	setScrapeResult(
1548 		DownloadScrapeResult	result )
1549 	{
1550 		String class_name = getTrackingName( result );
1551 
1552 		if ( class_name != null ){
1553 
1554 			int	seeds 		= result.getSeedCount();
1555 			int	leechers 	= result.getNonSeedCount();
1556 
1557 			try{
1558 				peer_listeners_mon.enter();
1559 
1560 				if ( announce_response_map == null ){
1561 
1562 					announce_response_map = new HashMap<String, int[]>();
1563 
1564 				}else{
1565 
1566 					if ( announce_response_map.size() > 32 ){
1567 
1568 						Debug.out( "eh?" );
1569 
1570 						announce_response_map.clear();
1571 					}
1572 				}
1573 
1574 				int[]	data = (int[])announce_response_map.get( class_name );
1575 
1576 				if ( data == null ){
1577 
1578 					data = new int[4];
1579 
1580 					announce_response_map.put( class_name, data );
1581 				}
1582 
1583 				data[0]	= seeds;
1584 				data[1]	= leechers;
1585 				data[3] = (int)(SystemTime.getCurrentTime()/1000);
1586 			}finally{
1587 
1588 				peer_listeners_mon.exit();
1589 			}
1590 		}
1591 
1592 		download_manager.setScrapeResult( result );
1593 	}
1594 
1595 	public void
stateChanged( DownloadManagerState state, DownloadManagerStateEvent event )1596 	stateChanged(
1597 		DownloadManagerState			state,
1598 		DownloadManagerStateEvent		event )
1599 	{
1600 		final int type = event.getType();
1601 
1602 		if ( 	type == DownloadManagerStateEvent.ET_ATTRIBUTE_WRITTEN ||
1603 				type == DownloadManagerStateEvent.ET_ATTRIBUTE_WILL_BE_READ 	){
1604 
1605 			String	name = (String)event.getData();
1606 
1607 			List	property_listeners_ref = property_listeners;
1608 
1609 			final TorrentAttribute	attr = convertAttribute( name );
1610 
1611 			if ( attr != null ){
1612 
1613 				for (int i=0;i<property_listeners_ref.size();i++){
1614 
1615 					try{
1616 						((DownloadPropertyListener)property_listeners_ref.get(i)).propertyChanged(
1617 								this,
1618 								new DownloadPropertyEvent()
1619 								{
1620 									public int
1621 									getType()
1622 									{
1623 										return( type==DownloadManagerStateEvent.ET_ATTRIBUTE_WRITTEN
1624 													?DownloadPropertyEvent.PT_TORRENT_ATTRIBUTE_WRITTEN
1625 													:DownloadPropertyEvent.PT_TORRENT_ATTRIBUTE_WILL_BE_READ	);
1626 									}
1627 
1628 									public Object
1629 									getData()
1630 									{
1631 										return( attr );
1632 									}
1633 								});
1634 
1635 					}catch( Throwable e ){
1636 
1637 						Debug.printStackTrace( e );
1638 					}
1639 				}
1640 			}
1641 		}
1642 	}
1643 
1644 	public void
addPropertyListener( DownloadPropertyListener l )1645 	addPropertyListener(
1646 		DownloadPropertyListener	l )
1647 	{
1648 
1649 		// Compatibility for the autostop plugin.
1650 		if ("com.aimedia.stopseeding.core.RatioWatcher".equals(l.getClass().getName())) {
1651 
1652 			// Looking at the source code, this method doesn't actually appear to do anything,
1653 			// so we can avoid doing anything for now.
1654 			return;
1655 			/*
1656 			if (property_to_attribute_map == null) {property_to_attribute_map = new HashMap(1);}
1657 			DownloadAttributeListener dal = new PropertyListenerBridge(l);
1658 			property_to_attribute_map.put(l, dal);
1659 			this.addAttributeListener(dal, attr, event_type);
1660 			*/
1661 		}
1662 
1663 		PluginDeprecation.call("property listener", l);
1664 		try{
1665 			tracker_listeners_mon.enter();
1666 
1667 			List	new_property_listeners = new ArrayList( property_listeners );
1668 
1669 			new_property_listeners.add( l );
1670 
1671 			property_listeners	= new_property_listeners;
1672 
1673 			if ( property_listeners.size() == 1 ){
1674 
1675 				download_manager.getDownloadState().addListener( this );
1676 			}
1677 		}finally{
1678 
1679 			tracker_listeners_mon.exit();
1680 		}
1681 	}
1682 
1683 	public void
removePropertyListener( DownloadPropertyListener l )1684 	removePropertyListener(
1685 		DownloadPropertyListener	l )
1686 	{
1687 
1688 		// Compatibility for the autostop plugin.
1689 		if ("com.aimedia.stopseeding.core.RatioWatcher".equals(l.getClass().getName())) {
1690 
1691 			// Looking at the source code, this method doesn't actually appear to do anything,
1692 			// so we can avoid doing anything for now.
1693 			return;
1694 		}
1695 
1696 		try{
1697 			tracker_listeners_mon.enter();
1698 
1699 			List	new_property_listeners	= new ArrayList( property_listeners );
1700 
1701 			new_property_listeners.remove( l );
1702 
1703 			property_listeners	= new_property_listeners;
1704 
1705 			if ( property_listeners.size() == 0 ){
1706 
1707 				download_manager.getDownloadState().removeListener( this );
1708 			}
1709 		}finally{
1710 
1711 			tracker_listeners_mon.exit();
1712 		}
1713 	}
1714 
1715 	public void
torrentChanged()1716 	torrentChanged()
1717 	{
1718 		TRTrackerAnnouncer	client = download_manager.getTrackerClient();
1719 
1720 		if ( client != null ){
1721 
1722 			client.resetTrackerUrl(true);
1723 		}
1724 	}
1725 
1726 	public void
addTrackerListener( DownloadTrackerListener l )1727 	addTrackerListener(
1728 		DownloadTrackerListener	l )
1729 	{
1730 		addTrackerListener(l, true);
1731 	}
1732 
1733 	public void
addTrackerListener( DownloadTrackerListener l, boolean immediateTrigger )1734 	addTrackerListener(
1735 		DownloadTrackerListener	l,
1736 		boolean immediateTrigger )
1737 	{
1738 		try{
1739 			tracker_listeners_mon.enter();
1740 
1741 			List	new_tracker_listeners = new ArrayList( tracker_listeners );
1742 
1743 			new_tracker_listeners.add( l );
1744 
1745 			tracker_listeners	= new_tracker_listeners;
1746 
1747 			if ( tracker_listeners.size() == 1 ){
1748 
1749 				download_manager.addTrackerListener( this );
1750 			}
1751 		}finally{
1752 
1753 			tracker_listeners_mon.exit();
1754 		}
1755 
1756 		if (immediateTrigger) {this.announceTrackerResultsToListener(l);}
1757 	}
1758 
1759 	public void
removeTrackerListener( DownloadTrackerListener l )1760 	removeTrackerListener(
1761 		DownloadTrackerListener	l )
1762 	{
1763 		try{
1764 			tracker_listeners_mon.enter();
1765 
1766 			List	new_tracker_listeners	= new ArrayList( tracker_listeners );
1767 
1768 			new_tracker_listeners.remove( l );
1769 
1770 			tracker_listeners	= new_tracker_listeners;
1771 
1772 			if ( tracker_listeners.size() == 0 ){
1773 
1774 				download_manager.removeTrackerListener( this );
1775 			}
1776 		}finally{
1777 
1778 			tracker_listeners_mon.exit();
1779 		}
1780 	}
1781 
1782 	public void
addDownloadWillBeRemovedListener( DownloadWillBeRemovedListener l )1783 	addDownloadWillBeRemovedListener(
1784 		DownloadWillBeRemovedListener	l )
1785 	{
1786 		try{
1787 			removal_listeners_mon.enter();
1788 
1789 			List	new_removal_listeners	= new ArrayList( removal_listeners );
1790 
1791 			new_removal_listeners.add(l);
1792 
1793 			removal_listeners	= new_removal_listeners;
1794 
1795 		}finally{
1796 
1797 			removal_listeners_mon.exit();
1798 		}
1799 	}
1800 
1801 	public void
removeDownloadWillBeRemovedListener( DownloadWillBeRemovedListener l )1802 	removeDownloadWillBeRemovedListener(
1803 		DownloadWillBeRemovedListener	l )
1804 	{
1805 		try{
1806 			removal_listeners_mon.enter();
1807 
1808 			List	new_removal_listeners	= new ArrayList( removal_listeners );
1809 
1810 			new_removal_listeners.remove(l);
1811 
1812 			removal_listeners	= new_removal_listeners;
1813 
1814 		}finally{
1815 
1816 			removal_listeners_mon.exit();
1817 		}
1818 	}
1819 
1820 	public void
addPeerListener( final DownloadPeerListener listener )1821 	addPeerListener(
1822 		final DownloadPeerListener	listener )
1823 	{
1824 		DownloadManagerPeerListener delegate =
1825 			new DownloadManagerPeerListener()
1826 			{
1827 
1828 				public void
1829 				peerManagerAdded(
1830 					PEPeerManager	manager )
1831 				{
1832 					PeerManager pm = PeerManagerImpl.getPeerManager( manager);
1833 
1834 					listener.peerManagerAdded( DownloadImpl.this, pm );
1835 				}
1836 
1837 				public void
1838 				peerManagerRemoved(
1839 					PEPeerManager	manager )
1840 				{
1841 					PeerManager pm = PeerManagerImpl.getPeerManager( manager);
1842 
1843 					listener.peerManagerRemoved( DownloadImpl.this, pm );
1844 
1845 				}
1846 
1847 				public void
1848 				peerManagerWillBeAdded(
1849 					PEPeerManager	manager )
1850 				{
1851 				}
1852 
1853 				public void
1854 				peerAdded(
1855 					PEPeer 	peer )
1856 				{
1857 				}
1858 
1859 				public void
1860 				peerRemoved(
1861 					PEPeer	peer )
1862 				{
1863 				}
1864 			};
1865 
1866 		try{
1867 			peer_listeners_mon.enter();
1868 
1869 			peer_listeners.put( listener, delegate );
1870 
1871 		}finally{
1872 
1873 			peer_listeners_mon.exit();
1874 		}
1875 
1876 		download_manager.addPeerListener( delegate );
1877 	}
1878 
1879 
1880 	public void
removePeerListener( DownloadPeerListener listener )1881 	removePeerListener(
1882 		DownloadPeerListener	listener )
1883 	{
1884 		DownloadManagerPeerListener delegate;
1885 
1886 		try{
1887 			peer_listeners_mon.enter();
1888 
1889 			delegate = (DownloadManagerPeerListener)peer_listeners.remove( listener );
1890 
1891 		}finally{
1892 
1893 			peer_listeners_mon.exit();
1894 		}
1895 
1896 		if ( delegate == null ){
1897 
1898 			// sometimes we end up with double removal so don't bother spewing about this
1899 			// Debug.out( "Listener not found for removal" );
1900 
1901 		}else{
1902 
1903 			download_manager.removePeerListener( delegate );
1904 		}
1905 	}
1906 
1907 	public boolean
activateRequest( final int count )1908 	activateRequest(
1909 		final int		count )
1910 	{
1911 		DownloadActivationEvent event =
1912 			new DownloadActivationEvent()
1913 		{
1914 			public Download
1915 			getDownload()
1916 			{
1917 				return( DownloadImpl.this );
1918 			}
1919 
1920 			public int
1921 			getActivationCount()
1922 			{
1923 				return( count );
1924 			}
1925 		};
1926 
1927 		for (Iterator it=activation_listeners.iterator();it.hasNext();){
1928 
1929 			try{
1930 				DownloadActivationListener	listener = (DownloadActivationListener)it.next();
1931 
1932 				if ( listener.activationRequested( event )){
1933 
1934 					return( true );
1935 				}
1936 			}catch( Throwable e ){
1937 
1938 				Debug.printStackTrace(e);
1939 			}
1940 		}
1941 
1942 		return( false );
1943 	}
1944 
1945 	public DownloadActivationEvent
getActivationState()1946 	getActivationState()
1947 	{
1948 		return( activation_state );
1949 	}
1950 
1951 	public void
addActivationListener( DownloadActivationListener l )1952 	addActivationListener(
1953 		DownloadActivationListener		l )
1954 	{
1955 		try{
1956 			peer_listeners_mon.enter();
1957 
1958 			activation_listeners.add( l );
1959 
1960 			if ( activation_listeners.size() == 1 ){
1961 
1962 				download_manager.addActivationListener( this );
1963 			}
1964 		}finally{
1965 
1966 			peer_listeners_mon.exit();
1967 		}
1968 	}
1969 
1970 	public void
removeActivationListener( DownloadActivationListener l )1971 	removeActivationListener(
1972 		DownloadActivationListener		l )
1973 	{
1974 		try{
1975 			peer_listeners_mon.enter();
1976 
1977 			activation_listeners.remove( l );
1978 
1979 			if ( activation_listeners.size() == 0 ){
1980 
1981 				download_manager.removeActivationListener( this );
1982 			}
1983 		}finally{
1984 
1985 			peer_listeners_mon.exit();
1986 		}
1987 	}
1988 
addCompletionListener(DownloadCompletionListener l)1989 	public void	addCompletionListener(DownloadCompletionListener l) {
1990 		try {
1991 			listeners_mon.enter();
1992 			this.completion_listeners.add(l);
1993 		}
1994 		finally{
1995 			listeners_mon.exit();
1996 		}
1997 	}
1998 
removeCompletionListener(DownloadCompletionListener l)1999 	public void	removeCompletionListener(DownloadCompletionListener l) {
2000 		try {
2001 			listeners_mon.enter();
2002 			this.completion_listeners.remove(l);
2003 		}
2004 		finally{
2005 			listeners_mon.exit();
2006 		}
2007 	}
2008 
2009  	public PeerManager
getPeerManager()2010 	getPeerManager()
2011  	{
2012  		PEPeerManager	pm = download_manager.getPeerManager();
2013 
2014  		if ( pm == null ){
2015 
2016  			return( null );
2017  		}
2018 
2019  		return( PeerManagerImpl.getPeerManager( pm));
2020  	}
2021 
2022 	public DiskManager
getDiskManager()2023 	getDiskManager()
2024 	{
2025 		PeerManager	pm = getPeerManager();
2026 
2027 		if ( pm != null ){
2028 
2029 			return( pm.getDiskManager());
2030 		}
2031 
2032 		return( null );
2033 	}
2034 
getDiskManagerFileCount()2035 	public int getDiskManagerFileCount() {
2036 		return download_manager.getNumFileInfos();
2037 	}
2038 
getDiskManagerFileInfo(int index)2039 	public DiskManagerFileInfo getDiskManagerFileInfo(int index) {
2040 		org.gudy.azureus2.core3.disk.DiskManagerFileInfo[] info = download_manager.getDiskManagerFileInfo();
2041 
2042 		if (info == null) {
2043 			return null;
2044 		}
2045 		if (index < 0 || index >= info.length) {
2046 			return null;
2047 		}
2048 
2049 		return new DiskManagerFileInfoImpl(this, info[index]);
2050 	}
2051 
2052 	public DiskManagerFileInfo
getPrimaryFile()2053 	getPrimaryFile() {
2054 		org.gudy.azureus2.core3.disk.DiskManagerFileInfo primaryFile = download_manager.getDownloadState().getPrimaryFile();
2055 
2056 		if (primaryFile == null) {
2057 			return null;
2058 		}
2059 		return new DiskManagerFileInfoImpl(this, primaryFile);
2060 	}
2061 
2062 	public DiskManagerFileInfo[]
getDiskManagerFileInfo()2063 	getDiskManagerFileInfo()
2064 	{
2065 		org.gudy.azureus2.core3.disk.DiskManagerFileInfo[] info = download_manager.getDiskManagerFileInfo();
2066 
2067 		if ( info == null ){
2068 
2069 			return( new DiskManagerFileInfo[0] );
2070 		}
2071 
2072 		DiskManagerFileInfo[]	res = new DiskManagerFileInfo[info.length];
2073 
2074 		for (int i=0;i<res.length;i++){
2075 
2076 			res[i] = new DiskManagerFileInfoImpl( this, info[i] );
2077 		}
2078 
2079 		return( res );
2080 	}
2081 
2082  	public void
setMaximumDownloadKBPerSecond( int kb )2083 	setMaximumDownloadKBPerSecond(
2084 		int		kb )
2085  	{
2086          if(kb==-1){
2087             Debug.out("setMaximiumDownloadKBPerSecond got value (-1) ZERO_DOWNLOAD. (-1)"+
2088                 "does not work through this method, use getDownloadRateLimitBytesPerSecond() instead.");
2089          }//if
2090 
2091          download_manager.getStats().setDownloadRateLimitBytesPerSecond( kb < 0 ? 0 : kb*1024 );
2092  	}
2093 
2094 	public int getMaximumDownloadKBPerSecond() {
2095 		int bps = download_manager.getStats().getDownloadRateLimitBytesPerSecond();
2096 		return bps <= 0 ? bps : (bps < 1024 ? 1 : bps / 1024);
2097 	}
2098 
2099   	public int getUploadRateLimitBytesPerSecond() {
2100       return download_manager.getStats().getUploadRateLimitBytesPerSecond();
2101   	}
2102 
2103   	public void setUploadRateLimitBytesPerSecond( int max_rate_bps ) {
2104       download_manager.getStats().setUploadRateLimitBytesPerSecond( max_rate_bps );
2105   	}
2106 
2107   	public int getDownloadRateLimitBytesPerSecond() {
2108   		return download_manager.getStats().getDownloadRateLimitBytesPerSecond();
2109   	}
2110 
2111   	public void setDownloadRateLimitBytesPerSecond( int max_rate_bps ) {
2112   		download_manager.getStats().setDownloadRateLimitBytesPerSecond( max_rate_bps );
2113   	}
2114 
2115 	public void
2116 	addRateLimiter(
2117 		RateLimiter		limiter,
2118 		boolean			is_upload )
2119 	{
2120 		download_manager.addRateLimiter( UtilitiesImpl.wrapLimiter( limiter, false ), is_upload );
2121 	}
2122 
2123 	public void
2124 	removeRateLimiter(
2125 		RateLimiter		limiter,
2126 		boolean			is_upload )
2127 	{
2128 		download_manager.removeRateLimiter( UtilitiesImpl.wrapLimiter( limiter, false ), is_upload );
2129 	}
2130 
2131   public int getSeedingRank() {
2132     return download_manager.getSeedingRank();
2133   }
2134 
2135 	public void setSeedingRank(int rank) {
2136 		download_manager.setSeedingRank(rank);
2137 	}
2138 
2139 	public String
2140 	getSavePath()
2141  	{
2142 		return( download_manager.getSaveLocation().toString());
2143  	}
2144 
2145 	public void
2146   	moveDataFiles(
2147   		File	new_parent_dir )
2148 
2149   		throws DownloadException
2150   	{
2151  		try{
2152  			download_manager.moveDataFiles( new_parent_dir );
2153 
2154  		}catch( DownloadManagerException e ){
2155 
2156  			throw( new DownloadException("move operation failed", e ));
2157  		}
2158   	}
2159 
2160 	public void moveDataFiles(File new_parent_dir, String new_name)
2161 
2162   		throws DownloadException
2163   	{
2164  		try{
2165  			download_manager.moveDataFiles( new_parent_dir, new_name );
2166 
2167  		}catch( DownloadManagerException e ){
2168 
2169  			throw( new DownloadException("move / rename operation failed", e ));
2170  		}
2171   	}
2172 
2173 	public void renameDownload(String new_name) throws DownloadException {
2174 		try {download_manager.renameDownload(new_name);}
2175 		catch (DownloadManagerException e) {
2176 			throw new DownloadException("rename operation failed", e);
2177 		}
2178 	}
2179 
2180   	public void
2181   	moveTorrentFile(
2182   		File	new_parent_dir )
2183 
2184   		throws DownloadException
2185  	{
2186 		try{
2187  			download_manager.moveTorrentFile( new_parent_dir );
2188 
2189  		}catch( DownloadManagerException e ){
2190 
2191  			throw( new DownloadException("move operation failed", e ));
2192  		}
2193  	}
2194 
2195   	/**
2196   	 * @deprecated
2197   	 */
2198   	public File[] calculateDefaultPaths(boolean for_moving) {
2199   	  SaveLocationChange slc = this.calculateDefaultDownloadLocation();
2200 	  if (slc == null) {return null;}
2201 	  return new File[] {slc.download_location, slc.torrent_location};
2202   	}
2203 
2204   	public boolean isInDefaultSaveDir() {
2205   		return download_manager.isInDefaultSaveDir();
2206   	}
2207 
2208  	public void
2209 	requestTrackerAnnounce()
2210  	{
2211  		download_manager.requestTrackerAnnounce( false );
2212  	}
2213 
2214 	public void
2215 	requestTrackerAnnounce(
2216 		boolean		immediate )
2217 	{
2218 		download_manager.requestTrackerAnnounce( immediate );
2219 	}
2220 
2221 	public void
2222 	requestTrackerScrape(
2223 		boolean		immediate )
2224 	{
2225 		download_manager.requestTrackerScrape( immediate );
2226 	}
2227 
2228   public byte[] getDownloadPeerId() {
2229     TRTrackerAnnouncer announcer = download_manager.getTrackerClient();
2230     if(announcer == null) return null;
2231     return announcer.getPeerId();
2232   }
2233 
2234 
2235   public boolean isMessagingEnabled() {  return download_manager.getExtendedMessagingMode() == BTHandshake.AZ_RESERVED_MODE;  }
2236 
2237   public void setMessagingEnabled( boolean enabled ) {
2238 	  throw new RuntimeException("setMessagingEnabled is in the process of being removed - if you are seeing this error, let the Azureus developers know that you need this method to stay!");
2239     //download_manager.setAZMessagingEnabled( enabled );
2240   }
2241 
2242 
2243  	// Deprecated methods
2244 
2245   public int getPriority() {
2246     return 0;
2247   }
2248 
2249   public boolean isPriorityLocked() {
2250     return false;
2251   }
2252 
2253   public void setPriority(int priority) {
2254   }
2255 
2256   public boolean isRemoved() {
2257 	return( download_manager.isDestroyed());
2258   }
2259   // Pass LogRelation off to core objects
2260 
2261 	/* (non-Javadoc)
2262 	 * @see org.gudy.azureus2.core3.logging.LogRelation#getLogRelationText()
2263 	 */
2264 	public String getRelationText() {
2265 		return propogatedRelationText(download_manager);
2266 	}
2267 
2268 	/* (non-Javadoc)
2269 	 * @see org.gudy.azureus2.core3.logging.LogRelation#getQueryableInterfaces()
2270 	 */
2271 	public Object[] getQueryableInterfaces() {
2272 		return new Object[] { download_manager };
2273 	}
2274 
2275 	private CopyOnWriteMap getAttributeMapForType(int event_type) {
2276 		return event_type == DownloadPropertyEvent.PT_TORRENT_ATTRIBUTE_WILL_BE_READ ? read_attribute_listeners_map_cow : write_attribute_listeners_map_cow;
2277 	}
2278 
2279 	public boolean canMoveDataFiles() {
2280 		return download_manager.canMoveDataFiles();
2281 	}
2282 
2283 	public void attributeEventOccurred(DownloadManager download, String attribute, int event_type) {
2284 		CopyOnWriteMap attr_listener_map = getAttributeMapForType(event_type);
2285 
2286 		TorrentAttribute attr = convertAttribute(attribute);
2287 		if (attr == null) {return;}
2288 
2289 		List listeners = null;
2290 		listeners = ((CopyOnWriteList)attr_listener_map.get(attribute)).getList();
2291 
2292 		if (listeners == null) {return;}
2293 
2294 		for (int i=0; i<listeners.size(); i++) {
2295 			DownloadAttributeListener dal = (DownloadAttributeListener)listeners.get(i);
2296 			try {dal.attributeEventOccurred(this, attr, event_type);}
2297 			catch (Throwable t) {Debug.printStackTrace(t);}
2298 		}
2299 	}
2300 
2301 	public SaveLocationChange calculateDefaultDownloadLocation() {
2302 		return DownloadManagerMoveHandler.recalculatePath(this.download_manager);
2303 	}
2304 
2305 	 public Object getUserData( Object key ){
2306 		 return( download_manager.getUserData(key));
2307 	 }
2308 
2309 	 public void setUserData( Object key, Object data ){
2310 		 download_manager.setUserData(key, data);
2311 	 }
2312 
2313 	 public void startDownload(boolean force) {
2314 		if (force) {
2315 			this.setForceStart(true);
2316 			return;
2317 		}
2318 		this.setForceStart(false);
2319 
2320 		int state = this.getState();
2321 		if (state == DownloadManager.STATE_STOPPED ||	state == DownloadManager.STATE_QUEUED) {
2322 			download_manager.setStateWaiting();
2323 		}
2324 
2325 	 }
2326 
2327 	 public void stopDownload() {
2328 		 if (download_manager.getState() == DownloadManager.STATE_STOPPED) {return;}
2329 		 download_manager.stopIt(DownloadManager.STATE_STOPPED, false, false);
2330 	 }
2331 
2332 	 	// stub stuff
2333 
2334 	 public boolean
2335 	 isStub()
2336 	 {
2337 		 return( false );
2338 	 }
2339 
2340 	 public boolean
2341 	 canStubbify()
2342 	 {
2343 		 return( manager.canStubbify( this ));
2344 	 }
2345 
2346 	 public DownloadStub
2347 	 stubbify()
2348 
2349 	 	throws DownloadException, DownloadRemovalVetoException
2350 	 {
2351 		return( manager.stubbify( this ));
2352 	 }
2353 
2354 	 public Download
2355 	 destubbify()
2356 
2357 	 	throws DownloadException
2358 	 {
2359 		 return( this );
2360 	 }
2361 
2362 	 public List<DistributedDatabase>
2363 	 getDistributedDatabases()
2364 	 {
2365 		 return( DDBaseImpl.getDDBs( this ));
2366 	 }
2367 
2368 	 public byte[]
2369 	 getTorrentHash()
2370 	 {
2371 		 Torrent t = getTorrent();
2372 
2373 		 if ( t == null ){
2374 
2375 			 return( null );
2376 		 }
2377 
2378 		 return( t.getHash());
2379 	 }
2380 
2381 
2382 	 public long
2383 	 getTorrentSize()
2384 	 {
2385 		 Torrent t = getTorrent();
2386 
2387 		 if ( t == null ){
2388 
2389 			 return( 0 );
2390 		 }
2391 
2392 		 return( t.getSize());
2393 	 }
2394 
2395 	 public DownloadStubFile[]
2396 	 getStubFiles()
2397 	 {
2398 		 DiskManagerFileInfo[] dm_files = getDiskManagerFileInfo();
2399 
2400 		 DownloadStubFile[] files = new DownloadStubFile[dm_files.length];
2401 
2402 		 for ( int i=0;i<files.length;i++){
2403 
2404 			 final DiskManagerFileInfo dm_file = dm_files[i];
2405 
2406 			 files[i] =
2407 				new	DownloadStubFile()
2408 			 	{
2409 					public File
2410 					getFile()
2411 					{
2412 						return( dm_file.getFile( true ));
2413 					}
2414 
2415 					public long
2416 					getLength()
2417 					{
2418 						if ( dm_file.getDownloaded() == dm_file.getLength() && !dm_file.isSkipped()){
2419 
2420 							return( dm_file.getLength());
2421 
2422 						}else{
2423 
2424 							return( -dm_file.getLength());
2425 						}
2426 					}
2427 			 	};
2428 		 }
2429 
2430 		 return( files );
2431 	 }
2432 
2433 
2434 	 public void changeLocation(SaveLocationChange slc) throws DownloadException {
2435 
2436 		 // No change in the file.
2437 		 boolean has_change = slc.hasDownloadChange() || slc.hasTorrentChange();
2438 		 if (!has_change) {return;}
2439 
2440 		 // Test that one of the locations is actually different.
2441 		 has_change = slc.isDifferentDownloadLocation(new File(this.getSavePath()));
2442 		 if (!has_change) {
2443 			 has_change = slc.isDifferentTorrentLocation(new File(this.getTorrentFileName()));
2444 		 }
2445 
2446 		 if (!has_change) {return;}
2447 
2448 		 boolean try_to_resume = !this.isPaused();
2449 		 try {
2450 			 try {
2451 				 if (slc.hasDownloadChange()) {download_manager.moveDataFiles(slc.download_location, slc.download_name);}
2452 				 if (slc.hasTorrentChange()) {download_manager.moveTorrentFile(slc.torrent_location, slc.torrent_name);}
2453 			 }
2454 			 catch (DownloadManagerException e) {
2455 				 throw new DownloadException(e.getMessage(), e);
2456 			 }
2457 		 }
2458 		 finally {
2459 			 if (try_to_resume) {this.resume();}
2460 		 }
2461 
2462 	 }
2463 
2464 	 private static class PropertyListenerBridge implements DownloadAttributeListener {
2465 		 private DownloadPropertyListener l;
2466 		 public PropertyListenerBridge(DownloadPropertyListener l) {this.l = l;}
2467 		 public void attributeEventOccurred(Download d, final TorrentAttribute attr, final int event_type) {
2468 			 l.propertyChanged(d, new DownloadPropertyEvent() {
2469 				 public int getType() {return event_type;}
2470 				 public Object getData() {return attr;}
2471 			 });
2472 		 }
2473 	 }
2474 
2475 	 private static class
2476 	 AggregateScrapeResult
2477 	 	implements DownloadScrapeResult
2478 	 {
2479 		private Download	dl;
2480 
2481 		private TRTrackerScraperResponse		response;
2482 
2483 		private int		seeds;
2484 		private int		leechers;
2485 
2486 		private int		time_secs;
2487 
2488 		private
2489 		AggregateScrapeResult(
2490 			Download		_dl )
2491 		{
2492 			dl	= _dl;
2493 		}
2494 
2495 		private void
2496 		update(
2497 			TRTrackerScraperResponse		_response,
2498 			int								_seeds,
2499 			int								_peers,
2500 			int								_time_secs )
2501 		{
2502 			response			= _response;
2503 			seeds				= _seeds;
2504 			leechers			= _peers;
2505 			time_secs			= _time_secs;
2506 		}
2507 
2508 		public Download
2509 		getDownload()
2510 		{
2511 			return( dl );
2512 		}
2513 
2514 		public int
2515 		getResponseType()
2516 		{
2517 			return( RT_SUCCESS );
2518 		}
2519 
2520 		public int
2521 		getSeedCount()
2522 		{
2523 			return( seeds );
2524 		}
2525 
2526 		public int
2527 		getNonSeedCount()
2528 		{
2529 			return( leechers );
2530 		}
2531 
2532 		public long
2533 		getScrapeStartTime()
2534 		{
2535 			TRTrackerScraperResponse r = response;
2536 
2537 			if ( r != null ){
2538 
2539 				return( r.getScrapeStartTime());
2540 			}
2541 
2542 			if ( time_secs <= 0 ){
2543 
2544 				return( -1 );
2545 
2546 			}else{
2547 
2548 				return( time_secs * 1000L );
2549 			}
2550 		}
2551 
2552 		public void
2553 		setNextScrapeStartTime(
2554 			long nextScrapeStartTime)
2555 		{
2556 			Debug.out( "Not Supported" );
2557 		}
2558 
2559 		public long
2560 		getNextScrapeStartTime()
2561 		{
2562 			TRTrackerScraperResponse r = response;
2563 
2564 			return( r == null?-1:r.getScrapeStartTime());
2565 		}
2566 
2567 		public String
2568 		getStatus()
2569 		{
2570 			return( "Aggregate Scrape" );
2571 		}
2572 
2573 		public URL
2574 		getURL()
2575 		{
2576 			TRTrackerScraperResponse r = response;
2577 
2578 			return( r == null?null:r.getURL());
2579 		}
2580 	 }
2581 }