/* * File : CategoryManagerImpl.java * Created : 09 feb. 2004 * By : TuxPaper * * Azureus - a Java Bittorrent client * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.gudy.azureus2.core3.category.impl; import java.util.Collections; import java.util.Comparator; import java.util.Map; import java.util.Iterator; import java.util.HashMap; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import org.gudy.azureus2.core3.category.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerState; 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.XMLEscapeWriter; import org.gudy.azureus2.core3.xml.util.XUXmlWriter; import org.gudy.azureus2.plugins.download.Download; import org.gudy.azureus2.plugins.download.DownloadScrapeResult; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageRequest; import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageResponse; import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.core.rssgen.RSSGeneratorPlugin; import com.aelitis.azureus.core.tag.Tag; import com.aelitis.azureus.core.tag.TagDownload; import com.aelitis.azureus.core.tag.TagType; import com.aelitis.azureus.core.tag.impl.TagTypeBase; public class CategoryManagerImpl extends TagTypeBase implements RSSGeneratorPlugin.Provider { private static final int[] color_default = { 189, 178, 57 }; private static final String PROVIDER = "categories"; private static final String UNCAT_NAME = "__uncategorised__"; private static final String ALL_NAME = "__all__"; private static CategoryManagerImpl catMan; private static CategoryImpl catAll = null; private static CategoryImpl catUncategorized = null; private static boolean doneLoading = false; private static AEMonitor class_mon = new AEMonitor( "CategoryManager:class" ); private Map<String,CategoryImpl> categories = new HashMap<String,CategoryImpl>(); private AEMonitor categories_mon = new AEMonitor( "Categories" ); private static final int LDT_CATEGORY_ADDED = 1; private static final int LDT_CATEGORY_REMOVED = 2; private static final int LDT_CATEGORY_CHANGED = 3; private ListenerManager category_listeners = ListenerManager.createManager( "CatListenDispatcher", new ListenerManagerDispatcher() { public void dispatch(Object _listener, int type, Object value ) { CategoryManagerListener target = (CategoryManagerListener)_listener; if ( type == LDT_CATEGORY_ADDED ) target.categoryAdded((Category)value); else if ( type == LDT_CATEGORY_REMOVED ) target.categoryRemoved((Category)value); else if ( type == LDT_CATEGORY_CHANGED ) target.categoryChanged((Category)value); } }); protected CategoryManagerImpl() { super( TagType.TT_DOWNLOAD_CATEGORY, TagDownload.FEATURES, "Category" ); addTagType(); loadCategories(); } public void addCategoryManagerListener(CategoryManagerListener l) { category_listeners.addListener( l ); } public void removeCategoryManagerListener(CategoryManagerListener l) { category_listeners.removeListener( l ); } public static CategoryManagerImpl getInstance() { try{ class_mon.enter(); if (catMan == null) catMan = new CategoryManagerImpl(); return catMan; }finally{ class_mon.exit(); } } protected void loadCategories() { if (doneLoading) return; doneLoading = true; FileInputStream fin = null; BufferedInputStream bin = null; makeSpecialCategories(); try { //open the file File configFile = FileUtil.getUserFile("categories.config"); fin = new FileInputStream(configFile); bin = new BufferedInputStream(fin, 8192); Map map = BDecoder.decode(bin); List catList = (List) map.get("categories"); for (int i = 0; i < catList.size(); i++) { Map mCategory = (Map) catList.get(i); try { String catName = new String((byte[]) mCategory.get("name"), Constants.DEFAULT_ENCODING); Long l_maxup = (Long)mCategory.get( "maxup" ); Long l_maxdown = (Long)mCategory.get( "maxdown" ); Map<String,String> attributes = BDecoder.decodeStrings((Map)mCategory.get( "attr" )); if ( attributes == null ){ attributes = new HashMap<String, String>(); } if ( catName.equals( UNCAT_NAME )){ catUncategorized.setUploadSpeed(l_maxup==null?0:l_maxup.intValue()); catUncategorized.setDownloadSpeed(l_maxdown==null?0:l_maxdown.intValue()); catUncategorized.setAttributes( attributes ); }else if ( catName.equals( ALL_NAME )){ catAll.setAttributes( attributes ); }else{ categories.put( catName, new CategoryImpl( this, catName, l_maxup==null?0:l_maxup.intValue(), l_maxdown==null?0:l_maxdown.intValue(), attributes )); } } catch (UnsupportedEncodingException e1) { //Do nothing and process next. } } } catch (FileNotFoundException e) { //Do nothing } catch (Exception e) { Debug.printStackTrace( e ); } finally { try { if (bin != null) bin.close(); } catch (Exception e) {} try { if (fin != null) fin.close(); } catch (Exception e) {} checkConfig(); } } protected void saveCategories(Category category ){ saveCategories(); category_listeners.dispatch( LDT_CATEGORY_CHANGED, category ); } protected void saveCategories() { try{ categories_mon.enter(); Map map = new HashMap(); List list = new ArrayList(categories.size()); Iterator<CategoryImpl> iter = categories.values().iterator(); while (iter.hasNext()) { CategoryImpl cat = iter.next(); if (cat.getType() == Category.TYPE_USER) { Map catMap = new HashMap(); catMap.put( "name", cat.getName()); catMap.put( "maxup", new Long(cat.getUploadSpeed())); catMap.put( "maxdown", new Long(cat.getDownloadSpeed())); catMap.put( "attr", cat.getAttributes()); list.add(catMap); } } Map uncat = new HashMap(); uncat.put( "name", UNCAT_NAME ); uncat.put( "maxup", new Long(catUncategorized.getUploadSpeed())); uncat.put( "maxdown", new Long(catUncategorized.getDownloadSpeed())); uncat.put( "attr", catUncategorized.getAttributes()); list.add( uncat ); Map allcat = new HashMap(); allcat.put( "name", ALL_NAME ); allcat.put( "attr", catAll.getAttributes()); list.add( allcat ); map.put("categories", list); FileOutputStream fos = null; try { //encode the data byte[] torrentData = BEncoder.encode(map); File oldFile = FileUtil.getUserFile("categories.config"); File newFile = FileUtil.getUserFile("categories.config.new"); //write the data out fos = new FileOutputStream(newFile); fos.write(torrentData); fos.flush(); fos.getFD().sync(); //close the output stream fos.close(); fos = null; //delete the old file if ( !oldFile.exists() || oldFile.delete() ) { //rename the new one newFile.renameTo(oldFile); } } catch (Exception e) { Debug.printStackTrace( e ); } finally { try { if (fos != null) fos.close(); } catch (Exception e) {} } }finally{ checkConfig(); categories_mon.exit(); } } public Category createCategory(String name) { makeSpecialCategories(); CategoryImpl newCategory = getCategory(name); if (newCategory == null) { newCategory = new CategoryImpl(this,name, 0, 0, new HashMap<String,String>()); categories.put(name, newCategory); saveCategories(); category_listeners.dispatch( LDT_CATEGORY_ADDED, newCategory ); return (Category)categories.get(name); } return newCategory; } public void removeCategory(Category category) { if (categories.containsKey(category.getName())) { CategoryImpl old = categories.remove(category.getName()); saveCategories(); category_listeners.dispatch( LDT_CATEGORY_REMOVED, category ); if ( old != null ){ old.destroy(); } } } public Category[] getCategories() { if (categories.size() > 0) return (Category[])categories.values().toArray(new Category[categories.size()]); return (new Category[0]); } public CategoryImpl getCategory(String name) { return categories.get(name); } public Category getCategory(int type) { if (type == Category.TYPE_ALL) return catAll; if (type == Category.TYPE_UNCATEGORIZED) return catUncategorized; return null; } private void makeSpecialCategories() { if (catAll == null) { catAll = new CategoryImpl(this,"Categories.all", Category.TYPE_ALL, new HashMap<String,String>()); categories.put("Categories.all", catAll); } if (catUncategorized == null) { catUncategorized = new CategoryImpl(this,"Categories.uncategorized", Category.TYPE_UNCATEGORIZED, new HashMap<String,String>()); categories.put("Categories.uncategorized", catUncategorized); } } @Override public int[] getColorDefault() { return( color_default ); } public List<Tag> getTags() { return( new ArrayList<Tag>( categories.values())); } private void checkConfig() { boolean gen_enabled = false; for ( CategoryImpl cat: categories.values()){ if ( cat.getBooleanAttribute( Category.AT_RSS_GEN )){ gen_enabled = true; break; } } if ( gen_enabled ){ RSSGeneratorPlugin.registerProvider( PROVIDER, this ); }else{ RSSGeneratorPlugin.unregisterProvider( PROVIDER ); } } public boolean isEnabled() { return( true ); } public boolean generate( TrackerWebPageRequest request, TrackerWebPageResponse response ) throws IOException { URL url = request.getAbsoluteURL(); String path = url.getPath(); int pos = path.indexOf( '?' ); if ( pos != -1 ){ path = path.substring(0,pos); } path = path.substring( PROVIDER.length()+1); XMLEscapeWriter pw = new XMLEscapeWriter( new PrintWriter(new OutputStreamWriter( response.getOutputStream(), "UTF-8" ))); pw.setEnabled( false ); if ( path.length() <= 1 ){ response.setContentType( "text/html; charset=UTF-8" ); pw.println( "<HTML><HEAD><TITLE>Vuze Category Feeds</TITLE></HEAD><BODY>" ); Map<String,String> lines = new TreeMap<String, String>(); List<CategoryImpl> cats; try{ categories_mon.enter(); cats = new ArrayList<CategoryImpl>( categories.values()); }finally{ categories_mon.exit(); } for ( CategoryImpl c: cats ){ if ( c.getBooleanAttribute( Category.AT_RSS_GEN )){ String name = getDisplayName( c ); String cat_url = PROVIDER + "/" + URLEncoder.encode( c.getName(), "UTF-8" ); lines.put( name, "<LI><A href=\"" + cat_url + "\">" + name + "</A></LI>" ); } } for ( String line: lines.values() ){ pw.println( line ); } pw.println( "</BODY></HTML>" ); }else{ String cat_name = URLDecoder.decode( path.substring( 1 ), "UTF-8" ); CategoryImpl cat; try{ categories_mon.enter(); cat = categories.get( cat_name ); }finally{ categories_mon.exit(); } if ( cat == null ){ response.setReplyStatus( 404 ); return( true ); } List<DownloadManager> dms = cat.getDownloadManagers( AzureusCoreFactory.getSingleton().getGlobalManager().getDownloadManagers()); List<Download> downloads = new ArrayList<Download>( dms.size()); long dl_marker = 0; for ( DownloadManager dm: dms ){ TOTorrent torrent = dm.getTorrent(); if ( torrent == null ){ continue; } if ( !TorrentUtils.isReallyPrivate( torrent )){ dl_marker += dm.getDownloadState().getLongParameter( DownloadManagerState.PARAM_DOWNLOAD_ADDED_TIME ); downloads.add( PluginCoreUtils.wrap(dm)); } } String config_key = "cat.rss.config." + Base32.encode( cat.getName().getBytes( "UTF-8" )); long old_marker = COConfigurationManager.getLongParameter( config_key + ".marker", 0 ); long last_modified = COConfigurationManager.getLongParameter( config_key + ".last_mod", 0 ); long now = SystemTime.getCurrentTime(); if ( old_marker == dl_marker ){ if ( last_modified == 0 ){ last_modified = now; } }else{ COConfigurationManager.setParameter( config_key + ".marker", dl_marker ); last_modified = now; } if ( last_modified == now ){ COConfigurationManager.setParameter( config_key + ".last_mod", last_modified ); } pw.println( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" ); pw.println( "<rss version=\"2.0\" xmlns:vuze=\"http://www.vuze.com\">" ); pw.println( "<channel>" ); pw.println( "<title>" + escape( getDisplayName( cat )) + "</title>" ); Collections.sort( downloads, new Comparator<Download>() { public int compare( Download d1, Download d2) { long added1 = getAddedTime( d1 )/1000; long added2 = getAddedTime( d2 )/1000; return((int)(added2 - added1 )); } }); pw.println( "<pubDate>" + TimeFormatter.getHTTPDate( last_modified ) + "</pubDate>" ); for (int i=0;i<downloads.size();i++){ Download download = downloads.get( i ); DownloadManager core_download = PluginCoreUtils.unwrap( download ); Torrent torrent = download.getTorrent(); byte[] hash = torrent.getHash(); String hash_str = Base32.encode( hash ); pw.println( "<item>" ); pw.println( "<title>" + escape( download.getName()) + "</title>" ); pw.println( "<guid>" + hash_str + "</guid>" ); String magnet_url = escape( UrlUtils.getMagnetURI( download.getName(), torrent )); pw.println( "<link>" + magnet_url + "</link>" ); long added = core_download.getDownloadState().getLongParameter(DownloadManagerState.PARAM_DOWNLOAD_ADDED_TIME); pw.println( "<pubDate>" + TimeFormatter.getHTTPDate( added ) + "</pubDate>" ); pw.println( "<vuze:size>" + torrent.getSize()+ "</vuze:size>" ); pw.println( "<vuze:assethash>" + hash_str + "</vuze:assethash>" ); pw.println( "<vuze:downloadurl>" + magnet_url + "</vuze:downloadurl>" ); DownloadScrapeResult scrape = download.getLastScrapeResult(); if ( scrape != null && scrape.getResponseType() == DownloadScrapeResult.RT_SUCCESS ){ pw.println( "<vuze:seeds>" + scrape.getSeedCount() + "</vuze:seeds>" ); pw.println( "<vuze:peers>" + scrape.getNonSeedCount() + "</vuze:peers>" ); } pw.println( "</item>" ); } pw.println( "</channel>" ); pw.println( "</rss>" ); } pw.flush(); return( true ); } private String getDisplayName( CategoryImpl c ) { if ( c == catAll ){ return( MessageText.getString( "Categories.all" )); }else if ( c == catUncategorized ){ return( MessageText.getString( "Categories.uncategorized" )); }else{ return( c.getName()); } } protected long getAddedTime( Download download ) { DownloadManager core_download = PluginCoreUtils.unwrap( download ); return( core_download.getDownloadState().getLongParameter(DownloadManagerState.PARAM_DOWNLOAD_ADDED_TIME)); } protected String escape( String str ) { return( XUXmlWriter.escapeXML(str)); } }