/* * * RSSFeed - Azureus2 Plugin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package org.kmallan.azureus.rssfeed; import org.gudy.azureus2.core3.util.Constants; import javax.net.ssl.*; import java.io.*; import java.net.*; import java.util.*; import java.util.regex.*; public class Downloader extends InputStream { final static int DOWNLOADER_NON_INIT = 0, DOWNLOADER_INIT = 1, DOWNLOADER_START = 2; final static int DOWNLOADER_DOWNLOADING = 3, DOWNLOADER_FINISHED = 4, DOWNLOADER_CANCELED = 5; final static int DOWNLOADER_NOTMODIFIED = 6, DOWNLOADER_CHECKING = 7, DOWNLOADER_ERROR = -1; private URL url; private URLConnection con; private int state = DOWNLOADER_NON_INIT; private int percentDone = 0; private int readTotal = 0; private int size = 0; private int refCount = 0; protected String fileName, contentType, etag; protected long lastModified; protected InputStream in; protected List listeners = new ArrayList(); public int getState() { return state; } private synchronized void error(String err) { synchronized(listeners) { state = DOWNLOADER_ERROR; fireDownloaderUpdate(state, 0, 0, err); } } /* public static void testDownload(String url) throws Exception { Downloader downloader = new Downloader(); downloader.addListener(new DownloaderListener() { public void downloaderUpdate(int state, int percent, int amount, String err) { System.out.println(state + " " + percent + " " + amount + " " + err); } }); downloader.init(url, "application/x-bittorrent, application/x-httpd-php", null, null, 0, null); if(downloader.getState() == Downloader.DOWNLOADER_CANCELED || downloader.getState() == Downloader.DOWNLOADER_ERROR) { System.out.println("null"); return; } String filename = downloader.fileName; if(!downloader.fileName.toLowerCase().endsWith(".torrent")) filename = "temp-" + Long.toString((new Date()).getTime()) + "-" + Long.toString((new Random()).nextLong()) + ".torrent"; File torrentLocation = new File(filename); torrentLocation.createNewFile(); FileOutputStream fileout = new FileOutputStream(torrentLocation, false); byte[] buf = new byte[4096]; int read; while((read = downloader.read(buf)) != -1) { fileout.write(buf, 0, read); if(downloader.getState() == Downloader.DOWNLOADER_CANCELED) break; } fileout.flush(); fileout.close(); if(downloader.getState() == Downloader.DOWNLOADER_CANCELED || downloader.getState() == Downloader.DOWNLOADER_ERROR) { downloader.done(); torrentLocation.delete(); return; } if(!downloader.fileName.toLowerCase().endsWith(".torrent")) { Plugin.debugOut("contentType: " + downloader.contentType); if(downloader.contentType != null && downloader.contentType.toLowerCase().startsWith("text/html")) { // html file encountered, look for link to torrent String href = TorrentDownloader.findTorrentHref(torrentLocation, url, null); if(href != null) { Plugin.debugOut("href: " + href); torrentLocation.delete(); downloader.done(); testDownload(href); } else throw new Exception("Html content returned, but no links to torrent files found."); } else if(downloader.contentType != null && !downloader.contentType.toLowerCase().startsWith("application/x-bittorrent")) { // something else encountered, just move it to outputdir System.out.println("other " + downloader.fileName); } } System.out.println("torrent?"); } public static void main(String[] args) throws Exception { String url = ""; testDownload(url); } */ public void init(String urlStr, String accept, String referer, String cookie, long lastModSince, String oldEtag) { Pattern exprHost = Pattern.compile(".*(https?://.*?)", Pattern.CASE_INSENSITIVE); Matcher m = exprHost.matcher(urlStr); if(m.matches() && !m.group(1).equalsIgnoreCase(urlStr)) urlStr = m.group(1); urlStr = urlStr.replaceAll(" ", "%20"); try { synchronized(listeners) { url = new URL(urlStr); for (int i=0;i<2;i++){ try{ if(url.getProtocol().equalsIgnoreCase("https")) { HttpsURLConnection sslCon = (HttpsURLConnection)url.openConnection(); // allow for certs that contain IP addresses rather than dns names sslCon.setHostnameVerifier(new HostnameVerifier() { public boolean verify(String host, SSLSession session) {return true;} }); con = sslCon; } else { con = url.openConnection(); } con.setDoInput(true); con.setUseCaches(false); if(con instanceof HttpURLConnection) { exprHost = Pattern.compile("https?://([^/]+@)?([^/@:]+)(:[0-9]+)?/.*"); m = exprHost.matcher(urlStr.toLowerCase()); if(m.matches()) con.setRequestProperty("Host", m.group(2)); // isn't this handled automatically? /bow con.setRequestProperty("User-Agent", Plugin.PLUGIN_VERSION); if(referer != null && referer.length() > 0) con.setRequestProperty("Referer", referer); if(accept != null && accept.length() > 0) con.setRequestProperty("Accept", accept); if(cookie != null && cookie.length() > 0) con.setRequestProperty("Cookie", cookie); if(lastModSince > 0) con.setIfModifiedSince(lastModSince); if(oldEtag != null) con.setRequestProperty("If-None-Match", oldEtag); } state = DOWNLOADER_INIT; fireDownloaderUpdate(state, 0, 0, ""); con.connect(); if(con instanceof HttpURLConnection) { int response = ((HttpURLConnection)con).getResponseCode(); Plugin.debugOut("response code: " + response); if(response == -1) { // HttpURLConnection in undefined state? weird stuff... occurs sporadically Thread.sleep(10000); // waiting and trying again seems to do the trick if(refCount++ < 5) { init(urlStr, accept, referer, cookie, lastModSince, oldEtag); return; } } String refresh = con.getHeaderField("Refresh"); if(refresh != null) { Plugin.debugOut("refresh: " + refresh); int idx = refresh.indexOf("url="); if(idx > -1) { refresh = refresh.substring(idx + 4); if(refresh.indexOf(' ') > -1) refresh = refresh.substring(0, refresh.lastIndexOf(' ')); ((HttpURLConnection)con).disconnect(); if(refresh.indexOf("://") == -1) refresh = HtmlAnalyzer.resolveRelativeURL(urlStr, refresh); Plugin.debugOut("new url: " + refresh); if(refCount++ < 3) init(refresh, accept, referer, cookie, lastModSince, oldEtag); } } if(response == HttpURLConnection.HTTP_NOT_MODIFIED) { state = DOWNLOADER_NOTMODIFIED; return; } else if((response != HttpURLConnection.HTTP_ACCEPTED) && (response != HttpURLConnection.HTTP_OK)) { error("Bad response for '" + url.toString() + "': " + Integer.toString(response) + " " + ((HttpURLConnection)con).getResponseMessage()); return; } contentType = con.getContentType(); lastModified = con.getLastModified(); etag = con.getHeaderField("ETag"); url = con.getURL(); // some code to handle b0rked servers. fileName = con.getHeaderField("Content-Disposition"); if((fileName != null) && fileName.toLowerCase().matches(".*attachment.*")) while(fileName.toLowerCase().charAt(0) != 'a') fileName = fileName.substring(1); if((fileName == null) || !fileName.toLowerCase().startsWith("attachment") || (fileName.indexOf('=') == -1)) { String tmp = url.getFile(); if(tmp.lastIndexOf('/') != -1) tmp = tmp.substring(tmp.lastIndexOf('/') + 1); // remove any params in the url int paramPos = tmp.indexOf('?'); if(paramPos != -1) tmp = tmp.substring(0, paramPos); fileName = URLDecoder.decode(tmp, Constants.DEFAULT_ENCODING); } else { fileName = fileName.substring(fileName.indexOf('=') + 1); if(fileName.startsWith("\"") && fileName.endsWith("\"")) fileName = fileName.substring(1, fileName.lastIndexOf('\"')); File temp = new File(fileName); fileName = temp.getName(); } } }catch( SSLException e ){ if ( i == 0 ){ // Plugin.getPluginInterface().getUtilities().getSecurityManager().installServerCertificate( url ); System.out.println("installServerCertificate"); }else{ throw( e ); } } } } } catch(java.net.MalformedURLException e) { e.printStackTrace(); error("Bad URL '" + url + "':" + e.getMessage()); } catch(java.net.UnknownHostException e) { e.printStackTrace(); error("Unknown Host '" + e.getMessage() + "'"); } catch(IOException ioe) { ioe.printStackTrace(); error("Failed: " + ioe.getMessage()); } catch(Throwable e) { e.printStackTrace(); error("Failed: " + e.toString()); } if(state != DOWNLOADER_ERROR) { synchronized(listeners) { state = DOWNLOADER_START; fireDownloaderUpdate(state, 0, 0, ""); state = DOWNLOADER_DOWNLOADING; fireDownloaderUpdate(state, 0, 0, ""); } try { in = con.getInputStream(); size = con.getContentLength(); percentDone = readTotal = 0; } catch(Exception e) { error("Exception while downloading '" + url.toString() + "':" + e.getMessage()); } } } public InputStream getStream() { return in; } public void cancel() { if(state == DOWNLOADER_ERROR) return; synchronized(listeners) { state = DOWNLOADER_CANCELED; fireDownloaderUpdate(state, 0, 0, ""); } } public void done() { if(state == DOWNLOADER_ERROR || state == DOWNLOADER_CANCELED || state == DOWNLOADER_FINISHED) return; synchronized(listeners) { if(state != DOWNLOADER_NOTMODIFIED && readTotal == 0) { error("No data contained in '" + url.toString() + "'"); return; } state = DOWNLOADER_FINISHED; fireDownloaderUpdate(state, 0, 0, ""); } } public void notModified() { if(state == DOWNLOADER_NOTMODIFIED) return; synchronized(listeners) { state = DOWNLOADER_NOTMODIFIED; fireDownloaderUpdate(state, 0, 0, ""); } } protected void finalize() { try { in.close(); } catch(Exception e) {} done(); } // listeners public void addListener(DownloaderListener l) { synchronized(listeners) { listeners.add(l); } } public void removeListener(DownloaderListener l) { synchronized(listeners) { listeners.remove(l); } } private void fireDownloaderUpdate(int state, int percentDone, int readTotal, String str) { for(int i = 0; i < listeners.size(); i++) { ((DownloaderListener)listeners.get(i)).downloaderUpdate(state, percentDone, readTotal, str); } } // InputStream Filtered Stuff public int read() { try { synchronized(listeners) { int read = in.read(); readTotal += read; if(this.size > 0) { this.percentDone = (100 * this.readTotal) / this.size; fireDownloaderUpdate(state, percentDone, 0, ""); } else fireDownloaderUpdate(state, 0, readTotal, ""); return read; } } catch(IOException e) {} return -1; } public int read(byte b[]) { return read(b, 0, b.length); } public int read(byte b[], int off, int len) { try { synchronized(listeners) { int read = in.read(b, off, len); this.readTotal += read; if(this.size > 0) { this.percentDone = (100 * this.readTotal) / this.size; fireDownloaderUpdate(state, percentDone, 0, ""); } else fireDownloaderUpdate(state, 0, readTotal, ""); return read; } } catch(IOException e) { } return -1; } // InputStream PassThru Stuff public long skip(long n) { try { return in.skip(n); } catch(IOException e) { } return 0; } public int available() { try { return in.available(); } catch(IOException e) { } return 0; } public void close() { try { in.close(); } catch(IOException e) { } } public synchronized void mark(int readlimit) { in.mark(readlimit); } public synchronized void reset() { try { in.reset(); } catch(IOException e) { } } public boolean markSupported() { return in.markSupported(); } }