Search in sources :

Example 1 with DiskManagerChannel

use of com.biglybt.pif.disk.DiskManagerChannel in project BiglyBT by BiglySoftware.

the class ManagerUtils method browse.

public static String browse(final DownloadManager dm, DiskManagerFileInfo _file, final boolean anon, final boolean launch) {
    Properties props = new Properties();
    File save_location = dm.getSaveLocation();
    final String root_dir;
    if (save_location.isFile()) {
        root_dir = save_location.getParentFile().getAbsolutePath();
    } else {
        root_dir = save_location.getAbsolutePath();
    }
    final String url_suffix;
    boolean always_browse = COConfigurationManager.getBooleanParameter("Library.LaunchWebsiteInBrowserDirList");
    if (!always_browse) {
        if (_file == null) {
            _file = getBrowseHomePage(dm);
        }
    }
    final DiskManagerFileInfo file = _file;
    if (file == null) {
        // asked to launch a download (note that the double-click on a download that has an index.html file will by default result in
        // us getting here with the file set, not null)
        url_suffix = "";
    } else {
        String relative_path = file.getTorrentFile().getRelativePath();
        String[] bits = relative_path.replace(File.separatorChar, '/').split("/");
        String _url_suffix = "";
        int bits_to_use = always_browse ? bits.length - 1 : bits.length;
        for (int i = 0; i < bits_to_use; i++) {
            String bit = bits[i];
            if (bit.length() == 0) {
                continue;
            }
            _url_suffix += (_url_suffix == "" ? "" : "/") + UrlUtils.encode(bit);
        }
        url_suffix = _url_suffix;
    }
    synchronized (browse_plugins) {
        WebPlugin plugin = browse_plugins.get(dm);
        if (plugin == null) {
            props.put(WebPlugin.PR_PORT, 0);
            props.put(WebPlugin.PR_BIND_IP, "127.0.0.1");
            props.put(WebPlugin.PR_HOME_PAGE, "");
            props.put(WebPlugin.PR_ROOT_DIR, root_dir);
            props.put(WebPlugin.PR_ACCESS, "local");
            props.put(WebPlugin.PR_HIDE_RESOURCE_CONFIG, true);
            props.put(WebPlugin.PR_ENABLE_KEEP_ALIVE, true);
            props.put(WebPlugin.PR_ENABLE_PAIRING, false);
            props.put(WebPlugin.PR_ENABLE_UPNP, false);
            props.put(WebPlugin.PR_ENABLE_I2P, false);
            props.put(WebPlugin.PR_ENABLE_TOR, false);
            final String plugin_id = "webserver:" + dm.getInternalName();
            final String plugin_name = "Web Server for " + dm.getDisplayName();
            Properties messages = new Properties();
            messages.put("plugins." + plugin_id, plugin_name);
            PluginInitializer.getDefaultInterface().getUtilities().getLocaleUtilities().integrateLocalisedMessageBundle(messages);
            final AESemaphore waiter = new AESemaphore("waiter");
            final String[] url_holder = { null };
            plugin = new UnloadableWebPlugin(props) {

                private Map<String, Object> file_map = new HashMap<>();

                private String protocol;

                private String host;

                private int port;

                @Override
                public void initialize(PluginInterface plugin_interface) throws PluginException {
                    DiskManagerFileInfoSet file_set = dm.getDiskManagerFileInfoSet();
                    DiskManagerFileInfo[] files = file_set.getFiles();
                    Set<Object> root_dir = new HashSet<>();
                    file_map.put("", root_dir);
                    for (DiskManagerFileInfo dm_file : files) {
                        TOTorrentFile file = dm_file.getTorrentFile();
                        String path = file.getRelativePath();
                        file_map.put(path, dm_file);
                        if (path.startsWith(File.separator)) {
                            path = path.substring(1);
                        }
                        Set<Object> dir = root_dir;
                        int pos = 0;
                        while (true) {
                            int next_pos = path.indexOf(File.separatorChar, pos);
                            if (next_pos == -1) {
                                dir.add(dm_file);
                                break;
                            } else {
                                String bit = path.substring(pos, next_pos);
                                dir.add(bit);
                                String sub_path = path.substring(0, next_pos);
                                dir = (Set<Object>) file_map.get(sub_path);
                                if (dir == null) {
                                    dir = new HashSet<>();
                                    file_map.put(sub_path, dir);
                                }
                                pos = next_pos + 1;
                            }
                        }
                    }
                    Properties props = plugin_interface.getPluginProperties();
                    props.put("plugin.name", plugin_name);
                    super.initialize(plugin_interface);
                    InetAddress bind_ip = getServerBindIP();
                    if (bind_ip.isAnyLocalAddress()) {
                        host = "127.0.0.1";
                    } else {
                        host = bind_ip.getHostAddress();
                    }
                    port = getServerPort();
                    log("Assigned port: " + port);
                    protocol = getProtocol();
                    String url = protocol + "://" + host + ":" + port + "/" + url_suffix;
                    if (launch) {
                        Utils.launch(url, false, true, anon);
                    } else {
                        synchronized (url_holder) {
                            url_holder[0] = url;
                        }
                        waiter.release();
                    }
                }

                @Override
                public boolean generate(TrackerWebPageRequest request, TrackerWebPageResponse response) throws IOException {
                    try {
                        boolean res = super.generate(request, response);
                        if (!res) {
                            response.setReplyStatus(404);
                        }
                    } catch (Throwable e) {
                        response.setReplyStatus(404);
                    }
                    return (true);
                }

                @Override
                protected boolean useFile(TrackerWebPageRequest request, final TrackerWebPageResponse response, File root, String relative_url) throws IOException {
                    URL absolute_url = request.getAbsoluteURL();
                    String query = absolute_url.getQuery();
                    if (query != null) {
                        String[] args = query.split("&");
                        String vuze_source = null;
                        int vuze_file_index = -1;
                        String vuze_file_name = null;
                        List<String> networks = new ArrayList<>();
                        for (String arg : args) {
                            String[] bits = arg.split("=");
                            String lhs = bits[0];
                            String rhs = UrlUtils.decode(bits[1]);
                            if (lhs.equals("vuze_source")) {
                                if (rhs.endsWith(".torrent") || rhs.startsWith("magnet")) {
                                    vuze_source = rhs;
                                }
                            } else if (lhs.equals("vuze_file_index")) {
                                vuze_file_index = Integer.parseInt(rhs);
                            } else if (lhs.equals("vuze_file_name")) {
                                vuze_file_name = rhs;
                            } else if (lhs.equals("vuze_network")) {
                                String net = AENetworkClassifier.internalise(rhs);
                                if (net != null) {
                                    networks.add(net);
                                }
                            }
                        }
                        if (vuze_source != null) {
                            String referrer = (String) request.getHeaders().get("referer");
                            if (referrer == null || !referrer.contains("://" + host + ":" + port)) {
                                response.setReplyStatus(403);
                                return (true);
                            }
                            if (vuze_source.endsWith(".torrent")) {
                                Object file_node = file_map.get(vuze_source);
                                if (file_node instanceof DiskManagerFileInfo) {
                                    DiskManagerFileInfo dm_file = (DiskManagerFileInfo) file_node;
                                    long file_size = dm_file.getLength();
                                    File target_file = dm_file.getFile(true);
                                    boolean done = dm_file.getDownloaded() == file_size && target_file.length() == file_size;
                                    if (done) {
                                        return (handleRedirect(dm, target_file, vuze_file_index, vuze_file_name, networks, request, response));
                                    } else {
                                        try {
                                            File torrent_file = AETemporaryFileHandler.createTempFile();
                                            final FileOutputStream fos = new FileOutputStream(torrent_file);
                                            try {
                                                DiskManagerChannel chan = PluginCoreUtils.wrap(dm_file).createChannel();
                                                try {
                                                    final DiskManagerRequest req = chan.createRequest();
                                                    req.setOffset(0);
                                                    req.setLength(file_size);
                                                    req.addListener(new DiskManagerListener() {

                                                        @Override
                                                        public void eventOccurred(DiskManagerEvent event) {
                                                            int type = event.getType();
                                                            if (type == DiskManagerEvent.EVENT_TYPE_BLOCKED) {
                                                                return;
                                                            } else if (type == DiskManagerEvent.EVENT_TYPE_FAILED) {
                                                                throw (new RuntimeException(event.getFailure()));
                                                            }
                                                            PooledByteBuffer buffer = event.getBuffer();
                                                            if (buffer == null) {
                                                                throw (new RuntimeException("eh?"));
                                                            }
                                                            try {
                                                                byte[] data = buffer.toByteArray();
                                                                fos.write(data);
                                                            } catch (IOException e) {
                                                                throw (new RuntimeException("Failed to write to " + file, e));
                                                            } finally {
                                                                buffer.returnToPool();
                                                            }
                                                        }
                                                    });
                                                    req.run();
                                                } finally {
                                                    chan.destroy();
                                                }
                                            } finally {
                                                fos.close();
                                            }
                                            return (handleRedirect(dm, torrent_file, vuze_file_index, vuze_file_name, networks, request, response));
                                        } catch (Throwable e) {
                                            Debug.out(e);
                                            return (false);
                                        }
                                    }
                                } else {
                                    return (false);
                                }
                            } else {
                                URL magnet = new URL(vuze_source);
                                File torrent_file = AETemporaryFileHandler.createTempFile();
                                try {
                                    URLConnection connection = magnet.openConnection();
                                    connection.connect();
                                    FileUtil.copyFile(connection.getInputStream(), torrent_file.getAbsoluteFile());
                                    return (handleRedirect(dm, torrent_file, vuze_file_index, vuze_file_name, networks, request, response));
                                } catch (Throwable e) {
                                    Debug.out(e);
                                }
                            }
                        }
                    }
                    String path = absolute_url.getPath();
                    if (path.equals("/")) {
                        if (COConfigurationManager.getBooleanParameter("Library.LaunchWebsiteInBrowserDirList")) {
                            relative_url = "/";
                        }
                    }
                    String download_name = XUXmlWriter.escapeXML(dm.getDisplayName());
                    String relative_file = relative_url.replace('/', File.separatorChar);
                    String node_key = relative_file.substring(1);
                    Object file_node = file_map.get(node_key);
                    boolean file_node_is_parent = false;
                    if (file_node == null) {
                        int pos = node_key.lastIndexOf(File.separator);
                        if (pos == -1) {
                            node_key = "";
                        } else {
                            node_key = node_key.substring(0, pos);
                        }
                        file_node = file_map.get(node_key);
                        file_node_is_parent = true;
                    }
                    if (file_node == null) {
                        return (false);
                    }
                    if (file_node instanceof Set) {
                        if (relative_url.equals("/favicon.ico")) {
                            try {
                                InputStream stream = getClass().getClassLoader().getResourceAsStream("com/biglybt/ui/icons/favicon.ico");
                                response.useStream("image/x-icon", stream);
                                return (true);
                            } catch (Throwable e) {
                            }
                        }
                        Set<Object> kids = (Set<Object>) file_node;
                        String request_url = request.getURL();
                        if (file_node_is_parent) {
                            int pos = request_url.lastIndexOf("/");
                            if (pos == -1) {
                                request_url = "";
                            } else {
                                request_url = request_url.substring(0, pos);
                            }
                        }
                        response.setContentType("text/html");
                        OutputStream os = response.getOutputStream();
                        String title = XUXmlWriter.escapeXML(UrlUtils.decode(request_url));
                        if (title.length() == 0) {
                            title = "/";
                        }
                        os.write(("<html>" + NL + " <head>" + NL + " <meta charset=\"UTF-8\">" + NL + "  <title>" + download_name + ": Index of " + title + "</title>" + NL + " </head>" + NL + " <body>" + NL + "  <p>" + download_name + "</p>" + NL + "  <h1>Index of " + title + "</h1>" + NL + "  <pre><hr>" + NL).getBytes("UTF-8"));
                        String root_url = request_url;
                        if (!root_url.endsWith("/")) {
                            root_url += "/";
                        }
                        if (request_url.length() > 1) {
                            int pos = request_url.lastIndexOf('/');
                            if (pos == 0) {
                                pos++;
                            }
                            String parent = request_url.substring(0, pos);
                            os.write(("<a href=\"" + parent + "\">..</a>" + NL).getBytes("UTF-8"));
                        }
                        List<String[]> filenames = new ArrayList<>(kids.size());
                        int max_filename = 0;
                        int MAX_LEN = 120;
                        for (Object entry : kids) {
                            DiskManagerFileInfo file;
                            String file_name;
                            if (entry instanceof String) {
                                file = null;
                                file_name = (String) entry;
                            } else {
                                file = (DiskManagerFileInfo) entry;
                                if (file.isSkipped()) {
                                    continue;
                                }
                                file_name = file.getTorrentFile().getRelativePath();
                                int pos = file_name.lastIndexOf(File.separatorChar);
                                if (pos != -1) {
                                    file_name = file_name.substring(pos + 1);
                                }
                            }
                            String url = root_url + UrlUtils.encode(file_name);
                            if (file == null) {
                                file_name += "/";
                            }
                            int len = file_name.length();
                            if (len > MAX_LEN) {
                                file_name = file_name.substring(0, MAX_LEN - 3) + "...";
                                len = file_name.length();
                            }
                            if (len > max_filename) {
                                max_filename = len;
                            }
                            filenames.add(new String[] { url, file_name, file == null ? "" : DisplayFormatters.formatByteCountToKiBEtc(file.getLength()) });
                        }
                        max_filename = ((max_filename + 15) / 8) * 8;
                        char[] padding = new char[max_filename];
                        Arrays.fill(padding, ' ');
                        Collections.sort(filenames, new Comparator<String[]>() {

                            Comparator comp = new FormattersImpl().getAlphanumericComparator(true);

                            @Override
                            public int compare(String[] o1, String[] o2) {
                                return (comp.compare(o1[0], o2[0]));
                            }
                        });
                        for (String[] entry : filenames) {
                            String file_name = entry[1];
                            int len = file_name.length();
                            StringBuilder line = new StringBuilder(max_filename + 64);
                            line.append("<a href=\"").append(entry[0]).append("\">").append(XUXmlWriter.escapeXML(file_name)).append("</a>");
                            line.append(padding, 0, max_filename - len);
                            line.append(entry[2]);
                            line.append(NL);
                            os.write(line.toString().getBytes("UTF-8"));
                        }
                        os.write(("  <hr></pre>" + NL + "  <address>" + Constants.APP_NAME + " Web Server at " + host + " Port " + getServerPort() + "</address>" + NL + " </body>" + NL + "</html>").getBytes("UTF-8"));
                        return (true);
                    } else {
                        DiskManagerFileInfo dm_file = (DiskManagerFileInfo) file_node;
                        long file_size = dm_file.getLength();
                        File target_file = dm_file.getFile(true);
                        boolean done = dm_file.getDownloaded() == file_size && target_file.length() == file_size;
                        String file_type;
                        // Use the original torrent file name when deducing file type to
                        // avoid incomplete suffix issues etc
                        String relative_path = dm_file.getTorrentFile().getRelativePath();
                        int pos = relative_path.lastIndexOf(".");
                        if (pos == -1) {
                            file_type = "";
                        } else {
                            file_type = relative_path.substring(pos + 1);
                        }
                        if (file_size >= 512 * 1024) {
                            String content_type = HTTPUtils.guessContentTypeFromFileType(file_type);
                            if (content_type.startsWith("text/") || content_type.startsWith("image/")) {
                            // don't want to be redirecting here as (for example) .html needs
                            // to remain in the 'correct' place so that relative assets work
                            } else {
                                URL stream_url = getMediaServerContentURL(dm_file);
                                if (stream_url != null) {
                                    OutputStream os = response.getRawOutputStream();
                                    os.write(("HTTP/1.1 302 Found" + NL + "Location: " + stream_url.toExternalForm() + NL + NL).getBytes("UTF-8"));
                                    return (true);
                                }
                            }
                        }
                        if (done) {
                            if (file_size < 512 * 1024) {
                                FileInputStream fis = null;
                                try {
                                    fis = new FileInputStream(target_file);
                                    response.useStream(file_type, fis);
                                    return (true);
                                } finally {
                                    if (fis != null) {
                                        fis.close();
                                    }
                                }
                            } else {
                                OutputStream os = null;
                                InputStream is = null;
                                try {
                                    os = response.getRawOutputStream();
                                    os.write(("HTTP/1.1 200 OK" + NL + "Content-Type:" + HTTPUtils.guessContentTypeFromFileType(file_type) + NL + "Content-Length: " + file_size + NL + "Connection: close" + NL + NL).getBytes("UTF-8"));
                                    byte[] buffer = new byte[128 * 1024];
                                    is = new FileInputStream(target_file);
                                    while (true) {
                                        int len = is.read(buffer);
                                        if (len <= 0) {
                                            break;
                                        }
                                        os.write(buffer, 0, len);
                                    }
                                } catch (Throwable e) {
                                // e.printStackTrace();
                                } finally {
                                    try {
                                        os.close();
                                    } catch (Throwable e) {
                                    }
                                    try {
                                        is.close();
                                    } catch (Throwable e) {
                                    }
                                }
                                return (true);
                            }
                        } else {
                            dm_file.setPriority(10);
                            try {
                                final OutputStream os = response.getRawOutputStream();
                                os.write(("HTTP/1.1 200 OK" + NL + "Content-Type:" + HTTPUtils.guessContentTypeFromFileType(file_type) + NL + "Content-Length: " + file_size + NL + "Connection: close" + NL + "X-Vuze-Hack: X").getBytes("UTF-8"));
                                DiskManagerChannel chan = PluginCoreUtils.wrap(dm_file).createChannel();
                                try {
                                    final DiskManagerRequest req = chan.createRequest();
                                    final boolean[] header_complete = { false };
                                    final long[] last_write = { 0 };
                                    req.setOffset(0);
                                    req.setLength(file_size);
                                    req.addListener(new DiskManagerListener() {

                                        @Override
                                        public void eventOccurred(DiskManagerEvent event) {
                                            int type = event.getType();
                                            if (type == DiskManagerEvent.EVENT_TYPE_BLOCKED) {
                                                return;
                                            } else if (type == DiskManagerEvent.EVENT_TYPE_FAILED) {
                                                throw (new RuntimeException(event.getFailure()));
                                            }
                                            PooledByteBuffer buffer = event.getBuffer();
                                            if (buffer == null) {
                                                throw (new RuntimeException("eh?"));
                                            }
                                            try {
                                                boolean do_header = false;
                                                synchronized (header_complete) {
                                                    if (!header_complete[0]) {
                                                        do_header = true;
                                                        header_complete[0] = true;
                                                    }
                                                    last_write[0] = SystemTime.getMonotonousTime();
                                                }
                                                if (do_header) {
                                                    os.write((NL + NL).getBytes("UTF-8"));
                                                }
                                                byte[] data = buffer.toByteArray();
                                                os.write(data);
                                            } catch (IOException e) {
                                                throw (new RuntimeException("Failed to write to " + file, e));
                                            } finally {
                                                buffer.returnToPool();
                                            }
                                        }
                                    });
                                    final TimerEventPeriodic[] timer_event = { null };
                                    timer_event[0] = SimpleTimer.addPeriodicEvent("KeepAlive", 10 * 1000, new TimerEventPerformer() {

                                        boolean cancel_outstanding = false;

                                        @Override
                                        public void perform(TimerEvent event) {
                                            if (cancel_outstanding) {
                                                req.cancel();
                                            } else {
                                                synchronized (header_complete) {
                                                    if (header_complete[0]) {
                                                        if (SystemTime.getMonotonousTime() - last_write[0] >= 5 * 60 * 1000) {
                                                            req.cancel();
                                                        }
                                                    } else {
                                                        try {
                                                            os.write("X".getBytes("UTF-8"));
                                                            os.flush();
                                                        } catch (Throwable e) {
                                                            req.cancel();
                                                        }
                                                    }
                                                }
                                                if (!response.isActive()) {
                                                    cancel_outstanding = true;
                                                }
                                            }
                                        }
                                    });
                                    try {
                                        req.run();
                                    } finally {
                                        timer_event[0].cancel();
                                    }
                                    return (true);
                                } finally {
                                    chan.destroy();
                                }
                            } catch (Throwable e) {
                                return (false);
                            }
                        }
                    }
                }

                private boolean handleRedirect(DownloadManager dm, File torrent_file, int file_index, String file_name, List<String> networks, TrackerWebPageRequest request, TrackerWebPageResponse response) {
                    try {
                        TOTorrent torrent = TOTorrentFactory.deserialiseFromBEncodedFile(torrent_file);
                        GlobalManager gm = CoreFactory.getSingleton().getGlobalManager();
                        UIFunctions uif = UIFunctionsManager.getUIFunctions();
                        TorrentOpenOptions torrent_options = new TorrentOpenOptions(torrent_file.getAbsolutePath(), torrent, false, null);
                        torrent_options.setTorrent(torrent);
                        String[] existing_nets;
                        if (networks.size() == 0) {
                            // inherit networks from parent
                            existing_nets = dm.getDownloadState().getNetworks();
                        } else {
                            existing_nets = networks.toArray(new String[networks.size()]);
                        }
                        for (String net : AENetworkClassifier.AT_NETWORKS) {
                            boolean found = false;
                            for (String x : existing_nets) {
                                if (net == x) {
                                    found = true;
                                    break;
                                }
                            }
                            torrent_options.setNetworkEnabled(net, found);
                        }
                        Map<String, Object> add_options = new HashMap<>();
                        add_options.put(UIFunctions.OTO_SILENT, true);
                        if (uif.addTorrentWithOptions(torrent_options, add_options)) {
                            long start = SystemTime.getMonotonousTime();
                            while (true) {
                                DownloadManager o_dm = gm.getDownloadManager(torrent);
                                if (o_dm != null) {
                                    if (!o_dm.getDownloadState().getFlag(DownloadManagerState.FLAG_METADATA_DOWNLOAD)) {
                                        DiskManagerFileInfo[] files = o_dm.getDiskManagerFileInfoSet().getFiles();
                                        DiskManagerFileInfo o_dm_file = null;
                                        if (file_name != null) {
                                            for (DiskManagerFileInfo file : files) {
                                                String path = file.getTorrentFile().getRelativePath();
                                                if (path.equals(file_name)) {
                                                    o_dm_file = file;
                                                    break;
                                                }
                                            }
                                            if (o_dm_file == null) {
                                                o_dm_file = files[0];
                                            }
                                        } else {
                                            if (file_index < 0) {
                                                long largest = -1;
                                                for (DiskManagerFileInfo file : files) {
                                                    if (file.getLength() > largest) {
                                                        o_dm_file = file;
                                                        largest = file.getLength();
                                                    }
                                                }
                                            } else {
                                                o_dm_file = files[file_index];
                                            }
                                        }
                                        String original_path = request.getAbsoluteURL().getPath();
                                        if (original_path.endsWith(".html")) {
                                            String url = browse(o_dm, file_index < 0 ? null : o_dm_file, anon, false);
                                            OutputStream os = response.getRawOutputStream();
                                            os.write(("HTTP/1.1 302 Found" + NL + "Location: " + url + NL + NL).getBytes("UTF-8"));
                                            return (true);
                                        } else {
                                            URL stream_url = getMediaServerContentURL(o_dm_file);
                                            if (stream_url != null) {
                                                OutputStream os = response.getRawOutputStream();
                                                os.write(("HTTP/1.1 302 Found" + NL + "Location: " + stream_url.toExternalForm() + NL + NL).getBytes("UTF-8"));
                                                return (true);
                                            }
                                        }
                                    }
                                }
                                long now = SystemTime.getMonotonousTime();
                                if (now - start > 3 * 60 * 1000) {
                                    Debug.out("Timeout waiting for download to be added");
                                    return (false);
                                }
                                Thread.sleep(1000);
                            }
                        } else {
                            Debug.out("Failed to add download for some reason");
                            return (false);
                        }
                    } catch (Throwable e) {
                        Debug.out(e);
                        return (false);
                    }
                }

                @Override
                public void unload() throws PluginException {
                    synchronized (browse_plugins) {
                        browse_plugins.remove(dm);
                    }
                    super.unload();
                }
            };
            PluginManager.registerPlugin(plugin, plugin_id, plugin_id);
            browse_plugins.put(dm, plugin);
            if (launch) {
                return (null);
            } else {
                waiter.reserve(10 * 1000);
                synchronized (url_holder) {
                    return (url_holder[0]);
                }
            }
        } else {
            String protocol = plugin.getProtocol();
            InetAddress bind_ip = plugin.getServerBindIP();
            String host;
            if (bind_ip.isAnyLocalAddress()) {
                host = "127.0.0.1";
            } else {
                host = bind_ip.getHostAddress();
            }
            String url = protocol + "://" + host + ":" + plugin.getServerPort() + "/" + url_suffix;
            if (launch) {
                Utils.launch(url, false, true, anon);
                return (null);
            } else {
                return (url);
            }
        }
    }
}
Also used : TrackerWebPageRequest(com.biglybt.pif.tracker.web.TrackerWebPageRequest) TrackerWebPageResponse(com.biglybt.pif.tracker.web.TrackerWebPageResponse) DownloadManager(com.biglybt.core.download.DownloadManager) GlobalManager(com.biglybt.core.global.GlobalManager) DiskManagerListener(com.biglybt.pif.disk.DiskManagerListener) UIFunctions(com.biglybt.ui.UIFunctions) SWTSkinObjectList(com.biglybt.ui.swt.skin.SWTSkinObjectList) List(java.util.List) DiskManagerChannel(com.biglybt.pif.disk.DiskManagerChannel) PluginException(com.biglybt.pif.PluginException) DiskManagerFileInfoSet(com.biglybt.core.disk.DiskManagerFileInfoSet) TOTorrentFile(com.biglybt.core.torrent.TOTorrentFile) SWTSkinObject(com.biglybt.ui.swt.skin.SWTSkinObject) TOTorrentFile(com.biglybt.core.torrent.TOTorrentFile) InetAddress(java.net.InetAddress) DiskManagerFileInfoSet(com.biglybt.core.disk.DiskManagerFileInfoSet) URL(java.net.URL) TorrentOpenOptions(com.biglybt.core.torrent.impl.TorrentOpenOptions) PooledByteBuffer(com.biglybt.pif.utils.PooledByteBuffer) WebPlugin(com.biglybt.ui.webplugin.WebPlugin) DiskManagerFileInfo(com.biglybt.core.disk.DiskManagerFileInfo) DiskManagerRequest(com.biglybt.pif.disk.DiskManagerRequest) PluginInterface(com.biglybt.pif.PluginInterface) FormattersImpl(com.biglybt.pifimpl.local.utils.FormattersImpl) URLConnection(java.net.URLConnection) TOTorrent(com.biglybt.core.torrent.TOTorrent) DiskManagerEvent(com.biglybt.pif.disk.DiskManagerEvent)

