1 /*
2  * File    : PluginInitializer.java
3  * Created : 2 nov. 2003 18:59:17
4  * By      : Olivier
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;
24 
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.InputStream;
28 import java.lang.reflect.Method;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.net.URLConnection;
32 import java.util.*;
33 
34 import org.gudy.azureus2.core3.config.COConfigurationManager;
35 import org.gudy.azureus2.core3.download.DownloadManager;
36 import org.gudy.azureus2.core3.global.GlobalManager;
37 import org.gudy.azureus2.core3.global.GlobalManagerListener;
38 import org.gudy.azureus2.core3.internat.MessageText;
39 import org.gudy.azureus2.core3.logging.*;
40 import org.gudy.azureus2.core3.security.SESecurityManager;
41 import org.gudy.azureus2.core3.util.*;
42 import org.gudy.azureus2.platform.PlatformManagerFactory;
43 import org.gudy.azureus2.plugins.*;
44 import org.gudy.azureus2.plugins.platform.PlatformManagerException;
45 import org.gudy.azureus2.pluginsimpl.local.launch.PluginLauncherImpl;
46 import org.gudy.azureus2.pluginsimpl.local.ui.UIManagerImpl;
47 import org.gudy.azureus2.pluginsimpl.local.update.UpdateManagerImpl;
48 import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl;
49 import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl.runnableWithException;
50 import org.gudy.azureus2.update.UpdaterUpdateChecker;
51 import org.gudy.azureus2.update.UpdaterUtils;
52 
53 
54 import com.aelitis.azureus.core.*;
55 import com.aelitis.azureus.core.versioncheck.VersionCheckClient;
56 
57 
58 
59 /**
60  * @author Olivier
61  *
62  */
63 public class
64 PluginInitializer
65 	implements GlobalManagerListener, AEDiagnosticsEvidenceGenerator
66 {
67 	public static final boolean DISABLE_PLUGIN_VERIFICATION = false;
68 
69 	private static final LogIDs LOGID = LogIDs.CORE;
70 	public static final String	INTERNAL_PLUGIN_ID = "<internal>";
71 
72 	// class name, plugin id, plugin key (key used for config props so if you change
73 	// it you'll need to migrate the config)
74 	// "id" is used when checking for updates
75 
76 	// IF YOU ADD TO THE BUILTIN PLUGINS, AMEND PluginManagerDefault appropriately!!!!
77 
78 		// Plugin ID constant
79 		// class
80 		// plugin id
81 		// plugin key for prefixing config data
82 		// report if not present
83 	// force re-enable if disabled by config
84 
85   private String[][]	builtin_plugins = {
86    			{	 PluginManagerDefaults.PID_START_STOP_RULES,
87    					"com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin",
88    					"azbpstartstoprules",
89    					"",
90    					"true",
91    					"true"},
92    			{	 PluginManagerDefaults.PID_REMOVE_RULES,
93    					"com.aelitis.azureus.plugins.removerules.DownloadRemoveRulesPlugin",
94    					"azbpremovalrules",
95    					"",
96 					"true",
97 					"false"},
98     		{	 PluginManagerDefaults.PID_SHARE_HOSTER,
99    					"com.aelitis.azureus.plugins.sharing.hoster.ShareHosterPlugin",
100    					"azbpsharehoster",
101    					"ShareHoster",
102 					"true",
103 					"false"},
104    			{	 PluginManagerDefaults.PID_PLUGIN_UPDATE_CHECKER,
105    					"org.gudy.azureus2.pluginsimpl.update.PluginUpdatePlugin",
106    					"azbppluginupdate",
107    					"PluginUpdate",
108 					"true",
109 					"true"},
110 			{	 PluginManagerDefaults.PID_UPNP,
111 				    "com.aelitis.azureus.plugins.upnp.UPnPPlugin",
112 				    "azbpupnp",
113 				    "UPnP",
114 					"true",
115 					"false"},
116 			{	 PluginManagerDefaults.PID_DHT,
117 					"com.aelitis.azureus.plugins.dht.DHTPlugin",
118 					"azbpdht",
119 					"DHT",
120 					"true",
121 					"false"},
122 			{	 PluginManagerDefaults.PID_DHT_TRACKER,
123 					"com.aelitis.azureus.plugins.tracker.dht.DHTTrackerPlugin",
124 					"azbpdhdtracker",
125 					"DHT Tracker",
126 					"true",
127 					"false"},
128 			{	 PluginManagerDefaults.PID_MAGNET,
129 					"com.aelitis.azureus.plugins.magnet.MagnetPlugin",
130 					"azbpmagnet",
131 					"Magnet URI Handler",
132 					"true",
133 					"false"},
134 			{	 PluginManagerDefaults.PID_CORE_UPDATE_CHECKER,
135    					"org.gudy.azureus2.update.CoreUpdateChecker",
136    					"azbpcoreupdater",
137    					"CoreUpdater",
138 					"true",
139 					"true"},
140 			{	 PluginManagerDefaults.PID_CORE_PATCH_CHECKER,
141    					"org.gudy.azureus2.update.CorePatchChecker",
142    					"azbpcorepatcher",
143    					"CorePatcher",
144 					"true",
145 					"true"},
146 	   		{	 PluginManagerDefaults.PID_PLATFORM_CHECKER,
147    					"org.gudy.azureus2.platform.PlatformManagerPluginDelegate",
148    					"azplatform2",
149    					"azplatform2",
150 					"true",
151 					"false"},
152 	   		//{	 PluginManagerDefaults.PID_JPC,
153 				//	"com.aelitis.azureus.plugins.jpc.JPCPlugin",
154 				//	"azjpc",
155 				//	"azjpc",
156 				//	"false" },
157 	   		{	 PluginManagerDefaults.PID_EXTERNAL_SEED,
158 					"com.aelitis.azureus.plugins.extseed.ExternalSeedPlugin",
159 					"azextseed",
160 					"azextseed",
161 	   				"true",
162 	   				"false"},
163 	   		{	 PluginManagerDefaults.PID_LOCAL_TRACKER,
164 	   				"com.aelitis.azureus.plugins.tracker.local.LocalTrackerPlugin",
165 	   				"azlocaltracker",
166 	   				"azlocaltracker",
167 					"true",
168 					"false"},
169 			{	 PluginManagerDefaults.PID_NET_STATUS,
170 		   			"com.aelitis.azureus.plugins.net.netstatus.NetStatusPlugin",
171 		   			"aznetstat",
172 		   			"aznetstat",
173 					"true",
174 					"false"},
175 			{	 PluginManagerDefaults.PID_BUDDY,
176 					"com.aelitis.azureus.plugins.net.buddy.BuddyPlugin",
177 					"azbuddy",
178 					"azbuddy",
179 					"true",
180 					"false"},
181 			{	 PluginManagerDefaults.PID_RSS,
182 					"com.aelitis.azureus.core.rssgen.RSSGeneratorPlugin",
183 					"azintrss",
184 					"azintrss",
185 					"true",
186 					"false"},
187 			/* disable until we can get some tracker admins to work on this
188 	   		{	 PluginManagerDefaults.PID_TRACKER_PEER_AUTH,
189 					"com.aelitis.azureus.plugins.tracker.peerauth.TrackerPeerAuthPlugin",
190 					"aztrackerpeerauth",
191 					"aztrackerpeerauth",
192 					"true",
193 					"false" },
194 			*/
195         };
196 
197   static VerifiedPluginHolder verified_plugin_holder;
198 
199   static{
200 	  synchronized( PluginInitializer.class ){
201 
202 		  verified_plugin_holder = new VerifiedPluginHolder();
203 	  }
204   }
205 
206   	// these can be removed one day
207 
208   private static String[][]default_version_details =
209   {
210 		{ "org.cneclipse.multiport.MultiPortPlugin",
211   				"multi-ports", 		"Mutli-Port Trackers",	"1.0" },
212   };
213 
214   private static PluginInitializer	singleton;
215   private static AEMonitor			class_mon	= new AEMonitor( "PluginInitializer");
216 
217   private static List		registration_queue 	= new ArrayList();
218 
219   private static List		initThreads = new ArrayList(1);
220 
221   private static AsyncDispatcher	async_dispatcher = new AsyncDispatcher();
222   private static List<PluginEvent>	plugin_event_history = new ArrayList<PluginEvent>();
223 
224 
225 
226   private AzureusCoreOperation core_operation;
227 
228   private AzureusCore		azureus_core;
229 
230   private PluginInterfaceImpl	default_plugin;
231   private PluginManager			plugin_manager;
232 
233   private ClassLoader			root_class_loader	= getClass().getClassLoader();
234 
235   private List		loaded_pi_list		= new ArrayList();
236 
237   private static boolean	loading_builtin;
238 
239   private List<Plugin>					s_plugins				= new ArrayList<Plugin>();
240   private List<PluginInterfaceImpl>		s_plugin_interfaces		= new ArrayList<PluginInterfaceImpl>();
241 
242   private boolean	initialisation_complete;
243 
244   private volatile boolean	plugins_initialised;
245 
246   private Set<String>	vc_disabled_plugins = VersionCheckClient.getSingleton().getDisabledPluginIDs();
247 
248   public static PluginInitializer
getSingleton( AzureusCore azureus_core, AzureusCoreOperation core_operation )249   getSingleton(
250   	AzureusCore		 		azureus_core,
251   	AzureusCoreOperation 	core_operation )
252   {
253   	try{
254   		class_mon.enter();
255 
256 	  	if ( singleton == null ){
257 
258 	  		singleton = new PluginInitializer( azureus_core, core_operation );
259 	  	}
260 
261 	  	return( singleton );
262 
263 	}finally{
264 
265 		class_mon.exit();
266 	}
267   }
268 
269   private static PluginInitializer
peekSingleton()270   peekSingleton()
271   {
272   	try{
273   		class_mon.enter();
274 
275 	  	return( singleton );
276 
277 	}finally{
278 
279 		class_mon.exit();
280 	}
281   }
282 
283   protected static void
queueRegistration( Class _class )284   queueRegistration(
285   	Class	_class )
286   {
287   	try{
288   		class_mon.enter();
289 
290 	   	if ( singleton == null ){
291 
292 	  		registration_queue.add( _class );
293 
294 	  	}else{
295 
296 	  		try{
297 	  			singleton.initializePluginFromClass( _class, INTERNAL_PLUGIN_ID, _class.getName(), false, false, true);
298 
299 			}catch(PluginException e ){
300 
301 	  		}
302 	  	}
303 	}finally{
304 
305 		class_mon.exit();
306 	}
307   }
308 
309   protected static void
queueRegistration( Plugin plugin, String id, String config_key )310   queueRegistration(
311   	Plugin		plugin,
312 	String		id,
313 	String		config_key )
314   {
315   	try{
316   		class_mon.enter();
317 
318 	   	if ( singleton == null ){
319 
320 	  		registration_queue.add( new Object[]{ plugin, id, config_key });
321 
322 	  	}else{
323 
324 	  		try{
325 	  			singleton.initializePluginFromInstance( plugin, id, config_key );
326 
327 			}catch( Throwable e ){
328 
329 				Debug.out( e );
330 	  		}
331 	  	}
332 	}finally{
333 
334 		class_mon.exit();
335 	}
336   }
337 
338   protected static boolean
isLoadingBuiltin()339   isLoadingBuiltin()
340   {
341 	  return( loading_builtin );
342   }
343 
344   public static void
checkAzureusVersion( String name, Properties props, boolean alert_on_fail )345   checkAzureusVersion(
346 	  String name,
347 	  Properties props,
348 	  boolean alert_on_fail
349   ) throws PluginException {
350 
351 	  String required_version = (String)props.get("plugin.azureus.min_version");
352 	  if (required_version == null) {return;}
353 	  if (Constants.compareVersions(Constants.AZUREUS_VERSION, required_version) < 0) {
354 		  String plugin_name_bit = name.length() > 0 ? (name+" "):"";
355 		  String msg = "Plugin " + plugin_name_bit + "requires " + Constants.APP_NAME + " version " + required_version + " or higher";
356 		  if (alert_on_fail) {
357 			  Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR, msg));
358 		  }
359 		  throw new PluginException(msg);
360 	  }
361   }
362 
363   public static void
checkJDKVersion( String name, Properties props, boolean alert_on_fail )364   checkJDKVersion(
365 	String		name,
366 	Properties	props,
367 	boolean		alert_on_fail )
368 
369   	throws PluginException
370   {
371       String	required_jdk = (String)props.get( "plugin.jdk.min_version" );
372 
373       if ( required_jdk != null ){
374 
375     	  String	actual_jdk = Constants.JAVA_VERSION;
376 
377     	  required_jdk 	= normaliseJDK( required_jdk );
378     	  actual_jdk	= normaliseJDK( actual_jdk );
379 
380     	  if ( required_jdk.length() == 0 || actual_jdk.length() == 0 ){
381 
382     		  return;
383     	  }
384 
385     	  if ( Constants.compareVersions( actual_jdk, required_jdk ) < 0 ){
386 
387     	    	String	msg =  "Plugin " + (name.length()>0?(name+" "):"" ) + "requires Java version " + required_jdk + " or higher";
388 
389     	    	if ( alert_on_fail ){
390 
391     	    		Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR, msg));
392     	    	}
393 
394     	        throw( new PluginException( msg ));
395     	  }
396       }
397   }
398 
399   protected static String
normaliseJDK( String jdk )400   normaliseJDK(
401 	String	jdk )
402   {
403 	  try{
404 		  String	str = "";
405 
406 		  // take leading digit+. portion only
407 
408 		  for (int i=0;i<jdk.length();i++){
409 
410 			  char c = jdk.charAt( i );
411 
412 			  if ( c == '.' || Character.isDigit( c )){
413 
414 				  str += c;
415 
416 			  }else{
417 
418 				  break;
419 			  }
420 		  }
421 
422 		  	// convert 5|6|... to 1.5|1.6 etc
423 
424 		  if ( Integer.parseInt( "" + str.charAt(0)) > 1 ){
425 
426 			  str = "1." + str;
427 		  }
428 
429 		  return( str );
430 
431 	  }catch( Throwable e ){
432 
433 		  return( "" );
434 	  }
435   }
436 
437   protected
PluginInitializer( AzureusCore _azureus_core, AzureusCoreOperation _core_operation )438   PluginInitializer(
439   	AzureusCore 			_azureus_core,
440   	AzureusCoreOperation	_core_operation )
441   {
442   	azureus_core	= _azureus_core;
443 
444   	AEDiagnostics.addEvidenceGenerator( this );
445 
446   	azureus_core.addLifecycleListener(
447 	    	new AzureusCoreLifecycleAdapter()
448 			{
449 	    		public void
450 				componentCreated(
451 					AzureusCore					core,
452 					AzureusCoreComponent		comp )
453 	    		{
454 	    			if ( comp instanceof GlobalManager ){
455 
456 	    				GlobalManager	gm	= (GlobalManager)comp;
457 
458 	    				gm.addListener( PluginInitializer.this );
459 	    			}
460 	    		}
461 			});
462 
463   	core_operation 	= _core_operation;
464 
465     UpdateManagerImpl.getSingleton( azureus_core );	// initialise the update manager
466 
467     plugin_manager = PluginManagerImpl.getSingleton( this );
468 
469     String	dynamic_plugins = System.getProperty( "azureus.dynamic.plugins", null );
470 
471     if ( dynamic_plugins != null ){
472 
473     	String[]	classes = dynamic_plugins.split( ";" );
474 
475     	for ( String c: classes ){
476 
477     		try{
478     			queueRegistration( Class.forName( c ));
479 
480     		}catch( Throwable e ){
481 
482     			Debug.out( "Registration of dynamic plugin '" + c + "' failed", e );
483     		}
484     	}
485     }
486 
487     UpdaterUtils.checkBootstrapPlugins();
488   }
489 
490   protected void
fireCreated( PluginInterfaceImpl pi )491   fireCreated(
492 	PluginInterfaceImpl pi )
493   {
494 	  azureus_core.triggerLifeCycleComponentCreated( pi );
495   }
496 
497   protected void
fireOperational( PluginInterfaceImpl pi, boolean op )498   fireOperational(
499 	PluginInterfaceImpl	pi,
500 	boolean				op )
501   {
502 	  fireEventSupport( op?PluginEvent.PEV_PLUGIN_OPERATIONAL:PluginEvent.PEV_PLUGIN_NOT_OPERATIONAL, pi );
503   }
504 
505   public static void
addInitThread()506   addInitThread()
507   {
508 	  synchronized( initThreads ){
509 
510 		  if ( initThreads.contains( Thread.currentThread())){
511 
512 			  Debug.out( "Already added" );
513 		  }
514 
515 		  initThreads.add( Thread.currentThread());
516 	  }
517   }
518 
519   public static void
removeInitThread()520   removeInitThread()
521   {
522 	  synchronized( initThreads ){
523 
524 		  initThreads.remove( Thread.currentThread());
525 	  }
526   }
527 
528   public static boolean
isInitThread()529   isInitThread()
530   {
531 	  synchronized( initThreads ){
532 
533 		  return initThreads.contains(Thread.currentThread());
534 	  }
535   }
536 
537   protected boolean
isInitialisationThread()538   isInitialisationThread()
539   {
540 	  return( isInitThread());
541   }
542 
543   	public List
loadPlugins( AzureusCore core, boolean bSkipAlreadyLoaded, boolean load_external_plugins, boolean loading_for_startup, boolean initialise_plugins)544 	loadPlugins(
545 		AzureusCore		core,
546 		boolean bSkipAlreadyLoaded,
547 		boolean load_external_plugins,
548 		boolean loading_for_startup,
549 		boolean initialise_plugins)
550   	{
551   		if ( bSkipAlreadyLoaded ){
552 
553   				// discard any failed ones
554 
555   			List pis;
556 
557   			synchronized( s_plugin_interfaces ){
558 
559   				pis = new ArrayList( s_plugin_interfaces );
560   			}
561 
562   			for (int i=0;i<pis.size();i++){
563 
564   				PluginInterfaceImpl pi = (PluginInterfaceImpl)pis.get(i);
565 
566   		  		Plugin p = pi.getPlugin();
567 
568   		  		if ( p instanceof FailedPlugin ){
569 
570   		  			unloadPlugin( pi );
571   		  		}
572   		  	}
573 
574   		}
575 
576   		List pluginLoaded = new ArrayList();
577 
578   		PluginManagerImpl.setStartDetails( core );
579 
580   		getRootClassLoader();
581 
582   			// first do explicit plugins
583 
584 	    File	user_dir = FileUtil.getUserFile("plugins");
585 
586 	    File	app_dir	 = FileUtil.getApplicationFile("plugins");
587 
588 	    int	user_plugins	= 0;
589 	    int app_plugins		= 0;
590 
591 	    if ( user_dir.exists() && user_dir.isDirectory()){
592 
593 	    	user_plugins = user_dir.listFiles().length;
594 
595 	    }
596 
597 	    if ( app_dir.exists() && app_dir.isDirectory()){
598 
599 	    	app_plugins = app_dir.listFiles().length;
600 
601 	    }
602 
603 	    	// user ones first so they override app ones if present
604 
605 	    if (load_external_plugins) {
606 		    pluginLoaded.addAll(loadPluginsFromDir(user_dir, 0, user_plugins
607 					+ app_plugins, bSkipAlreadyLoaded, loading_for_startup, initialise_plugins));
608 
609 		    if ( !user_dir.equals( app_dir )){
610 
611 		    	pluginLoaded.addAll(loadPluginsFromDir(app_dir, user_plugins,
612 						user_plugins + app_plugins, bSkipAlreadyLoaded, loading_for_startup, initialise_plugins));
613 		    }
614 	    }
615 	    else {
616 	    	if (Logger.isEnabled()) {
617 	    		Logger.log(new LogEvent(LOGID, "Loading of external plugins skipped"));
618 	    	}
619 	    }
620 
621 	    if (Logger.isEnabled())
622 	    	Logger.log(new LogEvent(LOGID, "Loading built-in plugins"));
623 
624   		PluginManagerDefaults	def = PluginManager.getDefaults();
625 
626   		for (int i=0;i<builtin_plugins.length;i++){
627 
628   			if ( def.isDefaultPluginEnabled( builtin_plugins[i][0])){
629 
630   			    if (core_operation != null) {
631   			    	core_operation.reportCurrentTask(MessageText.getString("splash.plugin")
632   			    			+ builtin_plugins[i][0]);
633   			    }
634 
635   				try{
636   					loading_builtin	= true;
637 
638   						// lazyness here, for builtin we use static load method with default plugin interface
639   						// if we need to improve on this then we'll have to move to a system more akin to
640   						// the dir-loaded plugins
641 
642   					Class	cla = root_class_loader.loadClass( builtin_plugins[i][1]);
643 
644   			      	Method	load_method = cla.getMethod( "load", new Class[]{ PluginInterface.class });
645 
646   			      	load_method.invoke( null, new Object[]{ getDefaultInterfaceSupport() });
647 
648 					Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
649 							"Built-in plugin '" + builtin_plugins[i][0] + "' ok"));
650   			      }catch( NoSuchMethodException e ){
651 
652   			      }catch( Throwable e ){
653 
654 					if ( builtin_plugins[i][4].equalsIgnoreCase("true" )){
655 
656 						Debug.printStackTrace( e );
657 
658 						Logger.log(new LogAlert(LogAlert.UNREPEATABLE,
659 								"Load of built in plugin '" + builtin_plugins[i][2] + "' fails", e));
660 					}
661   				}finally{
662 
663   					loading_builtin = false;
664   				}
665   			}else{
666   				if (Logger.isEnabled())
667 						Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
668 								"Built-in plugin '" + builtin_plugins[i][2] + "' is disabled"));
669   			}
670   		}
671 
672  		if (Logger.isEnabled())
673 			Logger.log(new LogEvent(LOGID, "Loading dynamically registered plugins"));
674 
675 		for (int i=0;i<registration_queue.size();i++){
676 
677 			Object	entry = registration_queue.get(i);
678 
679 			Class	cla;
680 			String	id;
681 
682 			if ( entry instanceof Class ){
683 
684   				cla = (Class)entry;
685 
686   				id	= cla.getName();
687 			}else{
688 
689 				Object[]	x = (Object[])entry;
690 
691 				Plugin	plugin = (Plugin)x[0];
692 
693 				cla	= plugin.getClass();
694 
695 				id	= (String)x[1];
696 			}
697 
698 			try{
699 					// lazyness here, for dynamic we use static load method with default plugin interface
700 					// if we need to improve on this then we'll have to move to a system more akin to
701 					// the dir-loaded plugins
702 
703 				Method	load_method = cla.getMethod( "load", new Class[]{ PluginInterface.class });
704 
705 		      	load_method.invoke( null, new Object[]{ getDefaultInterfaceSupport() });
706 
707 		    }catch( NoSuchMethodException e ){
708 
709 		    }catch( Throwable e ){
710 
711 				Debug.printStackTrace( e );
712 
713 				Logger.log(new LogAlert(LogAlert.UNREPEATABLE,
714 						"Load of dynamic plugin '" + id + "' fails", e));
715 			}
716 		}
717 
718 		return pluginLoaded;
719   	}
720 
721   	private void
getRootClassLoader()722 	getRootClassLoader()
723   	{
724   			// first do explicit plugins
725 
726   		File	user_dir = FileUtil.getUserFile("shared");
727 
728   		getRootClassLoader( user_dir );
729 
730   		File	app_dir	 = FileUtil.getApplicationFile("shared");
731 
732   		if ( !user_dir.equals( app_dir )){
733 
734   			getRootClassLoader( app_dir );
735   		}
736   	}
737 
738  	private void
getRootClassLoader( File dir )739 	getRootClassLoader(
740 		File		dir )
741   	{
742  		dir = new File( dir, "lib" );
743 
744  		if ( dir.exists() && dir.isDirectory()){
745 
746  			File[]	files = dir.listFiles();
747 
748  			if ( files != null ){
749 
750  				files = PluginLauncherImpl.getHighestJarVersions( files, new String[]{ null }, new String[]{ null }, false );
751 
752  				for (int i=0;i<files.length;i++){
753 
754  				 	if (Logger.isEnabled())
755  						Logger.log(new LogEvent(LOGID, "Share class loader extended by " + files[i].toString()));
756 
757  				 	root_class_loader =
758  				 		PluginLauncherImpl.addFileToClassPath(
759  				 				PluginInitializer.class.getClassLoader(),
760  				 				root_class_loader, files[i] );
761  				}
762  			}
763  		}
764   	}
765 
766   private List
loadPluginsFromDir( File pluginDirectory, int plugin_offset, int plugin_total, boolean bSkipAlreadyLoaded, boolean loading_for_startup, boolean initialise)767   loadPluginsFromDir(
768   	File	pluginDirectory,
769 	int		plugin_offset,
770 	int		plugin_total,
771 	boolean bSkipAlreadyLoaded,
772 	boolean loading_for_startup,
773 	boolean initialise)
774   {
775   	List dirLoadedPIs = new ArrayList();
776 
777   	if (Logger.isEnabled())
778 			Logger.log(new LogEvent(LOGID, "Plugin Directory is " + pluginDirectory));
779 
780     if ( !pluginDirectory.exists() ){
781 
782     	FileUtil.mkdirs(pluginDirectory);
783     }
784 
785     if( pluginDirectory.isDirectory()){
786 
787 	    File[] pluginsDirectory = pluginDirectory.listFiles();
788 
789 	    for(int i = 0 ; i < pluginsDirectory.length ; i++) {
790 
791         if( pluginsDirectory[i].getName().equals( "CVS" ) ) {
792 
793         	if (Logger.isEnabled())
794 						Logger.log(new LogEvent(LOGID, "Skipping plugin "
795 								+ pluginsDirectory[i].getName()));
796 
797           continue;
798         }
799 
800 
801       if (Logger.isEnabled())
802 				Logger.log(new LogEvent(LOGID, "Loading plugin "
803 						+ pluginsDirectory[i].getName()));
804 
805 	    if(core_operation != null) {
806 
807 	    	core_operation.reportCurrentTask(MessageText.getString("splash.plugin") + pluginsDirectory[i].getName());
808 	    }
809 
810 	    try{
811 
812 	    	List	loaded_pis = loadPluginFromDir(pluginsDirectory[i], bSkipAlreadyLoaded, loading_for_startup, initialise);
813 
814 	    		// save details for later initialisation
815 
816 	    	loaded_pi_list.add( loaded_pis );
817 	    	dirLoadedPIs.addAll( loaded_pis );
818 
819 	      }catch( PluginException e ){
820 
821 	      		// already handled
822 	      }
823 
824 	      if( core_operation != null ){
825 
826 	    	  core_operation.reportPercent( (100 * (i + plugin_offset)) / plugin_total );
827 	      }
828 	    }
829     }
830     return dirLoadedPIs;
831   }
832 
833   private List
loadPluginFromDir( File directory, boolean bSkipAlreadyLoaded, boolean loading_for_startup, boolean initialise)834   loadPluginFromDir(
835 		  File directory,
836 		  boolean bSkipAlreadyLoaded,
837 		  boolean loading_for_startup,
838 		  boolean initialise) // initialise setting is used if loading_for_startup isnt
839 
840   throws PluginException
841   {
842 	  List	loaded_pis = new ArrayList();
843 
844 	  ClassLoader plugin_class_loader = root_class_loader;
845 
846 	  if( !directory.isDirectory()){
847 
848 		  return( loaded_pis );
849 	  }
850 
851 	  String pluginName = directory.getName();
852 
853 	  File[] pluginContents = directory.listFiles();
854 
855 	  if ( pluginContents == null || pluginContents.length == 0){
856 
857 		  return( loaded_pis );
858 	  }
859 
860 	  // first sanity check - dir must include either a plugin.properties or
861 	  // at least one .jar file
862 
863 	  boolean	looks_like_plugin	= false;
864 
865 	  for (int i=0;i<pluginContents.length;i++){
866 
867 		  String	name = pluginContents[i].getName().toLowerCase();
868 
869 		  if ( name.endsWith( ".jar") || name.equals( "plugin.properties" )){
870 
871 			  looks_like_plugin = true;
872 
873 			  break;
874 		  }
875 	  }
876 
877 	  if ( !looks_like_plugin ){
878 
879 		  if (Logger.isEnabled())
880 			  Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
881 					  "Plugin directory '" + directory + "' has no plugin.properties "
882 					  + "or .jar files, skipping"));
883 
884 		  return( loaded_pis );
885 	  }
886 
887 	  // take only the highest version numbers of jars that look versioned
888 
889 	  String[]	plugin_version = {null};
890 	  String[]	plugin_id = {null};
891 
892 	  pluginContents	= PluginLauncherImpl.getHighestJarVersions( pluginContents, plugin_version, plugin_id, true );
893 
894 	  for( int i = 0 ; i < pluginContents.length ; i++){
895 
896 		  File	jar_file = pluginContents[i];
897 
898 		  // migration hack for i18nAZ_1.0.jar
899 
900 		  if ( pluginContents.length > 1 ){
901 
902 			  String	name = jar_file.getName();
903 
904 			  if ( name.startsWith( "i18nPlugin_" )){
905 
906 				  // non-versioned version still there, rename it
907 
908 				  if (Logger.isEnabled())
909 					  Logger.log(new LogEvent(LOGID, "renaming '" + name
910 							  + "' to conform with versioning system"));
911 
912 				  jar_file.renameTo( new File( jar_file.getParent(), "i18nAZ_0.1.jar  " ));
913 
914 				  continue;
915 			  }
916 		  }
917 
918 		  plugin_class_loader = PluginLauncherImpl.addFileToClassPath( root_class_loader, plugin_class_loader, jar_file);
919 	  }
920 
921 	  String plugin_class_string = null;
922 
923 	  try {
924 		  Properties props = new Properties();
925 
926 		  File	properties_file = new File(directory.toString() + File.separator + "plugin.properties");
927 
928 		  try {
929 
930 			  // if properties file exists on its own then override any properties file
931 			  // potentially held within a jar
932 
933 			  if ( properties_file.exists()){
934 
935 				  FileInputStream	fis = null;
936 
937 				  try{
938 					  fis = new FileInputStream( properties_file );
939 
940 					  props.load( fis );
941 
942 				  }finally{
943 
944 					  if ( fis != null ){
945 
946 						  fis.close();
947 					  }
948 				  }
949 
950 			  }else{
951 
952 				  if ( plugin_class_loader instanceof URLClassLoader ){
953 
954 					  URLClassLoader	current = (URLClassLoader)plugin_class_loader;
955 
956 					  URL url = current.findResource("plugin.properties");
957 
958 					  if ( url != null ){
959 						  URLConnection connection = url.openConnection();
960 
961 						  InputStream is = connection.getInputStream();
962 
963 						  props.load(is);
964 
965 					  }else{
966 
967 						  throw( new Exception( "failed to load plugin.properties from jars"));
968 					  }
969 				  }else{
970 
971 					  throw( new Exception( "failed to load plugin.properties from dir or jars"));
972 
973 				  }
974 			  }
975 		  }catch( Throwable e ){
976 
977 			  Debug.printStackTrace( e );
978 
979 			  String	msg =  "Can't read 'plugin.properties' for plugin '" + pluginName + "': file may be missing";
980 
981 			  Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg));
982 
983 			  System.out.println( msg );
984 
985 			  throw( new PluginException( msg, e ));
986 		  }
987 
988 		  checkJDKVersion( pluginName, props, true );
989 		  checkAzureusVersion(pluginName, props, true);
990 
991 		  plugin_class_string = (String)props.get( "plugin.class");
992 
993 		  if ( plugin_class_string == null ){
994 
995 			  plugin_class_string = (String)props.get( "plugin.classes");
996 
997 			  if ( plugin_class_string == null ){
998 
999 				  // set so we don't bork later will npe
1000 
1001 				  plugin_class_string = "";
1002 			  }
1003 		  }
1004 
1005 		  String	plugin_name_string = (String)props.get( "plugin.name");
1006 
1007 		  if ( plugin_name_string == null ){
1008 
1009 			  plugin_name_string = (String)props.get( "plugin.names");
1010 		  }
1011 
1012 		  int	pos1 = 0;
1013 		  int	pos2 = 0;
1014 
1015 		  while(true){
1016 			  int	p1 = plugin_class_string.indexOf( ";", pos1 );
1017 
1018 			  String	plugin_class;
1019 
1020 			  if ( p1 == -1 ){
1021 				  plugin_class = plugin_class_string.substring(pos1).trim();
1022 			  }else{
1023 				  plugin_class	= plugin_class_string.substring(pos1,p1).trim();
1024 				  pos1 = p1+1;
1025 			  }
1026 
1027 			  PluginInterfaceImpl existing_pi = getPluginFromClass( plugin_class );
1028 
1029 			  if ( existing_pi != null ){
1030 
1031 				  if (bSkipAlreadyLoaded) {
1032 					  break;
1033 				  }
1034 
1035 				  // allow user dir entries to override app dir entries without warning
1036 
1037 				  File	this_parent 	= directory.getParentFile();
1038 				  File	existing_parent = null;
1039 
1040 				  if ( existing_pi.getInitializerKey() instanceof File ){
1041 
1042 					  existing_parent	= ((File)existing_pi.getInitializerKey()).getParentFile();
1043 				  }
1044 
1045 				  if ( 	this_parent.equals( FileUtil.getApplicationFile("plugins")) &&
1046 						  existing_parent	!= null &&
1047 						  existing_parent.equals( FileUtil.getUserFile( "plugins" ))){
1048 
1049 					  // skip this overridden plugin
1050 
1051 					  if (Logger.isEnabled())
1052 						  Logger.log(new LogEvent(LOGID, "Plugin '" + plugin_name_string
1053 								  + "/" + plugin_class
1054 								  + ": shared version overridden by user-specific one"));
1055 
1056 					  return( new ArrayList());
1057 
1058 				  }else{
1059 					  Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING,
1060 							  "Error loading '" + plugin_name_string + "', plugin class '"
1061 							  + plugin_class + "' is already loaded"));
1062 				  }
1063 
1064 			  }else{
1065 
1066 				  String	plugin_name = null;
1067 
1068 				  if ( plugin_name_string != null ){
1069 
1070 					  int	p2 = plugin_name_string.indexOf( ";", pos2 );
1071 
1072 
1073 					  if ( p2 == -1 ){
1074 						  plugin_name = plugin_name_string.substring(pos2).trim();
1075 					  }else{
1076 						  plugin_name	= plugin_name_string.substring(pos2,p2).trim();
1077 						  pos2 = p2+1;
1078 					  }
1079 				  }
1080 
1081 				  Properties new_props = (Properties)props.clone();
1082 
1083 				  for (int j=0;j<default_version_details.length;j++){
1084 
1085 					  if ( plugin_class.equals( default_version_details[j][0] )){
1086 
1087 						  if ( new_props.get( "plugin.id") == null ){
1088 
1089 							  new_props.put( "plugin.id", default_version_details[j][1]);
1090 						  }
1091 
1092 						  if ( plugin_name == null ){
1093 
1094 							  plugin_name	= default_version_details[j][2];
1095 						  }
1096 
1097 						  if ( new_props.get( "plugin.version") == null ){
1098 
1099 							  // no explicit version. If we've derived one then use that, otherwise defaults
1100 
1101 							  if ( plugin_version[0] != null ){
1102 
1103 								  new_props.put( "plugin.version", plugin_version[0]);
1104 
1105 							  }else{
1106 
1107 								  new_props.put( "plugin.version", default_version_details[j][3]);
1108 							  }
1109 						  }
1110 					  }
1111 				  }
1112 
1113 				  new_props.put( "plugin.class", plugin_class );
1114 
1115 				  if ( plugin_name != null ){
1116 
1117 					  new_props.put( "plugin.name", plugin_name );
1118 				  }
1119 
1120 				  // System.out.println( "loading plugin '" + plugin_class + "' using cl " + classLoader);
1121 
1122 				  // if the plugin load fails we still need to generate a plugin entry
1123 				  // as this drives the upgrade process
1124 
1125 
1126 				  Throwable	load_failure	= null;
1127 
1128 				  String pid = plugin_id[0]==null?directory.getName():plugin_id[0];
1129 
1130 				  List<File>	verified_files = null;
1131 
1132 				  Plugin plugin = null;
1133 
1134 				  if ( vc_disabled_plugins.contains ( pid )){
1135 
1136 					  log( "Plugin '" + pid + "' has been administratively disabled" );
1137 
1138 				  }else{
1139 					  try{
1140 						  String cl_key = "plugin.cl.ext." + pid;
1141 
1142 						  String str = COConfigurationManager.getStringParameter( cl_key, null );
1143 
1144 						  if ( str != null && str.length() > 0 ){
1145 
1146 							  COConfigurationManager.removeParameter( cl_key );
1147 
1148 							  plugin_class_loader = PluginLauncherImpl.extendClassLoader( root_class_loader, plugin_class_loader, new URL( str ));
1149 						  }
1150 					  }catch( Throwable e ){
1151 					  }
1152 
1153 					  if ( pid.endsWith( "_v" )){
1154 
1155 						  verified_files = new ArrayList<File>();
1156 
1157 						  // re-verify jar files
1158 
1159 						  log( "Re-verifying " + pid );
1160 
1161 						  for( int i = 0 ; i < pluginContents.length ; i++){
1162 
1163 							  File	jar_file = pluginContents[i];
1164 
1165 							  if ( jar_file.getName().endsWith( ".jar" )){
1166 
1167 								  try{
1168 									  log( "    verifying " + jar_file );
1169 
1170 									  AEVerifier.verifyData( jar_file );
1171 
1172 									  verified_files.add( jar_file );
1173 
1174 									  log( "    OK" );
1175 
1176 								  }catch( Throwable e ){
1177 
1178 									  String	msg = "Error loading plugin '" + pluginName + "' / '" + plugin_class_string + "'";
1179 
1180 									  Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e));
1181 
1182 									  plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath());
1183 								  }
1184 							  }
1185 						  }
1186 					  }
1187 
1188 					  if ( plugin == null ){
1189 
1190 						  plugin = PluginLauncherImpl.getPreloadedPlugin( plugin_class );
1191 
1192 						  if ( plugin == null ){
1193 
1194 							  try{
1195 								  try{
1196 									  Class<Plugin> c = (Class<Plugin>)PlatformManagerFactory.getPlatformManager().loadClass( plugin_class_loader, plugin_class );
1197 
1198 
1199 									  //Class c = plugin_class_loader.loadClass(plugin_class);
1200 
1201 									  plugin	= c.newInstance();
1202 
1203 									  try{
1204 										  // kick off any pre-inits
1205 
1206 										  if ( plugin_class_loader instanceof URLClassLoader ){
1207 
1208 											  URL[] urls = ((URLClassLoader)plugin_class_loader).getURLs();
1209 
1210 											  for ( URL u: urls ){
1211 
1212 												  String path = u.getPath();
1213 
1214 												  if ( path.endsWith( ".jar" )){
1215 
1216 													  int	s1 = path.lastIndexOf( '/' );
1217 													  int	s2 = path.lastIndexOf( '\\' );
1218 
1219 													  path = path.substring( Math.max( s1, s2 )+1);
1220 
1221 													  s2 = path.indexOf( '_' );
1222 
1223 													  if ( s2 > 0 ){
1224 
1225 														  path = path.substring( 0, s2 );
1226 
1227 														  path = path.replaceAll( "-", "" );
1228 
1229 														  String cl = "plugin.preinit." + pid + ".PI" + path;
1230 
1231 														  try{
1232 															  Class pic = plugin_class_loader.loadClass( cl );
1233 
1234 															  if ( pic != null ){
1235 
1236 																  pic.newInstance();
1237 															  }
1238 														  }catch( Throwable e ){
1239 														  }
1240 													  }
1241 												  }
1242 											  }
1243 										  }
1244 									  }catch( Throwable e ){
1245 									  }
1246 								  }catch( PlatformManagerException e ){
1247 
1248 									  throw( e.getCause());
1249 								  }
1250 
1251 							  }catch (java.lang.UnsupportedClassVersionError e) {
1252 								  plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath());
1253 
1254 								  // shorten stack trace
1255 								  load_failure	= new UnsupportedClassVersionError(e.getMessage());
1256 
1257 							  }catch( Throwable e ){
1258 
1259 								  if ( 	e instanceof ClassNotFoundException &&
1260 										  props.getProperty( "plugin.install_if_missing", "no" ).equalsIgnoreCase( "yes" )){
1261 
1262 									  // don't report the failure
1263 
1264 								  }else{
1265 
1266 									  load_failure	= e;
1267 								  }
1268 
1269 								  plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath());
1270 							  }
1271 						  }else{
1272 
1273 							  plugin_class_loader = plugin.getClass().getClassLoader();
1274 						  }
1275 					  }
1276 
1277 					  MessageText.integratePluginMessages((String)props.get("plugin.langfile"),plugin_class_loader);
1278 
1279 					  PluginInterfaceImpl plugin_interface =
1280 						  new PluginInterfaceImpl(
1281 								  plugin,
1282 								  this,
1283 								  directory,
1284 								  plugin_class_loader,
1285 								  verified_files,
1286 								  directory.getName(),	// key for config values
1287 								  new_props,
1288 								  directory.getAbsolutePath(),
1289 								  pid,
1290 								  plugin_version[0] );
1291 
1292 					  boolean bEnabled = (loading_for_startup) ? plugin_interface.getPluginState().isLoadedAtStartup() : initialise;
1293 
1294 					  plugin_interface.getPluginState().setDisabled(!bEnabled);
1295 
1296 					  try{
1297 
1298 						  Method	load_method = plugin.getClass().getMethod( "load", new Class[]{ PluginInterface.class });
1299 
1300 						  load_method.invoke( plugin, new Object[]{ plugin_interface });
1301 
1302 					  }catch( NoSuchMethodException e ){
1303 
1304 					  }catch( Throwable e ){
1305 
1306 						  load_failure	= e;
1307 					  }
1308 
1309 					  loaded_pis.add( plugin_interface );
1310 
1311 					  if ( load_failure != null ){
1312 
1313 						  plugin_interface.setAsFailed();
1314 
1315 						  	// don't complain about our internal one
1316 
1317 						  if ( !pid.equals(UpdaterUpdateChecker.getPluginID())){
1318 
1319 							  String msg =
1320 								  MessageText.getString("plugin.init.load.failed",
1321 									new String[]{
1322 										  plugin_name==null?pluginName:plugin_name,
1323 										  directory.getAbsolutePath()
1324 							  		});
1325 
1326 							  LogAlert la;
1327 
1328 							  if ( load_failure instanceof UnsupportedClassVersionError ){
1329 
1330 								  la = new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg + ".\n\n" + MessageText.getString("plugin.install.class_version_error"));
1331 
1332 							  }else if ( load_failure instanceof ClassNotFoundException ){
1333 
1334 								  la = new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg + ".\n\n" + MessageText.getString("plugin.init.load.failed.classmissing") + "\n\n", load_failure );
1335 
1336 							  }else{
1337 
1338 								  la = new LogAlert(LogAlert.UNREPEATABLE, msg, load_failure);
1339 							  }
1340 
1341 							  Logger.log( la );
1342 
1343 							  System.out.println( msg + ": " + load_failure);
1344 						  }
1345 					  }
1346 				  }
1347 			  }
1348 			  if ( p1 == -1 ){
1349 				  break;
1350 
1351 			  }
1352 		  }
1353 
1354 		  return( loaded_pis );
1355 
1356 	  }catch(Throwable e) {
1357 
1358 		  if ( e instanceof PluginException ){
1359 
1360 			  throw((PluginException)e);
1361 		  }
1362 
1363 		  Debug.printStackTrace( e );
1364 
1365 		  String	msg = "Error loading plugin '" + pluginName + "' / '" + plugin_class_string + "'";
1366 
1367 		  Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e));
1368 
1369 		  System.out.println( msg + ": " + e);
1370 
1371 		  throw( new PluginException( msg, e ));
1372 	  }
1373   }
1374 
1375   private void
log( String str )1376   log(
1377 	String	str )
1378   {
1379 	if (Logger.isEnabled()){
1380 		Logger.log(new LogEvent(LOGID, str ));
1381 	}
1382   }
1383 
1384   public void
initialisePlugins()1385   initialisePlugins()
1386   {
1387 	  try{
1388 		  addInitThread();
1389 
1390 		  final LinkedList<Runnable> initQueue = new LinkedList<Runnable>();
1391 
1392 			for (int i = 0; i < loaded_pi_list.size(); i++) {
1393 				final int idx = i;
1394 				initQueue.add(new Runnable() {
1395 					public void run() {
1396 						try {
1397 							List l = (List) loaded_pi_list.get(idx);
1398 
1399 							if (l.size() > 0) {
1400 								PluginInterfaceImpl plugin_interface = (PluginInterfaceImpl) l.get(0);
1401 
1402 								if (Logger.isEnabled())
1403 									Logger.log(new LogEvent(LOGID, "Initializing plugin '"
1404 											+ plugin_interface.getPluginName() + "'"));
1405 
1406 								if (core_operation != null) {
1407 									core_operation.reportCurrentTask(MessageText
1408 											.getString("splash.plugin.init")
1409 											+ " " + plugin_interface.getPluginName());
1410 								}
1411 
1412 								initialisePlugin(l);
1413 
1414 								if (Logger.isEnabled())
1415 									Logger.log(new LogEvent(LOGID, "Initialization of plugin '"
1416 											+ plugin_interface.getPluginName() + "' complete"));
1417 
1418 							}
1419 
1420 						} catch ( Throwable e ){
1421 							// already handled
1422 						} finally {
1423 							if (core_operation != null) {
1424 								core_operation.reportPercent((100 * (idx + 1)) / loaded_pi_list.size());
1425 							}
1426 						}
1427 
1428 						// some plugins try and steal the logger stdout redirects.
1429 						// re-establish them if needed
1430 						Logger.doRedirects();
1431 					}
1432 				});
1433 			}
1434 
1435 
1436 
1437 			// now do built in ones
1438 
1439 			initQueue.add(new Runnable() {
1440 				public void run() {
1441 					if (Logger.isEnabled())
1442 						Logger.log(new LogEvent(LOGID, "Initializing built-in plugins"));
1443 				}
1444 			});
1445 
1446 
1447 
1448 			final PluginManagerDefaults def = PluginManager.getDefaults();
1449 
1450 			for (int i = 0; i < builtin_plugins.length; i++) {
1451 
1452 				final int idx = i;
1453 
1454 				initQueue.add(new Runnable() {
1455 					public void run() {
1456 						if (def.isDefaultPluginEnabled(builtin_plugins[idx][0])) {
1457 							String id = builtin_plugins[idx][2];
1458 							String key = builtin_plugins[idx][3];
1459 
1460 							try {
1461 								Class cla = root_class_loader.loadClass(
1462 										builtin_plugins[idx][1]);
1463 
1464 								if (Logger.isEnabled())
1465 									Logger.log(new LogEvent(LOGID, "Initializing built-in plugin '"
1466 											+ builtin_plugins[idx][2] + "'" ));
1467 
1468 								initializePluginFromClass(cla, id, key, "true".equals(builtin_plugins[idx][5]), true, true);
1469 
1470 								if (Logger.isEnabled())
1471 								Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
1472 										"Initialization of built in plugin '" + builtin_plugins[idx][2] + "' complete"));
1473 							} catch (Throwable e) {
1474 								try {
1475 									// replace it with a "broken" plugin instance
1476 									initializePluginFromClass(FailedPlugin.class, id, key, false, false, true);
1477 
1478 								} catch (Throwable f) {
1479 								}
1480 
1481 								if (builtin_plugins[idx][4].equalsIgnoreCase("true")) {
1482 									Debug.printStackTrace(e);
1483 									Logger.log(new LogAlert(LogAlert.UNREPEATABLE,
1484 											"Initialization of built in plugin '" + builtin_plugins[idx][2]
1485 													+ "' fails", e));
1486 								}
1487 							}
1488 						} else {
1489 							if (Logger.isEnabled())
1490 								Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
1491 										"Built-in plugin '" + builtin_plugins[idx][2] + "' is disabled"));
1492 						}
1493 
1494 					}
1495 				});
1496 
1497 			}
1498 
1499 			initQueue.add(new Runnable() {
1500 				public void run() {
1501 					if (Logger.isEnabled())
1502 						Logger.log(new LogEvent(LOGID,
1503 								"Initializing dynamically registered plugins"));
1504 				}
1505 			});
1506 
1507 			for (int i = 0; i < registration_queue.size(); i++) {
1508 
1509 				final int idx = i;
1510 
1511 				initQueue.add(new Runnable() {
1512 					public void run() {
1513 						try {
1514 							Object entry = registration_queue.get(idx);
1515 
1516 							if (entry instanceof Class) {
1517 
1518 								Class cla = (Class) entry;
1519 
1520 								singleton.initializePluginFromClass(cla, INTERNAL_PLUGIN_ID, cla
1521 										.getName(), false, true, true);
1522 
1523 							} else {
1524 								Object[] x = (Object[]) entry;
1525 
1526 								Plugin plugin = (Plugin) x[0];
1527 
1528 								singleton.initializePluginFromInstance(plugin, (String) x[1], (String)x[2]);
1529 							}
1530 						} catch (PluginException e) {
1531 						}
1532 					}
1533 				});
1534 			}
1535 
1536 			AEThread2 secondaryInitializer =
1537 				new AEThread2("2nd PluginInitializer Thread",true)
1538 				{
1539 					public void run()
1540 					{
1541 
1542 						try{
1543 							addInitThread();
1544 
1545 							while( true ){
1546 
1547 								Runnable toRun;
1548 
1549 								synchronized (initQueue){
1550 
1551 									if (initQueue.isEmpty()){
1552 
1553 										break;
1554 									}
1555 
1556 									toRun = (Runnable)initQueue.remove(0);
1557 								}
1558 
1559 								try{
1560 									toRun.run();
1561 
1562 								}catch( Throwable e ){
1563 
1564 									Debug.out(e);
1565 								}
1566 							}
1567 						}finally{
1568 
1569 							removeInitThread();
1570 						}
1571 					}
1572 				};
1573 			secondaryInitializer.start();
1574 
1575 			while(true){
1576 
1577 				Runnable toRun;
1578 
1579 				synchronized( initQueue ){
1580 
1581 					if( initQueue.isEmpty()){
1582 
1583 						break;
1584 					}
1585 
1586 					toRun = (Runnable)initQueue.remove(0);
1587 				}
1588 
1589 				try{
1590 					toRun.run();
1591 
1592 				}catch( Throwable e ){
1593 
1594 					Debug.out(e);
1595 				}
1596 			}
1597 
1598 			secondaryInitializer.join();
1599 
1600 			registration_queue.clear();
1601 
1602 			plugins_initialised = true;
1603 
1604 			fireEvent( PluginEvent.PEV_ALL_PLUGINS_INITIALISED );
1605 
1606 	  }finally{
1607 
1608 		  removeInitThread();
1609 	  }
1610 	}
1611 
1612   	protected void
checkPluginsInitialised()1613   	checkPluginsInitialised()
1614   	{
1615   		if ( !plugins_initialised ){
1616 
1617   			Debug.out( "Wait until plugin initialisation is complete until doing this!" );
1618   		}
1619   	}
1620 
1621   	protected boolean
isInitialized()1622 	isInitialized()
1623   	{
1624   		return( plugins_initialised );
1625   	}
1626 
1627   	private void
initialisePlugin( List l )1628 	initialisePlugin(
1629 		List	l )
1630 
1631   		throws PluginException
1632   	{
1633   		PluginException	last_load_failure = null;
1634 
1635   		for (int i=0;i<l.size();i++){
1636 
1637   			final PluginInterfaceImpl	plugin_interface = (PluginInterfaceImpl)l.get(i);
1638 
1639   			if (plugin_interface.getPluginState().isDisabled()) {
1640 
1641   				synchronized( s_plugin_interfaces ){
1642 
1643   					s_plugin_interfaces.add( plugin_interface );
1644   				}
1645 
1646   				continue;
1647   			}
1648 
1649   			if ( plugin_interface.getPluginState().isOperational()){
1650 
1651   				continue;
1652   			}
1653 
1654   			Throwable	load_failure = null;
1655 
1656  			final Plugin	plugin = plugin_interface.getPlugin();
1657 
1658   			try{
1659 
1660   				UtilitiesImpl.callWithPluginThreadContext(
1661   					plugin_interface,
1662   					new runnableWithException<PluginException>()
1663   					{
1664   						public void
1665   						run()
1666   							throws PluginException
1667   						{
1668   							fireCreated( plugin_interface );
1669 
1670   							plugin.initialize(plugin_interface);
1671 
1672   							if (!(plugin instanceof FailedPlugin)){
1673 
1674   								plugin_interface.getPluginStateImpl().setOperational( true, false );
1675   							}
1676   						}
1677   					});
1678 
1679   			}catch( Throwable e ){
1680 
1681   				load_failure	= e;
1682   			}
1683 
1684   			synchronized( s_plugin_interfaces ){
1685 
1686   				s_plugins.add( plugin );
1687 
1688   				s_plugin_interfaces.add( plugin_interface );
1689   			}
1690 
1691   			if ( load_failure != null ){
1692 
1693   				Debug.printStackTrace( load_failure );
1694 
1695   				String	msg = "Error initializing plugin '" + plugin_interface.getPluginName() + "'";
1696 
1697   				Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, load_failure));
1698 
1699   				System.out.println( msg + " : " + load_failure);
1700 
1701   				last_load_failure = new PluginException( msg, load_failure );
1702   			}
1703   		}
1704 
1705   		if ( last_load_failure != null ){
1706 
1707   			throw( last_load_failure );
1708   		}
1709   	}
1710 
1711   protected void
initializePluginFromClass( final Class plugin_class, final String plugin_id, String plugin_config_key, boolean force_enabled, boolean loading_for_startup, boolean initialise)1712   initializePluginFromClass(
1713   	final Class 	plugin_class,
1714 	final String	plugin_id,
1715 	String			plugin_config_key,
1716 	boolean 		force_enabled,
1717 	boolean 		loading_for_startup,
1718 	boolean 		initialise)
1719 
1720   	throws PluginException
1721   {
1722 
1723   	if ( plugin_class != FailedPlugin.class && getPluginFromClass( plugin_class ) != null ){
1724 
1725 
1726   		Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING,
1727 					"Error loading '" + plugin_id + "', plugin class '"
1728 							+ plugin_class.getName() + "' is already loaded"));
1729 
1730   		return;
1731   	}
1732 
1733   	try{
1734   		final Plugin plugin = (Plugin) plugin_class.newInstance();
1735 
1736   		String	plugin_name;
1737 
1738   		if ( plugin_config_key.length() == 0 ){
1739 
1740   			plugin_name = plugin_class.getName();
1741 
1742   			int	pos = plugin_name.lastIndexOf(".");
1743 
1744   			if ( pos != -1 ){
1745 
1746   				plugin_name = plugin_name.substring( pos+1 );
1747 
1748   			}
1749   		}else{
1750 
1751   			plugin_name = plugin_config_key;
1752   		}
1753 
1754   		Properties properties = new Properties();
1755 
1756   			// default plugin name
1757 
1758   		properties.put( "plugin.name", plugin_name );
1759 
1760   		final PluginInterfaceImpl plugin_interface =
1761   			new PluginInterfaceImpl(
1762   						plugin,
1763 						this,
1764 						plugin_class,
1765 						plugin_class.getClassLoader(),
1766 						null,
1767 						plugin_config_key,
1768 						properties,
1769 						"",
1770 						plugin_id,
1771 						null );
1772 
1773   		boolean bEnabled = (loading_for_startup) ? plugin_interface.getPluginState().isLoadedAtStartup() : initialise;
1774 
1775 	      /**
1776 	       * For some plugins, override any config setting which disables the plugin.
1777 	       */
1778 	      if (force_enabled && !bEnabled) {
1779 	    	  plugin_interface.getPluginState().setLoadedAtStartup(true);
1780 	    	  bEnabled = true;
1781 	    	  Logger.log(new LogAlert(false, LogAlert.AT_WARNING, MessageText.getString(
1782 	    	      "plugins.init.force_enabled", new String[] {plugin_id}
1783 	    	  )));
1784 	      }
1785 
1786 	      plugin_interface.getPluginState().setDisabled(!bEnabled);
1787 
1788 	      final boolean f_enabled = bEnabled;
1789 
1790 	      UtilitiesImpl.callWithPluginThreadContext(
1791 	    		 plugin_interface,
1792 	    		 new runnableWithException<PluginException>()
1793 	    		 {
1794 	    			 public void
1795 	    			 run()
1796 	    			 	throws PluginException
1797 	    			 {
1798 	    				 try{
1799 
1800 	    					 Method	load_method = plugin_class.getMethod( "load", new Class[]{ PluginInterface.class });
1801 
1802 	    					 load_method.invoke( plugin, new Object[]{ plugin_interface });
1803 
1804 	    				 }catch( NoSuchMethodException e ){
1805 
1806 	    				 }catch( Throwable e ){
1807 
1808 	    					 Debug.printStackTrace( e );
1809 
1810 	    					 Logger.log(new LogAlert(LogAlert.UNREPEATABLE,
1811 	    							 "Load of built in plugin '" + plugin_id + "' fails", e));
1812 	    				 }
1813 
1814 	    				 if (f_enabled) {
1815 
1816 	    					 if ( core_operation != null ){
1817 
1818 	    						 core_operation.reportCurrentTask(MessageText.getString("splash.plugin.init") + " " + plugin_interface.getPluginName());
1819 	    					 }
1820 
1821 	    					 fireCreated( plugin_interface );
1822 
1823 	    					 plugin.initialize(plugin_interface);
1824 
1825 	    					 if (!(plugin instanceof FailedPlugin)){
1826 
1827 	    						 plugin_interface.getPluginStateImpl().setOperational( true, false );
1828 	    					 }
1829 	    				 }
1830 	    			 }
1831 	    		 });
1832 
1833 		 synchronized( s_plugin_interfaces ){
1834 
1835 			s_plugins.add( plugin );
1836 
1837 			s_plugin_interfaces.add( plugin_interface );
1838 		 }
1839   	}catch(Throwable e){
1840 
1841   		Debug.printStackTrace( e );
1842 
1843   		String	msg = "Error loading internal plugin '" + plugin_class.getName() + "'";
1844 
1845   		Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e));
1846 
1847   		System.out.println(msg + " : " + e);
1848 
1849   		throw( new PluginException( msg, e ));
1850   	}
1851   }
1852 
1853   protected void
initializePluginFromInstance( final Plugin plugin, String plugin_id, String plugin_config_key )1854   initializePluginFromInstance(
1855   	final Plugin		plugin,
1856 	String				plugin_id,
1857 	String				plugin_config_key )
1858 
1859   	throws PluginException
1860   {
1861   	try{
1862   		final PluginInterfaceImpl plugin_interface =
1863   			new PluginInterfaceImpl(
1864   						plugin,
1865 						this,
1866 						plugin.getClass(),
1867 						plugin.getClass().getClassLoader(),
1868 						null,
1869 						plugin_config_key,
1870 						new Properties(),
1871 						"",
1872 						plugin_id,
1873 						null );
1874 
1875 		UtilitiesImpl.callWithPluginThreadContext(
1876 			plugin_interface,
1877 			new UtilitiesImpl.runnableWithException<PluginException>()
1878 			{
1879 				public void
1880 				run()
1881 
1882 					throws PluginException
1883 				{
1884 					fireCreated( plugin_interface );
1885 
1886 			  		plugin.initialize(plugin_interface);
1887 
1888 			  		if (!(plugin instanceof FailedPlugin)){
1889 
1890 			  			plugin_interface.getPluginStateImpl().setOperational( true, false );
1891 			  		}
1892 				}
1893 			});
1894 
1895   		synchronized( s_plugin_interfaces ){
1896 
1897   			s_plugins.add( plugin );
1898 
1899   			s_plugin_interfaces.add( plugin_interface );
1900   		}
1901   	}catch(Throwable e){
1902 
1903   		Debug.printStackTrace( e );
1904 
1905   		String	msg = "Error loading internal plugin '" + plugin.getClass().getName() + "'";
1906 
1907   		Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e));
1908 
1909   		System.out.println(msg + " : " + e);
1910 
1911   		throw( new PluginException( msg, e ));
1912   	}
1913   }
1914 
1915   protected void
unloadPlugin( PluginInterfaceImpl pi )1916   unloadPlugin(
1917   	PluginInterfaceImpl		pi )
1918   {
1919 	synchronized( s_plugin_interfaces ){
1920 
1921 		s_plugins.remove( pi.getPlugin());
1922 
1923 		s_plugin_interfaces.remove( pi );
1924 	}
1925 
1926   	pi.unloadSupport();
1927 
1928   	for (int i=0;i<loaded_pi_list.size();i++){
1929 
1930   		List	l = (List)loaded_pi_list.get(i);
1931 
1932   		if ( l.remove(pi)){
1933 
1934   			if ( l.size() == 0 ){
1935 
1936   				loaded_pi_list.remove(i);
1937   			}
1938 
1939   			break;
1940   		}
1941   	}
1942 
1943   	verified_plugin_holder.removeValue( pi );
1944   }
1945 
reloadPlugin(PluginInterfaceImpl pi)1946   protected void reloadPlugin(PluginInterfaceImpl pi) throws PluginException {
1947 	  reloadPlugin(pi, false, true);
1948   }
1949 
reloadPlugin( PluginInterfaceImpl pi, boolean loading_for_startup, boolean initialise)1950 	protected void reloadPlugin(
1951 			PluginInterfaceImpl pi,
1952 			boolean loading_for_startup,
1953 			boolean initialise) throws PluginException {
1954 
1955 	  unloadPlugin( pi );
1956 
1957 	  Object key 			= pi.getInitializerKey();
1958 	  String config_key	= pi.getPluginConfigKey();
1959 
1960 	  if ( key instanceof File ){
1961 
1962   		List	pis = loadPluginFromDir( (File)key, false, loading_for_startup, initialise);
1963 
1964   		initialisePlugin( pis );
1965 
1966 	  }else{
1967   		initializePluginFromClass( (Class) key, pi.getPluginID(), config_key, false, loading_for_startup, initialise );
1968 	  }
1969 	}
1970 
1971   protected AzureusCore
getAzureusCore()1972   getAzureusCore()
1973   {
1974   	return( azureus_core );
1975   }
1976 
1977   protected GlobalManager
getGlobalManager()1978   getGlobalManager()
1979   {
1980   	return( azureus_core.getGlobalManager() );
1981   }
1982 
1983 	public static PluginInterface
getDefaultInterface()1984 	getDefaultInterface()
1985 	{
1986   	if (singleton == null) {
1987   		throw new AzureusCoreException(
1988 					"PluginInitializer not instantiated by AzureusCore.create yet");
1989   	}
1990   	return( singleton.getDefaultInterfaceSupport());
1991 	}
1992 
1993   protected PluginInterface
getDefaultInterfaceSupport()1994   getDefaultInterfaceSupport()
1995   {
1996 	  synchronized( s_plugin_interfaces ){
1997 
1998 	  	if ( default_plugin == null ){
1999 
2000 	  		try{
2001 		  		default_plugin =
2002 		  			new PluginInterfaceImpl(
2003 		  					new Plugin()
2004 							{
2005 		  						public void
2006 								initialize(
2007 									PluginInterface pi)
2008 		  						{
2009 		  						}
2010 							},
2011 							this,
2012 							getClass(),
2013 							getClass().getClassLoader(),
2014 							null,
2015 							"default",
2016 							new Properties(),
2017 							null,
2018 							INTERNAL_PLUGIN_ID,
2019 							null );
2020 
2021 	  		}catch( Throwable e ){
2022 
2023 	  			Debug.out( e );
2024 	  		}
2025 	  	}
2026   	}
2027 
2028   	return( default_plugin );
2029   }
2030 
2031   public void
downloadManagerAdded( DownloadManager dm )2032   downloadManagerAdded(
2033   	DownloadManager	dm )
2034   {
2035   }
2036 
2037   public void
downloadManagerRemoved( DownloadManager dm )2038   downloadManagerRemoved(
2039   	DownloadManager	dm )
2040   {
2041   }
2042 
2043   public void
destroyInitiated()2044   destroyInitiated()
2045   {
2046 	  List plugin_interfaces;
2047 
2048 	  synchronized( s_plugin_interfaces ){
2049 
2050 		  plugin_interfaces = new ArrayList( s_plugin_interfaces );
2051 	  }
2052 
2053 	  for (int i=0;i<plugin_interfaces.size();i++){
2054 
2055 		  ((PluginInterfaceImpl)plugin_interfaces.get(i)).closedownInitiated();
2056 	  }
2057 
2058 	  if ( default_plugin != null ){
2059 
2060 		  default_plugin.closedownInitiated();
2061 	  }
2062   }
2063 
2064   public void
destroyed()2065   destroyed()
2066   {
2067 	  List plugin_interfaces;
2068 
2069 	  synchronized( s_plugin_interfaces ){
2070 
2071 		  plugin_interfaces = new ArrayList( s_plugin_interfaces );
2072 	  }
2073 
2074 	  for (int i=0;i<plugin_interfaces.size();i++){
2075 
2076 		  ((PluginInterfaceImpl)plugin_interfaces.get(i)).closedownComplete();
2077 	  }
2078 
2079 	  if ( default_plugin != null ){
2080 
2081 		  default_plugin.closedownComplete();
2082 	  }
2083   }
2084 
2085 
seedingStatusChanged( boolean seeding_only_mode, boolean b )2086   public void seedingStatusChanged( boolean seeding_only_mode, boolean b ){
2087     /*nothing*/
2088   }
2089 
2090   protected void
runPEVTask( AERunnable run )2091   runPEVTask(
2092 		AERunnable	run )
2093   {
2094 	  async_dispatcher.dispatch( run );
2095   }
2096 
2097 
2098   protected List<PluginEvent>
getPEVHistory()2099   getPEVHistory()
2100   {
2101 	  return( plugin_event_history );
2102   }
2103 
2104   protected void
fireEventSupport( final int type, final Object value )2105   fireEventSupport(
2106   	final int		type,
2107   	final Object	value )
2108   {
2109 	  async_dispatcher.dispatch(
2110 		 new AERunnable()
2111 		 {
2112 			 public void
2113 			 runSupport()
2114 			 {
2115 			  	PluginEvent	ev =
2116 			  		new PluginEvent()
2117 			  		{
2118 			  			public int
2119 			  			getType()
2120 			  			{
2121 			  				return( type );
2122 			  			}
2123 
2124 			  			public Object
2125 			  			getValue()
2126 			  			{
2127 			  				return( value );
2128 			  			}
2129 			  		};
2130 
2131 			  	if ( 	type == PluginEvent.PEV_CONFIGURATION_WIZARD_STARTS ||
2132 			  			type == PluginEvent.PEV_CONFIGURATION_WIZARD_COMPLETES ||
2133 			  			type == PluginEvent.PEV_INITIAL_SHARING_COMPLETE ||
2134 			  			type == PluginEvent.PEV_INITIALISATION_UI_COMPLETES ||
2135 			  			type == PluginEvent.PEV_ALL_PLUGINS_INITIALISED ){
2136 
2137 			  		plugin_event_history.add( ev );
2138 
2139 			  		if ( plugin_event_history.size() > 1024 ){
2140 
2141 			  			Debug.out( "Plugin event history too large!!!!" );
2142 
2143 			  			plugin_event_history.remove( 0 );
2144 			  		}
2145 			  	}
2146 
2147 			  	List plugin_interfaces;
2148 
2149 			  	synchronized( s_plugin_interfaces ){
2150 
2151 			  		plugin_interfaces = new ArrayList( s_plugin_interfaces );
2152 			  	}
2153 
2154 			  	for (int i=0;i<plugin_interfaces.size();i++){
2155 
2156 			  		try{
2157 			  			((PluginInterfaceImpl)plugin_interfaces.get(i)).firePluginEventSupport(ev);
2158 
2159 			  		}catch(Throwable e ){
2160 
2161 			  			Debug.printStackTrace(e);
2162 			  		}
2163 			  	}
2164 
2165 			 	if ( default_plugin != null ){
2166 
2167 			  		default_plugin.firePluginEventSupport(ev);
2168 			  	}
2169 			  }
2170 		 });
2171   }
2172 
2173   private void
waitForEvents()2174   waitForEvents()
2175   {
2176 	  if ( async_dispatcher.isDispatchThread()){
2177 
2178 		  Debug.out( "Deadlock - recode this monkey boy" );
2179 
2180 	  }else{
2181 
2182 		  final AESemaphore sem = new AESemaphore( "waiter" );
2183 
2184 		  async_dispatcher.dispatch(
2185 				new AERunnable()
2186 				{
2187 					public void
2188 					runSupport()
2189 					{
2190 						sem.release();
2191 					}
2192 				});
2193 
2194 		  if ( !sem.reserve( 10*1000 )){
2195 
2196 			  Debug.out( "Timeout waiting for event dispatch" );
2197 		  }
2198 
2199 	  }
2200   }
2201 
2202   public static void
fireEvent( int type )2203   fireEvent(
2204   	int		type )
2205   {
2206   	singleton.fireEventSupport(type, null);
2207   }
2208 
2209   public static void
fireEvent( int type, Object value )2210   fireEvent(
2211 	int		type,
2212 	Object	value )
2213   {
2214   	singleton.fireEventSupport(type, value);
2215   }
2216 
2217   public static void
waitForPluginEvents()2218   waitForPluginEvents()
2219   {
2220 	  singleton.waitForEvents();
2221   }
2222 
2223   public void
initialisationComplete()2224   initialisationComplete()
2225   {
2226   	initialisation_complete	= true;
2227 
2228   	UIManagerImpl.initialisationComplete();
2229 
2230 	List plugin_interfaces;
2231 
2232 	synchronized( s_plugin_interfaces ){
2233 
2234 		plugin_interfaces = new ArrayList( s_plugin_interfaces );
2235 	}
2236 
2237   	for (int i=0;i<plugin_interfaces.size();i++){
2238 
2239   		((PluginInterfaceImpl)plugin_interfaces.get(i)).initialisationComplete();
2240   	}
2241 
2242   		// keep this last as there are things out there that rely on the init complete of the
2243   		// default interface meaning that everything else is complete and informed complete
2244 
2245   	if ( default_plugin != null ){
2246 
2247   		default_plugin.initialisationComplete();
2248   	}
2249   }
2250 
2251   protected boolean
isInitialisationComplete()2252   isInitialisationComplete()
2253   {
2254   	return( initialisation_complete );
2255   }
2256 
2257 
getPluginInterfaces()2258   public static List<PluginInterfaceImpl> getPluginInterfaces() {
2259   	return singleton.getPluginInterfacesSupport( false );
2260   }
2261 
getPluginInterfacesSupport( boolean expect_partial_result )2262   private List<PluginInterfaceImpl> getPluginInterfacesSupport( boolean expect_partial_result ) {
2263 
2264 	if ( !expect_partial_result ){
2265 
2266 		checkPluginsInitialised();
2267 	}
2268 
2269 	synchronized( s_plugin_interfaces ){
2270 
2271 		return( new ArrayList<PluginInterfaceImpl>( s_plugin_interfaces ));
2272 	}
2273   }
2274 
2275   public PluginInterface[]
getPlugins()2276   getPlugins()
2277   {
2278 	  return( getPlugins( false ));
2279   }
2280 
2281   public PluginInterface[]
getPlugins( boolean expect_partial_result )2282   getPlugins(
2283 		boolean	expect_partial_result )
2284   {
2285   	List	pis = getPluginInterfacesSupport( expect_partial_result );
2286 
2287   	PluginInterface[]	res = new 	PluginInterface[pis.size()];
2288 
2289   	pis.toArray(res);
2290 
2291   	return( res );
2292   }
2293 
2294   protected PluginManager
getPluginManager()2295   getPluginManager()
2296   {
2297   	return( plugin_manager );
2298   }
2299 
2300   protected PluginInterfaceImpl
getPluginFromClass( Class cla )2301   getPluginFromClass(
2302   	Class	cla )
2303   {
2304   	return( getPluginFromClass( cla.getName()));
2305   }
2306 
2307   protected PluginInterfaceImpl
getPluginFromClass( String class_name )2308   getPluginFromClass(
2309   	String	class_name )
2310   {
2311 	List plugin_interfaces;
2312 
2313 	synchronized( s_plugin_interfaces ){
2314 
2315 		plugin_interfaces = new ArrayList( s_plugin_interfaces );
2316 	}
2317 
2318   	for (int i=0;i<plugin_interfaces.size();i++){
2319 
2320   		PluginInterfaceImpl	pi = (PluginInterfaceImpl)plugin_interfaces.get(i);
2321 
2322   		if ( pi.getPlugin().getClass().getName().equals( class_name )){
2323 
2324   			return( pi );
2325   		}
2326   	}
2327 
2328   		// fall back to the loaded but not-yet-initialised list
2329 
2330   	for (int i=0;i<loaded_pi_list.size();i++){
2331 
2332   		List	l = (List)loaded_pi_list.get(i);
2333 
2334   		for (int j=0;j<l.size();j++){
2335 
2336   			PluginInterfaceImpl	pi = (PluginInterfaceImpl)l.get(j);
2337 
2338   			if ( pi.getPlugin().getClass().getName().equals( class_name )){
2339 
2340   	  			return( pi );
2341   	  		}
2342   		}
2343   	}
2344 
2345   	return( null );
2346   }
2347 
2348 
2349 	public void
generate( IndentWriter writer )2350 	generate(
2351 		IndentWriter		writer )
2352 	{
2353 		writer.println( "Plugins" );
2354 
2355 		try{
2356 			writer.indent();
2357 
2358 			List plugin_interfaces;
2359 
2360 			synchronized( s_plugin_interfaces ){
2361 
2362 				plugin_interfaces = new ArrayList( s_plugin_interfaces );
2363 			}
2364 
2365 		 	for (int i=0;i<plugin_interfaces.size();i++){
2366 
2367 		  		PluginInterfaceImpl	pi = (PluginInterfaceImpl)plugin_interfaces.get(i);
2368 
2369 		  		pi.generateEvidence( writer );
2370 		 	}
2371 
2372 		}finally{
2373 
2374 			writer.exdent();
2375 		}
2376 	}
2377 
2378 	protected static void
setVerified( PluginInterfaceImpl pi, Plugin plugin, boolean v, boolean bad )2379 	setVerified(
2380 		PluginInterfaceImpl		pi,
2381 		Plugin					plugin,
2382 		boolean					v,
2383 		boolean					bad )
2384 
2385 		throws PluginException
2386 	{
2387 		Object[] existing = (Object[])verified_plugin_holder.setValue( pi, new Object[]{ plugin, v });
2388 
2389 		if ( existing != null && ( existing[0] != plugin || (Boolean)existing[1] != v )){
2390 
2391 			throw( new PluginException( "Verified status change not permitted" ));
2392 		}
2393 
2394   	  	if ( bad && !DISABLE_PLUGIN_VERIFICATION ){
2395 
2396 		  throw( new RuntimeException( "Plugin verification failed" ));
2397 	  }
2398 	}
2399 
2400 	public static boolean
isVerified( PluginInterface pi, Plugin plugin )2401 	isVerified(
2402 		PluginInterface		pi,
2403 		Plugin				plugin )
2404 	{
2405 		if ( !( pi instanceof PluginInterfaceImpl )){
2406 
2407 			return( false );
2408 		}
2409 
2410 		VerifiedPluginHolder holder = verified_plugin_holder;
2411 
2412 		if ( holder.getClass() != VerifiedPluginHolder.class ){
2413 
2414 			Debug.out( "class mismatch" );
2415 
2416 			return( false );
2417 		}
2418 
2419 		if ( DISABLE_PLUGIN_VERIFICATION ){
2420 
2421 			Debug.out( " **************************** VERIFICATION DISABLED ******************" );
2422 
2423 			return( true );
2424 		}
2425 
2426 		Object[] ver = (Object[])verified_plugin_holder.getValue( pi );
2427 
2428 		return( ver != null && ver[0] == plugin && (Boolean)ver[1] );
2429 	}
2430 
2431 	public static boolean
isCoreOrVerifiedPlugin()2432 	isCoreOrVerifiedPlugin()
2433 	{
2434 		Class<?>[] stack = SESecurityManager.getClassContext();
2435 
2436 		ClassLoader core = PluginInitializer.class.getClassLoader();
2437 
2438 		PluginInitializer singleton = peekSingleton();
2439 
2440 		PluginInterface[] pis = singleton==null?new PluginInterface[0]:singleton.getPlugins();
2441 
2442 		Set<ClassLoader>	ok_loaders = new HashSet<ClassLoader>();
2443 
2444 		ok_loaders.add( core );
2445 
2446 		for ( Class<?> c: stack ){
2447 
2448 			ClassLoader cl = c.getClassLoader();
2449 
2450 			if ( cl != null && !ok_loaders.contains( cl )){
2451 
2452 				boolean ok = false;
2453 
2454 				for ( PluginInterface pi: pis ){
2455 
2456 					Plugin plugin = pi.getPlugin();
2457 
2458 					if ( plugin.getClass().getClassLoader() == cl ){
2459 
2460 						if ( isVerified( pi, plugin )){
2461 
2462 							ok_loaders.add( cl );
2463 
2464 							ok = true;
2465 
2466 							break;
2467 						}
2468 					}
2469 				}
2470 
2471 				if ( !ok ){
2472 
2473 					Debug.out( "Class " + c.getCanonicalName() + " with loader " + cl + " isn't trusted" );
2474 
2475 					return( false );
2476 				}
2477 			}
2478 		}
2479 
2480 		return( true );
2481 	}
2482 
2483 	private static final class
2484 	VerifiedPluginHolder
2485 	{
2486 		private static final Object	NULL_VALUE = new Object();
2487 
2488 		private volatile boolean initialised;
2489 
2490 		private AESemaphore	request_sem = new AESemaphore( "ValueHolder" );
2491 
2492 		private List<Object[]>	request_queue = new ArrayList<Object[]>();
2493 
2494 		private
VerifiedPluginHolder()2495 		VerifiedPluginHolder()
2496 		{
2497 			Class[] context = SESecurityManager.getClassContext();
2498 
2499 			if ( context.length == 0 ){
2500 
2501 				return;
2502 			}
2503 
2504 			if ( context[2] != PluginInitializer.class ){
2505 
2506 				Debug.out( "Illegal operation" );
2507 
2508 				return;
2509 			}
2510 
2511 			AEThread2 t =
2512 				new AEThread2( "PluginVerifier" )
2513 				{
2514 					public void
2515 					run()
2516 					{
2517 						Map<Object,Object> values = new IdentityHashMap<Object,Object>();
2518 
2519 						while( true ){
2520 
2521 							request_sem.reserve();
2522 
2523 							Object[] req;
2524 
2525 							synchronized( request_queue ){
2526 
2527 								req = request_queue.remove(0);
2528 							}
2529 
2530 							if ( req[1] == null ){
2531 
2532 								req[1] = values.get( req[0] );
2533 
2534 							}else{
2535 
2536 								Object existing = values.get( req[0] );
2537 
2538 								if ( req[1] == NULL_VALUE ){
2539 
2540 									req[1] = existing;
2541 
2542 									values.remove( req[0] );
2543 
2544 								}else{
2545 									if ( existing != null){
2546 
2547 										req[1] = existing;
2548 
2549 									}else{
2550 
2551 										values.put( req[0], req[1] );
2552 									}
2553 								}
2554 							}
2555 
2556 							((AESemaphore)req[2]).release();
2557 						}
2558 					}
2559 				};
2560 
2561 			t.start();
2562 
2563 			initialised = true;
2564 		}
2565 
2566 		public Object
removeValue( Object key )2567 		removeValue(
2568 			Object	key )
2569 		{
2570 			if ( !initialised ){
2571 
2572 				return( null );
2573 			}
2574 
2575 			AESemaphore sem = new AESemaphore( "ValueHolder:remove" );
2576 
2577 			Object[] request = new Object[]{ key, NULL_VALUE, sem };
2578 
2579 			synchronized( request_queue ){
2580 
2581 				request_queue.add( request );
2582 			}
2583 
2584 			request_sem.release();
2585 
2586 			sem.reserve();
2587 
2588 			return(request[1]);
2589 		}
2590 
2591 		public Object
setValue( Object key, Object value )2592 		setValue(
2593 			Object	key,
2594 			Object	value )
2595 		{
2596 			if ( !initialised ){
2597 
2598 				return( null );
2599 			}
2600 
2601 			AESemaphore sem = new AESemaphore( "ValueHolder:set" );
2602 
2603 			Object[] request = new Object[]{ key, value, sem };
2604 
2605 			synchronized( request_queue ){
2606 
2607 				request_queue.add( request );
2608 			}
2609 
2610 			request_sem.release();
2611 
2612 			sem.reserve();
2613 
2614 			return(request[1]);
2615 		}
2616 
2617 		public Object
getValue( Object key )2618 		getValue(
2619 			Object	key )
2620 		{
2621 			if ( !initialised ){
2622 
2623 				return( null );
2624 			}
2625 
2626 			AESemaphore sem = new AESemaphore( "ValueHolder:get" );
2627 
2628 			Object[] request = new Object[]{ key, null, sem };
2629 
2630 			synchronized( request_queue ){
2631 
2632 				request_queue.add( request );
2633 			}
2634 
2635 			request_sem.release();
2636 
2637 			sem.reserve();
2638 
2639 			return( request[1] );
2640 		}
2641 	}
2642 }
2643