/* * Created on May 6, 2008 * Created by Paul Gardner * * Copyright 2008 Vuze, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ package com.aelitis.azureus.core.metasearch.impl; import java.io.File; import java.io.InputStream; import java.net.URL; import java.util.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.core3.xml.util.XUXmlWriter; import org.gudy.azureus2.plugins.PluginInterface; import org.gudy.azureus2.plugins.PluginListener; import org.gudy.azureus2.plugins.ui.UIManager; import org.gudy.azureus2.plugins.ui.UIManagerEvent; import org.gudy.azureus2.plugins.utils.FeatureManager; import org.gudy.azureus2.plugins.utils.StaticUtilities; import org.gudy.azureus2.plugins.utils.FeatureManager.FeatureDetails; import org.gudy.azureus2.plugins.utils.FeatureManager.Licence; import org.gudy.azureus2.plugins.utils.search.Search; import org.gudy.azureus2.plugins.utils.search.SearchException; import org.gudy.azureus2.plugins.utils.search.SearchInitiator; import org.gudy.azureus2.plugins.utils.search.SearchInstance; import org.gudy.azureus2.plugins.utils.search.SearchListener; import org.gudy.azureus2.plugins.utils.search.SearchObserver; import org.gudy.azureus2.plugins.utils.search.SearchProvider; import org.gudy.azureus2.plugins.utils.search.SearchProviderResults; import org.gudy.azureus2.plugins.utils.search.SearchResult; import org.gudy.azureus2.pluginsimpl.local.PluginInitializer; import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl; import com.aelitis.azureus.core.custom.Customization; import com.aelitis.azureus.core.custom.CustomizationManager; import com.aelitis.azureus.core.custom.CustomizationManagerFactory; import com.aelitis.azureus.core.messenger.config.PlatformMetaSearchMessenger; import com.aelitis.azureus.core.metasearch.Engine; import com.aelitis.azureus.core.metasearch.MetaSearch; import com.aelitis.azureus.core.metasearch.MetaSearchException; import com.aelitis.azureus.core.metasearch.MetaSearchManager; import com.aelitis.azureus.core.metasearch.MetaSearchManagerListener; import com.aelitis.azureus.core.metasearch.Result; import com.aelitis.azureus.core.metasearch.ResultListener; import com.aelitis.azureus.core.metasearch.SearchParameter; import com.aelitis.azureus.core.metasearch.impl.plugin.PluginEngine; import com.aelitis.azureus.core.subs.Subscription; import com.aelitis.azureus.core.subs.SubscriptionManager; import com.aelitis.azureus.core.subs.SubscriptionManagerFactory; import com.aelitis.azureus.core.vuzefile.VuzeFile; import com.aelitis.azureus.core.vuzefile.VuzeFileComponent; import com.aelitis.azureus.core.vuzefile.VuzeFileHandler; import com.aelitis.azureus.core.vuzefile.VuzeFileProcessor; import com.aelitis.azureus.util.ConstantsVuze; import com.aelitis.azureus.util.ImportExportUtils; public class MetaSearchManagerImpl implements MetaSearchManager, UtilitiesImpl.searchManager, AEDiagnosticsEvidenceGenerator { private static final boolean AUTO_MODE_DEFAULT = true; private static final String LOGGER_NAME = "MetaSearch"; private static final int REFRESH_MILLIS = 23*60*60*1000; private static MetaSearchManagerImpl singleton; public static void preInitialise() { VuzeFileHandler.getSingleton().addProcessor( new VuzeFileProcessor() { public void process( VuzeFile[] files, int expected_types ) { for (int i=0;i<files.length;i++){ VuzeFile vf = files[i]; VuzeFileComponent[] comps = vf.getComponents(); for (int j=0;j<comps.length;j++){ VuzeFileComponent comp = comps[j]; int comp_type = comp.getType(); if ( comp_type == VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ){ try{ Engine e = getSingleton().importEngine( comp.getContent(), (expected_types & VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE) == 0 ); comp.setProcessed(); if ( e != null ){ comp.setData( Engine.VUZE_FILE_COMPONENT_ENGINE_KEY, e ); } }catch( Throwable e ){ Debug.printStackTrace(e); } }else if ( comp_type == VuzeFileComponent.COMP_TYPE_METASEARCH_OPERATION ){ getSingleton().addOperation( comp.getContent()); comp.setProcessed(); } } } } }); TorrentUtils.addTorrentAttributeListener( new TorrentUtils.torrentAttributeListener() { public void attributeSet( TOTorrent torrent, String attribute, Object value ) { if ( attribute == TorrentUtils.TORRENT_AZ_PROP_OBTAINED_FROM && !TorrentUtils.isReallyPrivate( torrent )){ try{ getSingleton().checkPotentialAssociations( torrent.getHash(), (String)value ); }catch( Throwable e ){ Debug.printStackTrace(e); } } } }); } public static synchronized MetaSearchManagerImpl getSingleton() { if ( singleton == null ){ singleton = new MetaSearchManagerImpl(); } return( singleton ); } private MetaSearchImpl meta_search; private AsyncDispatcher dispatcher = new AsyncDispatcher( 10000 ); private AESemaphore initial_refresh_sem = new AESemaphore( "MetaSearch:initrefresh" ); private AESemaphore refresh_sem = new AESemaphore( "MetaSearch:refresh", 1 ); private boolean checked_customization; private AsyncDispatcher op_dispatcher = new AsyncDispatcher(5*1000); private List<MetaSearchManagerListener> listeners = new ArrayList<MetaSearchManagerListener>(); private List<Map> operations = new ArrayList<Map>(); private String extension_key; private Map<String,EngineImpl> potential_associations = new LinkedHashMap<String,EngineImpl>(32,0.75f,true) { protected boolean removeEldestEntry( Map.Entry<String,EngineImpl> eldest) { return size() > 32; } }; protected MetaSearchManagerImpl() { meta_search = new MetaSearchImpl( this ); AEDiagnostics.addEvidenceGenerator( this ); extension_key = COConfigurationManager.getStringParameter( "metasearch.extkey.latest", "" ); if ( extension_key.length() == 0 ){ extension_key = null; } setupExtensions(); SimpleTimer.addPeriodicEvent( "MetaSearchRefresh", REFRESH_MILLIS, new TimerEventPerformer() { public void perform( TimerEvent event ) { refresh(); } }); refresh(); UtilitiesImpl.addSearchManager( this ); } public void addProvider( PluginInterface pi, SearchProvider provider ) { String id = pi.getPluginID() + "." + provider.getProperty( SearchProvider.PR_NAME ); try{ meta_search.importFromPlugin( id, provider ); }catch( Throwable e ){ Debug.out( "Failed to add search provider '" + id + "' (" + provider + ")", e ); } } public void removeProvider( PluginInterface pi, SearchProvider provider ) { String id = pi.getPluginID() + "." + provider.getProperty( SearchProvider.PR_NAME ); try{ Engine[] engines = meta_search.getEngines( false, false ); for ( Engine engine: engines ){ if ( engine instanceof PluginEngine ){ PluginEngine pe = (PluginEngine)engine; if ( pe.getProvider() == provider ){ engine.delete(); } } } }catch( Throwable e ){ Debug.out( "Failed to remove search provider '" + id + "' (" + provider + ")", e ); } } public SearchProvider[] getProviders() { Engine[] engines = meta_search.getEngines( true, false ); SearchProvider[] result = new SearchProvider[engines.length]; for (int i=0;i<engines.length;i++){ result[i] = new engineInfo( engines[i] ); } return( result ); } public Search createSearch( String provider_ids, String properties_str ) throws SearchException { String[] bits = XUXmlWriter.splitWithEscape( provider_ids, ',' ); long[] pids = new long[ bits.length ]; for ( int i=0; i<bits.length; i++ ){ pids[i] = Long.parseLong( bits[i] ); } Map<String,String> properties = new HashMap<String, String>(); bits = XUXmlWriter.splitWithEscape( properties_str, ',' ); for ( int i=0; i<bits.length; i++ ){ String[] x = XUXmlWriter.splitWithEscape( bits[i], '=' ); properties.put( x[0].trim(), x[1].trim()); } return( createSearch( pids, properties, null )); } public Search createSearch( SearchProvider[] providers, Map<String,String> properties, SearchListener listener ) throws SearchException { long[] pids; if ( providers == null ){ pids = new long[0]; }else{ pids = new long[providers.length]; for (int i=0;i<pids.length;i++){ Long id = (Long)providers[i].getProperty( SearchProvider.PR_ID ); if ( id == null ){ throw( new SearchException( "Unknown provider - no id available" )); } pids[i] = id; } } return( createSearch( pids, properties, listener )); } protected Search createSearch( long[] provider_ids, Map<String,String> properties, SearchListener listener ) throws SearchException { List<SearchParameter> sps = new ArrayList<SearchParameter>(); String search_term = properties.get( SearchInitiator.PR_SEARCH_TERM ); if ( search_term == null ){ throw( new SearchException( "Search term is mandatory" )); } sps.add( new SearchParameter( "s", search_term )); String mature = properties.get( SearchInitiator.PR_MATURE ); if ( mature != null ){ sps.add( new SearchParameter( "m", mature.toString())); } SearchParameter[] parameters = (SearchParameter[])sps.toArray(new SearchParameter[ sps.size()] ); Map<String,String> context = new HashMap<String, String>(); context.put( Engine.SC_FORCE_FULL, "true" ); String headers = null; int max_per_engine = 256; SearchObject search = new SearchObject( listener ); Engine[] used_engines; if ( provider_ids.length == 0 ){ used_engines = getMetaSearch().search( search, parameters, headers, context, max_per_engine ); }else{ List<Engine> selected_engines = new ArrayList<Engine>(); for ( long id: provider_ids ){ Engine engine = meta_search.getEngine( id ); if ( engine == null ){ throw( new SearchException( "Unknown engine id - " + id )); }else{ selected_engines.add( engine ); } } Engine[] engines = selected_engines.toArray( new Engine[ selected_engines.size()] ); used_engines = getMetaSearch().search( engines, search, parameters, headers, context, max_per_engine ); } search.setEnginesUsed( used_engines ); return( search ); } protected void refresh() { dispatcher.dispatch( new AERunnable() { public void runSupport() { if ( dispatcher.getQueueSize() == 0 ){ try{ syncRefresh(); }catch( Throwable e ){ } } } }); } protected void ensureEnginesUpToDate() { long timeout = meta_search.getEngineCount() == 0?(30*1000):(10*1000); if ( !initial_refresh_sem.reserve( timeout )){ log( "Timeout waiting for initial refresh to complete, continuing" ); } } protected void syncRefresh() throws MetaSearchException { boolean refresh_completed = false; boolean first_run = false; try{ refresh_sem.reserve(); first_run = COConfigurationManager.getBooleanParameter( "metasearch.refresh.first_run", true ); if ( !checked_customization ){ checked_customization = true; CustomizationManager cust_man = CustomizationManagerFactory.getSingleton(); Customization cust = cust_man.getActiveCustomization(); if ( cust != null ){ String cust_name = COConfigurationManager.getStringParameter( "metasearch.custom.name", "" ); String cust_version = COConfigurationManager.getStringParameter( "metasearch.custom.version", "0" ); boolean new_name = !cust_name.equals( cust.getName()); boolean new_version = org.gudy.azureus2.core3.util.Constants.compareVersions( cust_version, cust.getVersion() ) < 0; if ( new_name || new_version ){ log( "Customization: checking templates for " + cust.getName() + "/" + cust.getVersion()); try{ InputStream[] streams = cust.getResources( Customization.RT_META_SEARCH_TEMPLATES ); if ( streams.length > 0 && new_name ){ // reset engines log( " setting auto-mode to false" ); setAutoMode( false ); /* Engine[] engines = meta_search.getEngines( false, false ); for (int i=0;i<engines.length;i++){ Engine engine = engines[i]; if ( engine.getSelectionState()) == Engine.SEL_STATE_MANUAL_SELECTED ){ } } */ } for (int i=0;i<streams.length;i++){ InputStream is = streams[i]; try{ VuzeFile vf = VuzeFileHandler.getSingleton().loadVuzeFile(is); if ( vf != null ){ VuzeFileComponent[] comps = vf.getComponents(); for (int j=0;j<comps.length;j++){ VuzeFileComponent comp = comps[j]; if ( comp.getType() == VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ){ try{ Engine e = getSingleton().importEngine( comp.getContent(), false ); log( " updated " + e.getName()); e.setSelectionState( Engine.SEL_STATE_MANUAL_SELECTED ); }catch( Throwable e ){ Debug.printStackTrace(e); } } } } }finally{ try{ is.close(); }catch( Throwable e ){ } } } }finally{ COConfigurationManager.setParameter( "metasearch.custom.name", cust.getName()); COConfigurationManager.setParameter( "metasearch.custom.version", cust.getVersion()); } } } } log( "Refreshing engines" ); // featured templates are always shown - can't be deselected // popular ones are selected if in 'auto' mode // manually selected ones are, well, manually selected Map<Long,PlatformMetaSearchMessenger.templateInfo> vuze_selected_ids = new HashMap<Long, PlatformMetaSearchMessenger.templateInfo>(); Map<Long,PlatformMetaSearchMessenger.templateInfo> vuze_preload_ids = new HashMap<Long,PlatformMetaSearchMessenger.templateInfo>(); Set<Long> featured_ids = new HashSet<Long>(); Set<Long> popular_ids = new HashSet<Long>(); Set<Long> manual_vuze_ids = new HashSet<Long>(); boolean auto_mode = isAutoMode(); Engine[] engines = meta_search.getEngines( false, false ); String fud = meta_search.getFUD(); try{ PlatformMetaSearchMessenger.templateInfo[] featured = PlatformMetaSearchMessenger.listFeaturedTemplates( extension_key, fud ); String featured_str = ""; for (int i=0;i<featured.length;i++){ PlatformMetaSearchMessenger.templateInfo template = featured[i]; if ( !template.isVisible()){ continue; } Long key = new Long( template.getId()); vuze_selected_ids.put( key, template ); featured_ids.add( key ); featured_str += (featured_str.length()==0?"":",") + key; } log( "Featured templates: " + featured_str ); if ( auto_mode || first_run ){ PlatformMetaSearchMessenger.templateInfo[] popular = PlatformMetaSearchMessenger.listTopPopularTemplates( extension_key, fud ); String popular_str = ""; String preload_str = ""; for (int i=0;i<popular.length;i++){ PlatformMetaSearchMessenger.templateInfo template = popular[i]; if ( !template.isVisible()){ continue; } Long key = new Long( template.getId()); if ( auto_mode ){ if ( !vuze_selected_ids.containsKey( key )){ vuze_selected_ids.put( key, template ); popular_ids.add( key ); popular_str += (popular_str.length()==0?"":",") + key; } }else{ if ( !vuze_preload_ids.containsKey( key )){ vuze_preload_ids.put( key, template ); preload_str += (preload_str.length()==0?"":",") + key; } } } log( "Popular templates: " + popular_str ); if ( preload_str.length() > 0 ){ log( "Pre-load templates: " + popular_str ); } } // pick up explicitly selected vuze ones String manual_str = ""; for (int i=0;i<engines.length;i++){ Engine engine = engines[i]; Long key = new Long( engine.getId()); if ( engine.getSource() == Engine.ENGINE_SOURCE_VUZE && engine.getSelectionState() == Engine.SEL_STATE_MANUAL_SELECTED && !vuze_selected_ids.containsKey( key )){ manual_vuze_ids.add( key ); } } if ( manual_vuze_ids.size() > 0 ){ long[] manual_ids = new long[manual_vuze_ids.size()]; Iterator<Long> it = manual_vuze_ids.iterator(); int pos = 0; while( it.hasNext()){ manual_ids[pos++] = it.next().longValue(); } PlatformMetaSearchMessenger.templateInfo[] manual = PlatformMetaSearchMessenger.getTemplateDetails( extension_key, manual_ids ); for (int i=0;i<manual.length;i++){ PlatformMetaSearchMessenger.templateInfo template = manual[i]; if ( !template.isVisible()){ continue; } Long key = new Long( template.getId()); vuze_selected_ids.put( key, template ); manual_str += (manual_str.length()==0?"":",") + key; } } log( "Manual templates: " + manual_str ); Map<Long,Engine> existing_engine_map = new HashMap<Long,Engine>(); String existing_str = ""; for (int i=0;i<engines.length;i++){ Engine engine = engines[i]; Long key = new Long( engine.getId()); existing_engine_map.put( key, engine ); existing_str += (existing_str.length()==0?"":",") + key + "[source=" + Engine.ENGINE_SOURCE_STRS[engine.getSource()] + ",type=" + engine.getType() + ",selected=" + Engine.SEL_STATE_STRINGS[engine.getSelectionState()] + "]"; } log( "Existing templates: " + existing_str ); // we've compiled a list of the engines we should have and their latest dates // update any that are out of date Iterator<Map.Entry<Long,PlatformMetaSearchMessenger.templateInfo>> it = vuze_selected_ids.entrySet().iterator(); while( it.hasNext()){ Map.Entry<Long,PlatformMetaSearchMessenger.templateInfo> entry = it.next(); vuze_preload_ids.remove( entry.getKey()); long id = entry.getKey().longValue(); PlatformMetaSearchMessenger.templateInfo template = entry.getValue(); long modified = template.getModifiedDate(); Engine this_engine = (Engine)existing_engine_map.get( new Long(id)); boolean update = this_engine == null || this_engine.getLastUpdated() < modified; if ( update ){ PlatformMetaSearchMessenger.templateDetails details = PlatformMetaSearchMessenger.getTemplate( extension_key, id ); log( "Downloading definition of template " + id ); log( details.getValue()); if ( details.isVisible()){ try{ this_engine = meta_search.importFromJSONString( details.getType()==PlatformMetaSearchMessenger.templateDetails.ENGINE_TYPE_JSON?Engine.ENGINE_TYPE_JSON:Engine.ENGINE_TYPE_REGEX, details.getId(), details.getModifiedDate(), details.getRankBias(), details.getName(), details.getValue()); this_engine.setSource( Engine.ENGINE_SOURCE_VUZE ); meta_search.addEngine( this_engine ); }catch( Throwable e ){ log( "Failed to import engine '" + details.getValue() + "'", e ); } } }else if ( this_engine.getRankBias() != template.getRankBias()){ this_engine.setRankBias( template.getRankBias()); log( "Updating rank bias for " + this_engine.getString() + " to " + template.getRankBias()); }else{ log( "Not updating " + this_engine.getString() + " as unchanged" ); } if ( this_engine != null ){ int sel_state = this_engine.getSelectionState(); if ( sel_state == Engine.SEL_STATE_DESELECTED ){ log( "Auto-selecting " + this_engine.getString()); this_engine.setSelectionState( Engine.SEL_STATE_AUTO_SELECTED ); }else if ( auto_mode && sel_state == Engine.SEL_STATE_MANUAL_SELECTED ){ log( "Switching Manual to Auto select for " + this_engine.getString()); this_engine.setSelectionState( Engine.SEL_STATE_AUTO_SELECTED ); } } } // do any pre-loads it = vuze_preload_ids.entrySet().iterator(); while( it.hasNext()){ Map.Entry<Long,PlatformMetaSearchMessenger.templateInfo> entry = it.next(); long id = ((Long)entry.getKey()).longValue(); Engine this_engine = (Engine)existing_engine_map.get( new Long(id)); if ( this_engine == null ){ PlatformMetaSearchMessenger.templateDetails details = PlatformMetaSearchMessenger.getTemplate( extension_key, id ); log( "Downloading pre-load definition of template " + id ); log( details.getValue()); if ( details.isVisible()){ try{ this_engine = meta_search.importFromJSONString( details.getType()==PlatformMetaSearchMessenger.templateDetails.ENGINE_TYPE_JSON?Engine.ENGINE_TYPE_JSON:Engine.ENGINE_TYPE_REGEX, details.getId(), details.getModifiedDate(), details.getRankBias(), details.getName(), details.getValue()); this_engine.setSource( Engine.ENGINE_SOURCE_VUZE ); this_engine.setSelectionState( Engine.SEL_STATE_DESELECTED ); meta_search.addEngine( this_engine ); }catch( Throwable e ){ log( "Failed to import engine '" + details.getValue() + "'", e ); } } } } // deselect any not in use for (int i=0;i<engines.length;i++){ Engine engine = engines[i]; if ( engine.getSource() == Engine.ENGINE_SOURCE_VUZE && engine.getSelectionState() == Engine.SEL_STATE_AUTO_SELECTED && !vuze_selected_ids.containsKey( new Long( engine.getId()))){ log( "Deselecting " + engine.getString() + " as no longer visible on Vuze"); engine.setSelectionState( Engine.SEL_STATE_DESELECTED ); } } // finally pick up any unreported selection changes and re-affirm positive selections for (int i=0;i<engines.length;i++){ Engine engine = engines[i]; if ( engine.getSource() == Engine.ENGINE_SOURCE_VUZE && engine.getSelectionState() == Engine.SEL_STATE_MANUAL_SELECTED ){ engine.recordSelectionState(); }else{ engine.checkSelectionStateRecorded(); } } refresh_completed = true; }catch( Throwable e ){ log( "Refresh failed", e ); throw( new MetaSearchException( "Refresh failed", e )); } }finally{ if ( first_run && refresh_completed ){ COConfigurationManager.setParameter( "metasearch.refresh.first_run", false ); } refresh_sem.release(); initial_refresh_sem.releaseForever(); } } public MetaSearch getMetaSearch() { return( meta_search ); } public boolean isAutoMode() { return( COConfigurationManager.getBooleanParameter( "metasearch.auto.mode", AUTO_MODE_DEFAULT )); } protected void setAutoMode( boolean auto ) { COConfigurationManager.setParameter( "metasearch.auto.mode", auto ); } public void setSelectedEngines( long[] ids, boolean auto ) throws MetaSearchException { try{ String s = ""; for (int i=0;i<ids.length;i++){ s += (i==0?"":",") + ids[i]; } log( "setSelectedIds: " + s + ", auto=" + auto ); // first update state of auto and existing engines COConfigurationManager.setParameter( "metasearch.auto.mode", auto ); Engine[] engines = meta_search.getEngines( false, false ); Map<Long,Engine> engine_map = new HashMap<Long,Engine>(); for( int i=0;i<engines.length;i++){ engine_map.put( new Long( engines[i].getId()), engines[i] ); } Set<Engine> selected_engine_set = new HashSet<Engine>(); for (int i=0;i<ids.length;i++){ long id = ids[i]; Engine existing = (Engine)engine_map.get(new Long(id)); if ( existing != null ){ existing.setSelectionState( Engine.SEL_STATE_MANUAL_SELECTED ); selected_engine_set.add( existing ); } } // now refresh - this will pick up latest state of things syncRefresh(); engines = meta_search.getEngines( false, false ); // next add in any missing engines for (int i=0;i<ids.length;i++){ long id = ids[i]; Engine existing = (Engine)engine_map.get(new Long(id)); if ( existing == null ){ PlatformMetaSearchMessenger.templateDetails details = PlatformMetaSearchMessenger.getTemplate( extension_key, id ); log( "Downloading definition of template " + id ); log( details.getValue()); Engine new_engine = meta_search.importFromJSONString( details.getType()==PlatformMetaSearchMessenger.templateDetails.ENGINE_TYPE_JSON?Engine.ENGINE_TYPE_JSON:Engine.ENGINE_TYPE_REGEX, details.getId(), details.getModifiedDate(), details.getRankBias(), details.getName(), details.getValue()); new_engine.setSelectionState( Engine.SEL_STATE_MANUAL_SELECTED ); new_engine.setSource( Engine.ENGINE_SOURCE_VUZE ); meta_search.addEngine( new_engine ); selected_engine_set.add( new_engine ); } } // deselect any existing manually selected ones that are no longer selected for( int i=0;i<engines.length;i++){ Engine e = engines[i]; if ( e.getSelectionState() == Engine.SEL_STATE_MANUAL_SELECTED ){ if ( !selected_engine_set.contains( e )){ e.setSelectionState( Engine.SEL_STATE_DESELECTED ); } } } }catch( Throwable e ){ e.printStackTrace(); if ( e instanceof MetaSearchException ){ throw((MetaSearchException)e); } throw( new MetaSearchException( "Failed to set selected engines", e )); } } public Engine addEngine( long id, int type, String name, String json_value ) throws MetaSearchException { if ( id == -1 ){ id = getLocalTemplateID(); } try{ Engine engine = meta_search.importFromJSONString( type, id, SystemTime.getCurrentTime(), 1, name, json_value ); engine.setSource( Engine.ENGINE_SOURCE_LOCAL ); engine.setSelectionState( Engine.SEL_STATE_MANUAL_SELECTED ); meta_search.addEngine( engine ); return( engine ); }catch( Throwable e ){ throw( new MetaSearchException( "Failed to add engine", e )); } } public boolean isImportable( VuzeFile vf ) { VuzeFileComponent[] comps = vf.getComponents(); for (int j=0;j<comps.length;j++){ VuzeFileComponent comp = comps[j]; int comp_type = comp.getType(); if ( comp_type == VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ){ try{ EngineImpl engine = (EngineImpl)meta_search.importFromBEncodedMap( comp.getContent()); long id = engine.getId(); Engine existing = meta_search.getEngine( id ); if ( existing != null ){ if ( existing.getSelectionState() == Engine.SEL_STATE_DESELECTED || !existing.sameLogicAs( engine )){ return( true ); } }else{ try{ Engine[] engines = meta_search.getEngines( false, false ); boolean is_new = true; for ( Engine e: engines ){ if ( e.getSelectionState() != Engine.SEL_STATE_DESELECTED && e.sameLogicAs( engine )){ is_new = false; break; } } if ( is_new ){ return( true ); } }catch( Throwable e ){ } } }catch( Throwable e ){ } } } return( false ); } public Engine importEngine( Map map, boolean warn_user ) throws MetaSearchException { try{ EngineImpl engine = (EngineImpl)meta_search.importFromBEncodedMap(map); long id = engine.getId(); Engine existing = meta_search.getEngine( id ); if ( existing != null ){ if ( existing.sameLogicAs( engine )){ if ( warn_user ){ UIManager ui_manager = StaticUtilities.getUIManager( 120*1000 ); String details = MessageText.getString( "metasearch.addtemplate.dup.desc", new String[]{ engine.getName() }); ui_manager.showMessageBox( "metasearch.addtemplate.dup.title", "!" + details + "!", UIManagerEvent.MT_OK ); } return( existing ); } }else{ try{ Engine[] engines = meta_search.getEngines( false, false ); for ( Engine e: engines ){ if ( e.sameLogicAs( engine )){ return( e ); } } }catch( Throwable e ){ } } if ( warn_user ){ UIManager ui_manager = StaticUtilities.getUIManager( 120*1000 ); String details = MessageText.getString( "metasearch.addtemplate.desc", new String[]{ engine.getName() }); long res = ui_manager.showMessageBox( "metasearch.addtemplate.title", "!" + details + "!", UIManagerEvent.MT_YES | UIManagerEvent.MT_NO ); if ( res != UIManagerEvent.MT_YES ){ throw( new MetaSearchException( "User declined the template" )); } } // if local template then we try to use the id as is otherwise we emsure that // it is a local one if ( id >= 0 && id < Integer.MAX_VALUE ){ id = getLocalTemplateID(); engine.setId( id ); } engine.setSource( Engine.ENGINE_SOURCE_LOCAL ); engine.setSelectionState( Engine.SEL_STATE_MANUAL_SELECTED ); meta_search.addEngine( engine ); if ( warn_user ){ UIManager ui_manager = StaticUtilities.getUIManager( 120*1000 ); String details = MessageText.getString( "metasearch.addtemplate.done.desc", new String[]{ engine.getName() }); ui_manager.showMessageBox( "metasearch.addtemplate.done.title", "!" + details + "!", UIManagerEvent.MT_OK ); } return( engine ); }catch( Throwable e ){ if ( warn_user ){ UIManager ui_manager = StaticUtilities.getUIManager( 120*1000 ); String details = MessageText.getString( "metasearch.addtemplate.failed.desc", new String[]{ Debug.getNestedExceptionMessage(e) }); ui_manager.showMessageBox( "metasearch.addtemplate.failed.title", "!" + details + "!", UIManagerEvent.MT_OK ); } throw( new MetaSearchException( "Failed to add engine", e )); } } protected void addPotentialAssociation( EngineImpl engine, String key ) { if ( engine.isShareable() && !engine.isAuthenticated()){ synchronized( potential_associations ){ potential_associations.put( key, engine ); } } } private void checkPotentialAssociations( byte[] hash, String key ) { EngineImpl engine; synchronized( potential_associations ){ engine = potential_associations.remove( key ); } if ( engine != null ){ try{ VuzeFile vf = engine.exportToVuzeFile( true ); byte[] bytes = vf.exportToBytes(); String url_str = "vuze://?body=" + new String( bytes, Constants.BYTE_ENCODING ); SubscriptionManager sub_man = SubscriptionManagerFactory.getSingleton(); Subscription subs = sub_man.createSingletonRSS( vf.getName() + ": " + engine.getName() + " (v" + engine.getVersion() + ")", new URL( url_str ), Integer.MAX_VALUE ); subs.setSubscribed( true ); subs.addAssociation( hash ); }catch( Throwable e ){ Debug.out( e ); } } } public Engine[] loadFromVuzeFile( File file ) { VuzeFile vf = VuzeFileHandler.getSingleton().loadVuzeFile( file.getAbsolutePath()); if ( vf != null ){ return( loadFromVuzeFile( vf )); } return( new Engine[0]); } public Engine[] loadFromVuzeFile( VuzeFile vf ) { List<Engine> result = new ArrayList<Engine>(); VuzeFileComponent[] comps = vf.getComponents(); for (int j=0;j<comps.length;j++){ VuzeFileComponent comp = comps[j]; if ( comp.getType() == VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ){ try{ result.add( importEngine( comp.getContent(), false )); }catch( Throwable e ){ Debug.printStackTrace(e); } } } return(result.toArray(new Engine[result.size()])); } public long getLocalTemplateID() { synchronized( this ){ Random random = new Random(); while( true ){ long id = ((long)Integer.MAX_VALUE) + random.nextInt( Integer.MAX_VALUE ); if ( meta_search.getEngine( id ) == null ){ return( id ); } } } } public void addListener( MetaSearchManagerListener listener ) { synchronized( listeners ){ listeners.add( listener ); } dispatchOps(); } public void removeListener( MetaSearchManagerListener listener ) { } private void addOperation( Map map ) { synchronized( listeners ){ operations.add( map ); } dispatchOps(); } private void dispatchOps() { op_dispatcher.dispatch( new AERunnable() { public void runSupport() { List<MetaSearchManagerListener> l; List<Map> o; synchronized( listeners ){ if ( listeners.size() == 0 || operations.size() == 0 ){ return; } l = new ArrayList<MetaSearchManagerListener>( listeners ); o = new ArrayList<Map>( operations ); operations.clear(); } for ( MetaSearchManagerListener listener: l ){ for ( Map operation: o ){ try{ int type = ImportExportUtils.importInt( operation, "type", -1 ); if ( type == 1 ){ String term = ImportExportUtils.importString( operation, "term", null ); if ( term == null ){ Debug.out( "search term missing" ); }else{ listener.searchRequest( term ); } }else{ Debug.out( "unknown operation type " + type ); } }catch( Throwable e ){ Debug.out( e ); } } } } }); } private void setupExtensions() { PluginInterface pi = PluginInitializer.getDefaultInterface(); final FeatureManager fm = pi.getUtilities().getFeatureManager(); fm.addListener( new FeatureManager.FeatureManagerListener() { public void licenceAdded( Licence licence ) { getExtensions( fm, false ); } public void licenceChanged( Licence licence ) { getExtensions( fm, false ); } public void licenceRemoved( Licence licence ) { getExtensions( fm, false ); } }); if ( pi.getPluginState().isInitialisationComplete()){ getExtensions( fm, true ); }else{ pi.addListener( new PluginListener() { public void initializationComplete() { getExtensions( fm, false ); } public void closedownInitiated() { } public void closedownComplete() { } }); } } private void getExtensions( FeatureManager fm, boolean init ) { String existing_ext = extension_key; String latest_ext = null; FeatureDetails[] fds = fm.getFeatureDetails( "core" ); for ( FeatureDetails fd: fds ){ if ( !fd.hasExpired()){ String finger_print = (String)fd.getProperty( FeatureDetails.PR_FINGERPRINT ); if ( finger_print != null ){ latest_ext = fd.getLicence().getShortID() + "-" + finger_print; break; } } } if ( existing_ext != latest_ext ){ if ( existing_ext == null || latest_ext == null || !existing_ext.equals( latest_ext )){ extension_key = latest_ext; COConfigurationManager.setParameter( "metasearch.extkey.latest", latest_ext==null?"":latest_ext ); if ( !init ){ refresh(); } } } } protected String getExtensionKey() { return( extension_key ); } public void log( String s, Throwable e ) { AEDiagnosticsLogger diag_logger = AEDiagnostics.getLogger( LOGGER_NAME ); diag_logger.log( s ); diag_logger.log( e ); if ( ConstantsVuze.DIAG_TO_STDOUT ){ System.out.println(Thread.currentThread().getName() + "|" + System.currentTimeMillis() + "] " + s + ": " + Debug.getNestedExceptionMessage(e)); } } public void log( String s ) { AEDiagnosticsLogger diag_logger = AEDiagnostics.getLogger( LOGGER_NAME ); diag_logger.log( s ); if ( ConstantsVuze.DIAG_TO_STDOUT ){ System.out.println(Thread.currentThread().getName() + "|" + System.currentTimeMillis() + "] " + s); } } public void generate( IndentWriter writer ) { writer.println( "Metasearch: auto=" + isAutoMode()); try{ writer.indent(); meta_search.generate( writer ); }finally{ writer.exdent(); } } protected static class SearchObject implements Search, ResultListener { private SearchListener listener; private Map<Long,engineInfo> engine_map = new HashMap<Long, engineInfo>(); private boolean engines_set; private List<SearchProviderResults> pending_results = new ArrayList<SearchProviderResults>(); private boolean is_complete; protected SearchObject( SearchListener _listener ) { listener = _listener; } protected void setEnginesUsed( Engine[] engines ) { boolean report_complete; synchronized( engine_map ){ for ( Engine e: engines ){ getInfo( e ); } engines_set = true; report_complete = reportOverallComplete(); } if ( listener != null && report_complete ){ listener.completed(); } } private boolean reportOverallComplete() { if ( is_complete || !engines_set ){ return( false ); } for ( engineInfo info: engine_map.values()){ if ( !info.isComplete()){ return( false ); } } is_complete = true; return( true ); } protected engineInfo getInfo( Engine engine ) { synchronized( engine_map ){ engineInfo res = engine_map.get( engine.getId()); if ( res == null ){ res = new engineInfo( engine ); engine_map.put( engine.getId(), res ); } return( res ); } } public void contentReceived( Engine engine, String content ) { // boring } public void matchFound( Engine engine, String[] fields ) { // boring } public void resultsReceived( Engine engine, final Result[] results ) { SearchProviderResults result; synchronized( engine_map ){ final engineInfo info = getInfo( engine ); result = new SearchProviderResults() { public SearchProvider getProvider() { return( info ); } public SearchResult[] getResults() { return( wrapResults( results )); } public boolean isComplete() { return( false ); } public SearchException getError() { return( null ); } }; pending_results.add( result ); } if ( listener != null ){ listener.receivedResults( new SearchProviderResults[]{ result }); } } public void resultsComplete( Engine engine ) { boolean report_complete; SearchProviderResults result; synchronized( engine_map ){ final engineInfo info = getInfo( engine ); info.setComplete(); report_complete = reportOverallComplete(); result = new SearchProviderResults() { public SearchProvider getProvider() { return( info ); } public SearchResult[] getResults() { return( new SearchResult[0] ); } public boolean isComplete() { return( true ); } public SearchException getError() { return( null ); } }; pending_results.add( result ); } if ( listener != null ){ listener.receivedResults( new SearchProviderResults[]{ result }); if ( report_complete ){ listener.completed(); } } } protected void failed( Engine engine, final SearchException error ) { boolean report_complete; SearchProviderResults result; synchronized( engine_map ){ final engineInfo info = getInfo( engine ); info.setComplete(); report_complete = reportOverallComplete(); result = new SearchProviderResults() { public SearchProvider getProvider() { return( info ); } public SearchResult[] getResults() { return( new SearchResult[0] ); } public boolean isComplete() { return( false ); } public SearchException getError() { return( error ); } }; pending_results.add( result ); } if ( listener != null ){ listener.receivedResults( new SearchProviderResults[]{ result }); if ( report_complete ){ listener.completed(); } } } public void engineFailed( Engine engine, Throwable cause ) { failed( engine, new SearchException( "Search failed", cause )); } public void engineRequiresLogin( Engine engine, Throwable cause ) { failed( engine, new SearchException( "Authentication required", cause )); } protected SearchResult[] wrapResults( Result[] res ) { SearchResult[] x = new SearchResult[ res.length ]; for ( int i=0;i<x.length;i++){ x[i] = new resultWrapper( res[i] ); } return( x ); } public SearchProviderResults[] getResults() { synchronized( engine_map ){ SearchProviderResults[] result = pending_results.toArray( new SearchProviderResults[pending_results.size()]); pending_results.clear(); return( result ); } } public boolean isComplete() { synchronized( engine_map ){ if ( !is_complete ){ return( false ); } if ( pending_results.size() > 0 ){ return( false ); } return( true ); } } protected static class resultWrapper implements SearchResult { private Result result; protected resultWrapper( Result _result ) { result = _result; } public Object getProperty( int property_name ) { switch( property_name ){ case PR_NAME:{ return( result.getName()); } case PR_PUB_DATE:{ return( result.getPublishedDate()); } case PR_SIZE:{ return( result.getSize()); } case PR_LEECHER_COUNT:{ return( new Long( result.getNbPeers())); } case PR_SEED_COUNT:{ return( new Long( result.getNbSeeds())); } case PR_SUPER_SEED_COUNT:{ return( new Long( result.getNbSuperSeeds())); } case PR_CATEGORY:{ return( result.getCategory()); } case PR_COMMENTS:{ return( new Long( result.getComments())); } case PR_VOTES:{ return( new Long( result.getVotes())); } case PR_CONTENT_TYPE:{ return( result.getContentType()); } case PR_DETAILS_LINK:{ return( result.getCDPLink()); } case PR_DOWNLOAD_LINK:{ return( result.getDownloadLink()); } case PR_PLAY_LINK:{ return( result.getPlayLink()); } case PR_PRIVATE:{ return( result.isPrivate()); } case PR_DRM_KEY:{ return( result.getDRMKey()); } case PR_DOWNLOAD_BUTTON_LINK:{ return( result.getDownloadButtonLink()); } case PR_RANK:{ float rank = result.getRank(); return( new Long(rank==-1?-1:(long)( rank * 100 ))); } case PR_ACCURACY:{ float accuracy = result.getAccuracy(); return( new Long(accuracy==-1?-1:(long)( accuracy * 100 ))); } case PR_VOTES_DOWN:{ return( new Long( result.getVotesDown())); } case PR_UID:{ return( result.getUID()); } case PR_HASH:{ String base32_hash = result.getHash(); if ( base32_hash != null ){ return( Base32.decode( base32_hash )); } return( null ); } default:{ Debug.out( "Unknown property type " + property_name ); } } return( null ); } } } protected static class engineInfo implements SearchProvider { private Engine engine; private boolean complete; protected engineInfo( Engine _engine ) { engine = _engine; } protected void setComplete() { complete = true; } protected boolean isComplete() { return( complete ); } public SearchInstance search( Map<String,Object> search_parameters, SearchObserver observer ) throws SearchException { throw( new SearchException( "Not supported" )); } public Object getProperty( int property ) { if ( property == PR_ID ){ return( engine.getId()); }else if ( property == PR_NAME ){ return( engine.getName()); }else{ return( null ); } } public void setProperty( int property, Object value ) { Debug.out( "Not supported" ); } } public static void main( String[] args ) { try{ VuzeFile vf = VuzeFileHandler.getSingleton().create(); Map contents = new HashMap(); contents.put( "type", new Long( 1 )); contents.put( "term", "donkey" ); vf.addComponent( VuzeFileComponent.COMP_TYPE_METASEARCH_OPERATION, contents); vf.write( new File( "C:\\temp\\search.vuze" )); }catch( Throwable e ){ e.printStackTrace(); } } }