Example 2 with DiskManagerChannel

use of com.biglybt.pif.disk.DiskManagerChannel in project BiglyBT by BiglySoftware.

the class MagnetPluginMDDownloader method startSupport.

private void startSupport(final DownloadListener listener) {
    String hash_str = ByteFormatter.encodeString(hash);
    File tmp_dir = null;
    File data_file = null;
    File torrent_file = null;
    DownloadManager download_manager = plugin_interface.getDownloadManager();
    Download download = null;
    final Throwable[] error = { null };
    final boolean[] manually_removed = { false };
    final ByteArrayOutputStream result = new ByteArrayOutputStream(32 * 1024);
    TOTorrentAnnounceURLSet[] url_sets = null;
    try {
        synchronized (active_set) {
            if (active_set.contains(hash_str)) {
                throw (new Exception("Download already active for hash " + hash_str));
            }
            active_set.add(hash_str);
        }
        Download existing_download = download_manager.getDownload(hash);
        if (existing_download != null) {
            throw (new Exception("download already exists"));
        }
        tmp_dir = AETemporaryFileHandler.createTempDir();
        int rand = RandomUtils.generateRandomIntUpto(10000);
        data_file = new File(tmp_dir, hash_str + "_" + rand + ".torrent");
        torrent_file = new File(tmp_dir, hash_str + "_" + rand + ".metatorrent");
        RandomAccessFile raf = new RandomAccessFile(data_file, "rw");
        try {
            byte[] buffer = new byte[512 * 1024];
            Arrays.fill(buffer, (byte) 0xff);
            for (long i = 0; i < 64 * 1024 * 1024; i += buffer.length) {
                raf.write(buffer);
            }
        } finally {
            raf.close();
        }
        URL announce_url = TorrentUtils.getDecentralisedURL(hash);
        TOTorrentCreator creator = TOTorrentFactory.createFromFileOrDirWithFixedPieceLength(data_file, announce_url, 16 * 1024);
        TOTorrent meta_torrent = creator.create();
        String[] bits = args.split("&");
        List<String> trackers = new ArrayList<>();
        String name = "magnet:" + Base32.encode(hash);
        Map<String, String> magnet_args = new HashMap<>();
        for (String bit : bits) {
            String[] x = bit.split("=");
            if (x.length == 2) {
                String lhs = x[0].toLowerCase();
                String rhs = UrlUtils.decode(x[1]);
                magnet_args.put(lhs, rhs);
                if (lhs.equals("tr")) {
                    String tracker = rhs;
                    trackers.add(tracker);
                } else if (lhs.equals("dn")) {
                    name = rhs;
                }
            }
        }
        if (trackers.size() > 0) {
            // stick the decentralised one we created above in position 0 - this will be
            // removed later if the torrent is downloaded
            trackers.add(0, announce_url.toExternalForm());
            TOTorrentAnnounceURLGroup ag = meta_torrent.getAnnounceURLGroup();
            List<TOTorrentAnnounceURLSet> sets = new ArrayList<>();
            for (String tracker : trackers) {
                try {
                    URL tracker_url = new URL(tracker);
                    sets.add(ag.createAnnounceURLSet(new URL[] { tracker_url }));
                } catch (Throwable e) {
                    Debug.out(e);
                }
            }
            if (sets.size() > 0) {
                url_sets = sets.toArray(new TOTorrentAnnounceURLSet[sets.size()]);
                ag.setAnnounceURLSets(url_sets);
            }
        }
        if (!data_file.delete()) {
            throw (new Exception("Failed to delete " + data_file));
        }
        meta_torrent.setHashOverride(hash);
        TorrentUtils.setFlag(meta_torrent, TorrentUtils.TORRENT_FLAG_METADATA_TORRENT, true);
        TorrentUtils.setFlag(meta_torrent, TorrentUtils.TORRENT_FLAG_LOW_NOISE, true);
        meta_torrent.serialiseToBEncodedFile(torrent_file);
        download_manager.clearNonPersistentDownloadState(hash);
        download = download_manager.addNonPersistentDownloadStopped(PluginCoreUtils.wrap(meta_torrent), torrent_file, data_file);
        String display_name = MessageText.getString("MagnetPlugin.use.md.download.name", new String[] { name });
        DownloadManagerState state = PluginCoreUtils.unwrap(download).getDownloadState();
        state.setDisplayName(display_name + ".torrent");
        if (networks.size() == 0 || (networks.size() == 1 && networks.contains(AENetworkClassifier.AT_PUBLIC))) {
            for (String network : AENetworkClassifier.AT_NETWORKS) {
                state.setNetworkEnabled(network, true);
            }
        } else {
            for (String network : networks) {
                state.setNetworkEnabled(network, true);
            }
            if (!networks.contains(AENetworkClassifier.AT_PUBLIC)) {
                state.setNetworkEnabled(AENetworkClassifier.AT_PUBLIC, false);
            }
        }
        if (!plugin.isNetworkEnabled(AENetworkClassifier.AT_PUBLIC)) {
            state.setNetworkEnabled(AENetworkClassifier.AT_PUBLIC, false);
        }
        final List<InetSocketAddress> peers_to_inject = new ArrayList<>();
        if (addresses != null && addresses.length > 0) {
            String[] enabled_nets = state.getNetworks();
            for (InetSocketAddress address : addresses) {
                String host = AddressUtils.getHostAddress(address);
                String net = AENetworkClassifier.categoriseAddress(host);
                for (String n : enabled_nets) {
                    if (n == net) {
                        peers_to_inject.add(address);
                        break;
                    }
                }
            }
        }
        final Set<String> peer_networks = new HashSet<>();
        final List<Map<String, Object>> peers_for_cache = new ArrayList<>();
        download.addPeerListener(new DownloadPeerListener() {

            @Override
            public void peerManagerAdded(final Download download, final PeerManager peer_manager) {
                if (cancelled || completed) {
                    download.removePeerListener(this);
                    return;
                }
                final PEPeerManager pm = PluginCoreUtils.unwrap(peer_manager);
                peer_manager.addListener(new PeerManagerListener2() {

                    private PeerManagerListener2 pm_listener = this;

                    private int md_size;

                    @Override
                    public void eventOccurred(PeerManagerEvent event) {
                        if (cancelled || completed) {
                            peer_manager.removeListener(this);
                            return;
                        }
                        if (event.getType() != PeerManagerEvent.ET_PEER_ADDED) {
                            return;
                        }
                        final Peer peer = event.getPeer();
                        try {
                            String peer_ip = peer.getIp();
                            String network = AENetworkClassifier.categoriseAddress(peer_ip);
                            synchronized (peer_networks) {
                                peer_networks.add(network);
                                Map<String, Object> map = new HashMap<>();
                                peers_for_cache.add(map);
                                map.put("ip", peer_ip.getBytes("UTF-8"));
                                map.put("port", new Long(peer.getPort()));
                            }
                        } catch (Throwable e) {
                            Debug.out(e);
                        }
                        peer.addListener(new PeerListener2() {

                            @Override
                            public void eventOccurred(PeerEvent event) {
                                if (cancelled || completed || md_size > 0) {
                                    peer.removeListener(this);
                                    return;
                                }
                                if (event.getType() != PeerEvent.ET_STATE_CHANGED) {
                                    return;
                                }
                                if ((Integer) event.getData() != Peer.TRANSFERING) {
                                    return;
                                }
                                synchronized (pm_listener) {
                                    if (md_size > 0) {
                                        return;
                                    }
                                    md_size = pm.getTorrentInfoDictSize();
                                    if (md_size > 0) {
                                        peer_manager.removeListener(pm_listener);
                                    } else {
                                        return;
                                    }
                                }
                                listener.reportProgress(0, md_size);
                                new AEThread2("") {

                                    @Override
                                    public void run() {
                                        DiskManagerChannel channel = null;
                                        try {
                                            channel = download.getDiskManagerFileInfo()[0].createChannel();
                                            final DiskManagerRequest request = channel.createRequest();
                                            request.setType(DiskManagerRequest.REQUEST_READ);
                                            request.setOffset(0);
                                            request.setLength(md_size);
                                            request.setMaximumReadChunkSize(16 * 1024);
                                            request.addListener(new DiskManagerListener() {

                                                @Override
                                                public void eventOccurred(DiskManagerEvent event) {
                                                    int type = event.getType();
                                                    if (type == DiskManagerEvent.EVENT_TYPE_FAILED) {
                                                        error[0] = event.getFailure();
                                                        running_sem.releaseForever();
                                                    } else if (type == DiskManagerEvent.EVENT_TYPE_SUCCESS) {
                                                        PooledByteBuffer buffer = null;
                                                        try {
                                                            buffer = event.getBuffer();
                                                            byte[] bytes = buffer.toByteArray();
                                                            int dl_size;
                                                            synchronized (MagnetPluginMDDownloader.this) {
                                                                result.write(bytes);
                                                                dl_size = result.size();
                                                                if (dl_size == md_size) {
                                                                    completed = true;
                                                                    listener.reportProgress(md_size, md_size);
                                                                    running_sem.releaseForever();
                                                                }
                                                            }
                                                            if (!completed) {
                                                                listener.reportProgress(dl_size, md_size);
                                                            }
                                                        } catch (Throwable e) {
                                                            error[0] = e;
                                                            request.cancel();
                                                            running_sem.releaseForever();
                                                        } finally {
                                                            if (buffer != null) {
                                                                buffer.returnToPool();
                                                            }
                                                        }
                                                    } else if (type == DiskManagerEvent.EVENT_TYPE_BLOCKED) {
                                                    // System.out.println( "Waiting..." );
                                                    }
                                                }
                                            });
                                            synchronized (MagnetPluginMDDownloader.this) {
                                                if (cancelled) {
                                                    return;
                                                }
                                                requests.add(request);
                                            }
                                            request.run();
                                            synchronized (MagnetPluginMDDownloader.this) {
                                                requests.remove(request);
                                            }
                                        } catch (Throwable e) {
                                            error[0] = e;
                                            running_sem.releaseForever();
                                        } finally {
                                            if (channel != null) {
                                                channel.destroy();
                                            }
                                        }
                                    }
                                }.start();
                            }
                        });
                    }
                });
            }

            @Override
            public void peerManagerRemoved(Download download, PeerManager peer_manager) {
            }
        });
        final Download f_download = download;
        DownloadManagerListener dl_listener = new DownloadManagerListener() {

            private Object lock = this;

            private TimerEventPeriodic timer_event;

            private boolean removed;

            @Override
            public void downloadAdded(final Download download) {
                if (download == f_download) {
                    synchronized (lock) {
                        if (!removed) {
                            if (timer_event == null) {
                                timer_event = SimpleTimer.addPeriodicEvent("announcer", 30 * 1000, new TimerEventPerformer() {

                                    @Override
                                    public void perform(TimerEvent event) {
                                        synchronized (lock) {
                                            if (removed) {
                                                return;
                                            }
                                            if (running_sem.isReleasedForever()) {
                                                if (timer_event != null) {
                                                    timer_event.cancel();
                                                    timer_event = null;
                                                }
                                                return;
                                            }
                                        }
                                        download.requestTrackerAnnounce(true);
                                        injectPeers(download);
                                    }
                                });
                            }
                            if (peers_to_inject.size() > 0) {
                                SimpleTimer.addEvent("injecter", SystemTime.getOffsetTime(5 * 1000), new TimerEventPerformer() {

                                    @Override
                                    public void perform(TimerEvent event) {
                                        injectPeers(download);
                                    }
                                });
                            }
                        }
                    }
                }
            }

            private void injectPeers(Download download) {
                PeerManager pm = download.getPeerManager();
                if (pm != null) {
                    for (InetSocketAddress address : peers_to_inject) {
                        pm.addPeer(AddressUtils.getHostAddress(address), address.getPort());
                    }
                }
            }

            @Override
            public void downloadRemoved(Download dl) {
                if (dl == f_download) {
                    synchronized (lock) {
                        removed = true;
                        if (timer_event != null) {
                            timer_event.cancel();
                            timer_event = null;
                        }
                    }
                    if (!(cancelled || completed)) {
                        error[0] = new Exception("Download manually removed");
                        manually_removed[0] = true;
                        running_sem.releaseForever();
                    }
                }
            }
        };
        download_manager.addListener(dl_listener, true);
        try {
            download.moveTo(1);
            download.setForceStart(true);
            download.setFlag(Download.FLAG_DISABLE_AUTO_FILE_MOVE, true);
            running_sem.reserve();
        } finally {
            download_manager.removeListener(dl_listener);
        }
        if (completed) {
            byte[] bytes = result.toByteArray();
            Map info = BDecoder.decode(bytes);
            Map map = new HashMap();
            map.put("info", info);
            TOTorrent torrent = TOTorrentFactory.deserialiseFromMap(map);
            byte[] final_hash = torrent.getHash();
            if (!Arrays.equals(hash, final_hash)) {
                throw (new Exception("Metadata torrent hash mismatch: expected=" + ByteFormatter.encodeString(hash) + ", actual=" + ByteFormatter.encodeString(final_hash)));
            }
            if (url_sets != null) {
                // first entry should be the decentralised one that we want to remove now
                List<TOTorrentAnnounceURLSet> updated = new ArrayList<>();
                for (TOTorrentAnnounceURLSet set : url_sets) {
                    if (!TorrentUtils.isDecentralised(set.getAnnounceURLs()[0])) {
                        updated.add(set);
                    }
                }
                if (updated.size() == 0) {
                    url_sets = null;
                } else {
                    url_sets = updated.toArray(new TOTorrentAnnounceURLSet[updated.size()]);
                }
            }
            if (url_sets != null) {
                torrent.setAnnounceURL(url_sets[0].getAnnounceURLs()[0]);
                torrent.getAnnounceURLGroup().setAnnounceURLSets(url_sets);
            } else {
                torrent.setAnnounceURL(TorrentUtils.getDecentralisedURL(hash));
            }
            if (peers_for_cache.size() > 0) {
                Map<String, List<Map<String, Object>>> peer_cache = new HashMap<>();
                peer_cache.put("tracker_peers", peers_for_cache);
                TorrentUtils.setPeerCache(torrent, peer_cache);
            }
            try {
                String dn = magnet_args.get("dn");
                if (dn != null) {
                    PlatformTorrentUtils.setContentTitle(torrent, dn);
                }
                String pfi_str = magnet_args.get("pfi");
                if (pfi_str != null) {
                    PlatformTorrentUtils.setContentPrimaryFileIndex(torrent, Integer.parseInt(pfi_str));
                }
            } catch (Throwable e) {
            }
            listener.complete(torrent, peer_networks);
        } else {
            if (cancelled) {
                throw (new Exception("Download cancelled"));
            } else {
                cancelSupport(true);
                try {
                    if (error[0] != null) {
                        throw (error[0]);
                    } else {
                        throw (new Exception("Download terminated prematurely"));
                    }
                } catch (Throwable e) {
                    listener.failed(manually_removed[0], e);
                    Debug.out(e);
                    throw (e);
                }
            }
        }
    } catch (Throwable e) {
        boolean was_cancelled = cancelled;
        cancelSupport(true);
        if (!was_cancelled) {
            listener.failed(manually_removed[0], e);
            Debug.out(e);
        }
    } finally {
        try {
            if (download != null) {
                try {
                    download.stop();
                } catch (Throwable e) {
                }
                try {
                    download.remove();
                } catch (Throwable e) {
                    Debug.out(e);
                }
            }
            List<DiskManagerRequest> to_cancel;
            synchronized (this) {
                to_cancel = new ArrayList<>(requests);
                requests.clear();
            }
            for (DiskManagerRequest request : to_cancel) {
                request.cancel();
            }
            if (torrent_file != null) {
                torrent_file.delete();
            }
            if (data_file != null) {
                data_file.delete();
            }
            if (tmp_dir != null) {
                tmp_dir.delete();
            }
        } catch (Throwable e) {
            Debug.out(e);
        } finally {
            synchronized (active_set) {
                active_set.remove(hash_str);
            }
            complete_sem.releaseForever();
        }
    }
}
Also used : InetSocketAddress(java.net.InetSocketAddress) DownloadManager(com.biglybt.pif.download.DownloadManager) DiskManagerListener(com.biglybt.pif.disk.DiskManagerListener) DiskManagerChannel(com.biglybt.pif.disk.DiskManagerChannel) DownloadPeerListener(com.biglybt.pif.download.DownloadPeerListener) RandomAccessFile(java.io.RandomAccessFile) PEPeerManager(com.biglybt.core.peer.PEPeerManager) RandomAccessFile(java.io.RandomAccessFile) File(java.io.File) DownloadManagerListener(com.biglybt.pif.download.DownloadManagerListener) DownloadManagerState(com.biglybt.core.download.DownloadManagerState) URL(java.net.URL) PooledByteBuffer(com.biglybt.pif.utils.PooledByteBuffer) Download(com.biglybt.pif.download.Download) DiskManagerRequest(com.biglybt.pif.disk.DiskManagerRequest) ByteArrayOutputStream(java.io.ByteArrayOutputStream) PEPeerManager(com.biglybt.core.peer.PEPeerManager) DiskManagerEvent(com.biglybt.pif.disk.DiskManagerEvent)

