Search in sources :

Example 1 with Peer

use of org.klomp.snark.Peer in project i2p.i2p by i2p.

the class I2PSnarkServlet method displaySnark.

/**
 *  Display one snark (one line in table, unless showPeers is true)
 *
 *  @param stats in/out param (totals)
 *  @param statsOnly if true, output nothing, update stats only
 *  @param canWrite is the i2psnark data directory writable?
 */
private void displaySnark(PrintWriter out, HttpServletRequest req, Snark snark, String uri, int row, long[] stats, boolean showPeers, boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly, boolean showRatios, boolean canWrite) throws IOException {
    // stats
    long uploaded = snark.getUploaded();
    stats[0] += snark.getDownloaded();
    stats[1] += uploaded;
    long downBps = snark.getDownloadRate();
    long upBps = snark.getUploadRate();
    boolean isRunning = !snark.isStopped();
    if (isRunning) {
        stats[2] += downBps;
        stats[3] += upBps;
    }
    int curPeers = snark.getPeerCount();
    stats[4] += curPeers;
    long total = snark.getTotalLength();
    if (total > 0)
        stats[5] += total;
    if (statsOnly)
        return;
    String basename = snark.getBaseName();
    String fullBasename = basename;
    if (basename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
        String start = ServletUtil.truncate(basename, MAX_DISPLAYED_FILENAME_LENGTH);
        if (start.indexOf(' ') < 0 && start.indexOf('-') < 0) {
            // browser has nowhere to break it
            basename = start + HELLIP;
        }
    }
    // includes skipped files, -1 for magnet mode
    long remaining = snark.getRemainingLength();
    if (remaining > total)
        remaining = total;
    // does not include skipped files, -1 for magnet mode or when not running.
    long needed = snark.getNeededLength();
    if (needed > total)
        needed = total;
    long remainingSeconds;
    if (downBps > 0 && needed > 0)
        remainingSeconds = needed / downBps;
    else
        remainingSeconds = -1;
    MetaInfo meta = snark.getMetaInfo();
    String b64 = Base64.encode(snark.getInfoHash());
    String b64Short = b64.substring(0, 6);
    // isValid means isNotMagnet
    boolean isValid = meta != null;
    boolean isMultiFile = isValid && meta.getFiles() != null;
    String err = snark.getTrackerProblems();
    int knownPeers = Math.max(curPeers, snark.getTrackerSeenPeers());
    String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
    String statusString;
    if (snark.isChecking()) {
        statusString = toThemeImg("stalled", "", _t("Checking")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Checking") + "</b>" + ' ' + (new DecimalFormat("0.00%")).format(snark.getCheckingProgress());
    } else if (snark.isAllocating()) {
        statusString = toThemeImg("stalled", "", _t("Allocating")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Allocating") + "</b>";
    } else if (err != null && isRunning && curPeers == 0) {
        // } else if (err != null && curPeers == 0) {
        // Also don't show if seeding... but then we won't see the not-registered error
        // && remaining != 0 && needed != 0) {
        // let's only show this if we have no peers, otherwise PEX and DHT should bail us out, user doesn't care
        // if (isRunning && curPeers > 0 && !showPeers)
        // statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
        // "<td class=\"snarkTorrentStatus " + rowClass + "\">" + _t("Tracker Error") +
        // ": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + "\">" +
        // curPeers + thinsp(noThinsp) +
        // ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
        // else if (isRunning)
        // if (isRunning) {
        statusString = toThemeImg("trackererror", "", err) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Tracker Error") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
    // } else {
    // if (err.length() > MAX_DISPLAYED_ERROR_LENGTH)
    // err = DataHelper.escapeHTML(err.substring(0, MAX_DISPLAYED_ERROR_LENGTH)) + "&hellip;";
    // else
    // err = DataHelper.escapeHTML(err);
    // statusString = toThemeImg("trackererror", "", err) + "</td>" +
    // "<td class=\"snarkTorrentStatus\">" + _t("Tracker Error");
    // }
    } else if (snark.isStarting()) {
        statusString = toThemeImg("stalled", "", _t("Starting")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Starting") + "</b>";
    } else if (remaining == 0 || needed == 0) {
        // partial complete or seeding
        if (isRunning) {
            String img;
            String txt;
            String tooltip;
            if (remaining == 0) {
                img = "seeding";
                txt = _t("Seeding");
                tooltip = _t("Seeding to {0} of {1} peers in swarm", curPeers, knownPeers);
            } else {
                // partial
                img = "complete";
                txt = _t("Complete");
                tooltip = txt;
                if (curPeers > 0) {
                    tooltip = txt + " (" + _t("Seeding to {0} of {1} peers in swarm", curPeers, knownPeers) + ")";
                }
            }
            if (curPeers > 0 && !showPeers) {
                statusString = toThemeImg(img, "", tooltip) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + txt + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
            } else {
                statusString = toThemeImg(img, "", tooltip) + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + txt + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
            }
        } else {
            statusString = toThemeImg("complete", "", _t("Complete")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Complete") + "</b>";
        }
    } else {
        if (isRunning && curPeers > 0 && downBps > 0 && !showPeers) {
            statusString = toThemeImg("downloading", "", _t("OK") + " (" + _t("Downloading from {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("OK") + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
        } else if (isRunning && curPeers > 0 && downBps > 0) {
            statusString = toThemeImg("downloading", "", _t("OK") + " (" + _t("Downloading from {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("OK") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
        } else if (isRunning && curPeers > 0 && !showPeers) {
            statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Stalled") + ":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
        } else if (isRunning && curPeers > 0) {
            statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("Stalled") + ":</b> " + curPeers + thinsp(noThinsp) + ngettext("1 peer", "{0} peers", knownPeers);
        } else if (isRunning && knownPeers > 0) {
            statusString = toThemeImg("nopeers", "", _t("No Peers") + " (" + _t("Connected to {0} of {1} peers in swarm", curPeers, knownPeers) + ")") + "</td>" + "<td class=\"snarkTorrentStatus\"><b>" + _t("No Peers") + ":</b> 0" + thinsp(noThinsp) + knownPeers;
        } else if (isRunning) {
            statusString = toThemeImg("nopeers", "", _t("No Peers")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("No Peers") + "</b>";
        } else {
            statusString = toThemeImg("stopped", "", _t("Stopped")) + "</td>" + "<td class=\"snarkTorrentStatus\"><b class=\"alwaysShow\">" + _t("Stopped") + "</b>";
        }
    }
    out.write("<tr class=\"" + rowClass + "\" id=\"" + b64Short + "\">");
    out.write("<td class=\"snarkGraphicStatus\" align=\"center\">");
    out.write(statusString + "</td>\n\t");
    // (i) icon column
    out.write("<td class=\"snarkTrackerDetails\">");
    if (isValid) {
        String announce = meta.getAnnounce();
        if (announce == null)
            announce = snark.getTrackerURL();
        if (announce != null) {
            // Link to tracker details page
            String trackerLink = getTrackerLink(announce, snark.getInfoHash());
            if (trackerLink != null)
                out.write(trackerLink);
        }
    }
    String encodedBaseName = encodePath(fullBasename);
    // File type icon column
    out.write("</td>\n<td class=\"snarkTorrentDetails\">");
    if (isValid) {
        // Link to local details page - note that trailing slash on a single-file torrent
        // gets us to the details page instead of the file.
        StringBuilder buf = new StringBuilder(128);
        buf.append("<a href=\"").append(encodedBaseName).append("/\" title=\"").append(_t("Torrent details")).append("\">");
        out.write(buf.toString());
    }
    String icon;
    if (isMultiFile)
        icon = "folder";
    else if (isValid)
        icon = toIcon(meta.getName());
    else if (snark instanceof FetchAndAdd)
        icon = "basket_put";
    else
        icon = "magnet";
    if (isValid) {
        out.write(toImg(icon));
        out.write("</a>");
    } else {
        out.write(toImg(icon));
    }
    // Torrent name column
    out.write("</td><td class=\"snarkTorrentName\">");
    // }
    if (remaining == 0 || isMultiFile) {
        StringBuilder buf = new StringBuilder(128);
        buf.append("<a href=\"").append(encodedBaseName);
        if (isMultiFile)
            buf.append('/');
        buf.append("\" title=\"");
        if (isMultiFile)
            buf.append(_t("View files"));
        else
            buf.append(_t("Open file"));
        buf.append("\">");
        out.write(buf.toString());
    }
    out.write(DataHelper.escapeHTML(basename));
    if (remaining == 0 || isMultiFile)
        out.write("</a>");
    out.write("<td align=\"right\" class=\"snarkTorrentETA\">");
    if (isRunning && remainingSeconds > 0 && !snark.isChecking())
        // (eta 6h)
        out.write(DataHelper.formatDuration2(Math.max(remainingSeconds, 10) * 1000));
    out.write("</td>\n\t");
    out.write("<td align=\"right\" class=\"snarkTorrentDownloaded\">");
    if (remaining > 0) {
        long percent = 100 * (total - remaining) / total;
        out.write("<div class=\"percentBarOuter\">");
        out.write("<div class=\"percentBarInner\" style=\"width: " + percent + "%;\">");
        out.write("<div class=\"percentBarText\" tabindex=\"0\" title=\"");
        out.write(percent + "% " + _t("complete") + "; " + formatSize(remaining) + ' ' + _t("remaining"));
        out.write("\">");
        out.write(formatSize(total - remaining) + thinsp(noThinsp) + formatSize(total));
        out.write("</div></div></div>");
    } else if (remaining == 0) {
        // needs locale configured for automatic translation
        SimpleDateFormat fmt = new SimpleDateFormat("HH:mm, EEE dd MMM yyyy");
        fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
        long[] dates = _manager.getSavedAddedAndCompleted(snark);
        String date = fmt.format(new Date(dates[1]));
        out.write("<div class=\"percentBarComplete\" title=\"");
        out.write(_t("Completed") + ": " + date + "\">");
        // 3GB
        out.write(formatSize(total));
        out.write("</div>");
    }
    // else
    // out.write("??");  // no meta size yet
    out.write("</td>\n\t");
    out.write("<td align=\"right\" class=\"snarkTorrentUploaded\">");
    if (isValid) {
        if (showRatios) {
            if (total > 0) {
                double ratio = uploaded / ((double) total);
                out.write((new DecimalFormat("0.000")).format(ratio));
                out.write("&nbsp;x");
            }
        } else if (uploaded > 0) {
            out.write(formatSize(uploaded));
        }
    }
    out.write("</td>\n\t");
    out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
    if (isRunning && needed > 0)
        out.write(formatSizeDec(downBps) + "ps");
    out.write("</td>\n\t");
    out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
    if (isRunning && isValid)
        out.write(formatSizeDec(upBps) + "ps");
    out.write("</td>\n\t");
    out.write("<td align=\"center\" class=\"snarkTorrentAction\">");
    if (snark.isChecking()) {
    // show no buttons
    } else if (isRunning) {
        // Stop Button
        if (isDegraded)
            out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&amp;nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
        else
            out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\"");
        out.write(_t("Stop the torrent"));
        out.write("\" src=\"" + _imgPath + "stop.png\" alt=\"");
        out.write(_t("Stop"));
        out.write("\">");
        if (isDegraded)
            out.write("</a>");
    } else if (!snark.isStarting()) {
        if (!_manager.isStopping()) {
            // This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
            if (noThinsp)
                out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&amp;nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
            else
                out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
            out.write(_t("Start the torrent"));
            out.write("\" src=\"" + _imgPath + "start.png\" alt=\"");
            out.write(_t("Start"));
            out.write("\">");
            if (isDegraded)
                out.write("</a>");
        }
        if (isValid && canWrite) {
            // Doesnt work with Opera so use noThinsp instead of isDegraded
            if (noThinsp)
                out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&amp;nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
            else
                out.write("<input type=\"image\" name=\"action_Remove_" + b64 + "\" value=\"foo\" title=\"");
            out.write(_t("Remove the torrent from the active list, deleting the .torrent file"));
            out.write("\" onclick=\"if (!confirm('");
            // Can't figure out how to escape double quotes inside the onclick string.
            // Single quotes in translate strings with parameters must be doubled.
            // Then the remaining single quote must be escaped
            out.write(_t("Are you sure you want to delete the file \\''{0}\\'' (downloaded data will not be deleted) ?", escapeJSString(snark.getName())));
            out.write("')) { return false; }\"");
            out.write(" src=\"" + _imgPath + "remove.png\" alt=\"");
            out.write(_t("Remove"));
            out.write("\">");
            if (isDegraded)
                out.write("</a>");
        }
        // We can delete magnets without write privs
        if (!isValid || canWrite) {
            // Doesnt work with Opera so use noThinsp instead of isDegraded
            if (noThinsp)
                out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&amp;nonce=" + _nonce + getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
            else
                out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\"");
            out.write(_t("Delete the .torrent file and the associated data file(s)"));
            out.write("\" onclick=\"if (!confirm('");
            // Can't figure out how to escape double quotes inside the onclick string.
            // Single quotes in translate strings with parameters must be doubled.
            // Then the remaining single quote must be escaped
            out.write(_t("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", escapeJSString(fullBasename)));
            out.write("')) { return false; }\"");
            out.write(" src=\"" + _imgPath + "delete.png\" alt=\"");
            out.write(_t("Delete"));
            out.write("\">");
            if (isDegraded)
                out.write("</a>");
        }
    }
    out.write("</td>\n</tr>\n");
    if (showPeers && isRunning && curPeers > 0) {
        List<Peer> peers = snark.getPeerList();
        if (!showDebug)
            Collections.sort(peers, new PeerComparator());
        for (Peer peer : peers) {
            if (!peer.isConnected())
                continue;
            out.write("<tr class=\"peerinfo " + rowClass + "\"><td class=\"snarkGraphicStatus\" title=\"");
            out.write(_t("Peer attached to swarm"));
            out.write("\"></td><td colspan=\"4\">");
            PeerID pid = peer.getPeerID();
            String ch = pid != null ? pid.toString().substring(0, 4) : "????";
            String client;
            if ("AwMD".equals(ch))
                client = _t("I2PSnark");
            else if ("LUFa".equals(ch))
                client = "Vuze" + getAzVersion(pid.getID());
            else if ("LUJJ".equals(ch))
                client = "BiglyBT" + getAzVersion(pid.getID());
            else if ("LVhE".equals(ch))
                client = "XD" + getAzVersion(pid.getID());
            else if ("ZV".equals(ch.substring(2, 4)) || "VUZP".equals(ch))
                client = "Robert" + getRobtVersion(pid.getID());
            else if (// LVCS 1.0.2?; LVRS 1.0.4
            ch.startsWith("LV"))
                client = "Transmission" + getAzVersion(pid.getID());
            else if ("LUtU".equals(ch))
                client = "KTorrent" + getAzVersion(pid.getID());
            else if ("CwsL".equals(ch))
                client = "I2PSnarkXL";
            else if ("BFJT".equals(ch))
                client = "I2PRufus";
            else if ("TTMt".equals(ch))
                client = "I2P-BT";
            else
                client = _t("Unknown") + " (" + ch + ')';
            out.write(client + "&nbsp;<tt title=\"");
            out.write(_t("Destination (identity) of peer"));
            out.write("\">" + peer.toString().substring(5, 9) + "</tt>");
            if (showDebug)
                out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s");
            out.write("</td>\n\t");
            out.write("<td class=\"snarkTorrentETA\">");
            out.write("</td>\n\t");
            out.write("<td align=\"right\" class=\"snarkTorrentDownloaded\">");
            float pct;
            if (isValid) {
                pct = (float) (100.0 * peer.completed() / meta.getPieces());
                if (pct >= 100.0)
                    out.write(_t("Seed"));
                else {
                    String ps = String.valueOf(pct);
                    if (ps.length() > 5)
                        ps = ps.substring(0, 5);
                    out.write("<div class=\"percentBarOuter\">");
                    out.write("<div class=\"percentBarInner\" style=\"width:" + ps + "%;\">");
                    out.write("<div class=\"percentBarText\" tabindex=\"0\">" + ps + "%</div>");
                    out.write("</div></div>");
                }
            } else {
                pct = (float) 101.0;
            // until we get the metainfo we don't know how many pieces there are
            // out.write("??");
            }
            out.write("</td>\n\t");
            out.write("<td class=\"snarkTorrentUploaded\">");
            out.write("</td>\n\t");
            out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
            if (needed > 0) {
                if (peer.isInteresting() && !peer.isChoked()) {
                    out.write("<span class=\"unchoked\">");
                    out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
                } else {
                    out.write("<span class=\"choked\" title=\"");
                    if (!peer.isInteresting())
                        out.write(_t("Uninteresting (The peer has no pieces we need)"));
                    else
                        out.write(_t("Choked (The peer is not allowing us to request pieces)"));
                    out.write("\">");
                    out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
                }
            } else if (!isValid) {
                // if (peer supports metadata extension) {
                out.write("<span class=\"unchoked\">");
                out.write(formatSizeDec(peer.getDownloadRate()) + "ps</span>");
            // } else {
            // }
            }
            out.write("</td>\n\t");
            out.write("<td align=\"right\" class=\"snarkTorrentRateUp\">");
            if (isValid && pct < 100.0) {
                if (peer.isInterested() && !peer.isChoking()) {
                    out.write("<span class=\"unchoked\">");
                    out.write(formatSizeDec(peer.getUploadRate()) + "ps</span>");
                } else {
                    out.write("<span class=\"choked\" title=\"");
                    if (!peer.isInterested())
                        out.write(_t("Uninterested (We have no pieces the peer needs)"));
                    else
                        out.write(_t("Choking (We are not allowing the peer to request pieces)"));
                    out.write("\">");
                    out.write(formatSizeDec(peer.getUploadRate()) + "ps</span>");
                }
            }
            out.write("</td>\n\t");
            out.write("<td class=\"snarkTorrentAction\">");
            out.write("</td></tr>\n\t");
            if (showDebug)
                out.write("<tr class=\"debuginfo " + rowClass + "\"><td class=\"snarkGraphicStatus\"></td><td colspan=\"10\">" + peer.getSocket() + "</td></tr>");
        }
    }
}
Also used : DecimalFormat(java.text.DecimalFormat) Peer(org.klomp.snark.Peer) MetaInfo(org.klomp.snark.MetaInfo) Date(java.util.Date) SimpleDateFormat(java.text.SimpleDateFormat) PeerID(org.klomp.snark.PeerID)

Aggregations

DecimalFormat (java.text.DecimalFormat)1 SimpleDateFormat (java.text.SimpleDateFormat)1 Date (java.util.Date)1 MetaInfo (org.klomp.snark.MetaInfo)1 Peer (org.klomp.snark.Peer)1 PeerID (org.klomp.snark.PeerID)1