Aggregations

DiskManagerChannel (com.biglybt.pif.disk.DiskManagerChannel)2 DiskManagerEvent (com.biglybt.pif.disk.DiskManagerEvent)2 DiskManagerListener (com.biglybt.pif.disk.DiskManagerListener)2 DiskManagerRequest (com.biglybt.pif.disk.DiskManagerRequest)2 PooledByteBuffer (com.biglybt.pif.utils.PooledByteBuffer)2 URL (java.net.URL)2 DiskManagerFileInfo (com.biglybt.core.disk.DiskManagerFileInfo)1 DiskManagerFileInfoSet (com.biglybt.core.disk.DiskManagerFileInfoSet)1 DownloadManager (com.biglybt.core.download.DownloadManager)1 DownloadManagerState (com.biglybt.core.download.DownloadManagerState)1 GlobalManager (com.biglybt.core.global.GlobalManager)1 PEPeerManager (com.biglybt.core.peer.PEPeerManager)1 TOTorrent (com.biglybt.core.torrent.TOTorrent)1 TOTorrentFile (com.biglybt.core.torrent.TOTorrentFile)1 TorrentOpenOptions (com.biglybt.core.torrent.impl.TorrentOpenOptions)1 PluginException (com.biglybt.pif.PluginException)1 PluginInterface (com.biglybt.pif.PluginInterface)1 Download (com.biglybt.pif.download.Download)1 DownloadManager (com.biglybt.pif.download.DownloadManager)1 DownloadManagerListener (com.biglybt.pif.download.DownloadManagerListener)1