Search in sources :

Example 1 with Storage

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

the class I2PSnarkServlet method getListHTML.

/**
 * Modded heavily from the Jetty version in Resource.java,
 * pass Resource as 1st param
 * All the xxxResource constructors are package local so we can't extend them.
 *
 * <pre>
 *      // ========================================================================
 *      // $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
 *      // Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
 *      // ------------------------------------------------------------------------
 *      // Licensed under the Apache License, Version 2.0 (the "License");
 *      // you may not use this file except in compliance with the License.
 *      // You may obtain a copy of the License at
 *      // http://www.apache.org/licenses/LICENSE-2.0
 *      // Unless required by applicable law or agreed to in writing, software
 *      // distributed under the License is distributed on an "AS IS" BASIS,
 *      // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *      // See the License for the specific language governing permissions and
 *      // limitations under the License.
 *      // ========================================================================
 * </pre>
 *
 * Get the resource list as a HTML directory listing.
 * @param xxxr The Resource unused
 * @param base The encoded base URL
 * @param parent True if the parent directory should be included
 * @param postParams map of POST parameters or null if not a POST
 * @param sortParam may be null
 * @return String of HTML or null if postParams != null
 * @since 0.7.14
 */
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams, String sortParam) throws IOException {
    String decodedBase = decodePath(base);
    String title = decodedBase;
    String cpath = _contextPath + '/';
    if (title.startsWith(cpath))
        title = title.substring(cpath.length());
    // Get the snark associated with this directory
    String torrentName;
    String pathInTorrent;
    int slash = title.indexOf('/');
    if (slash > 0) {
        torrentName = title.substring(0, slash);
        pathInTorrent = title.substring(slash);
    } else {
        torrentName = title;
        pathInTorrent = "/";
    }
    Snark snark = _manager.getTorrentByBaseName(torrentName);
    if (snark != null && postParams != null) {
        // caller must P-R-G
        String[] val = postParams.get("nonce");
        if (val != null) {
            String nonce = val[0];
            if (String.valueOf(_nonce).equals(nonce)) {
                if (postParams.get("savepri") != null) {
                    savePriorities(snark, postParams);
                } else if (postParams.get("addComment") != null) {
                    saveComments(snark, postParams);
                } else if (postParams.get("deleteComments") != null) {
                    deleteComments(snark, postParams);
                } else if (postParams.get("setCommentsEnabled") != null) {
                    saveCommentsSetting(snark, postParams);
                } else if (postParams.get("stop") != null) {
                    _manager.stopTorrent(snark, false);
                } else if (postParams.get("start") != null) {
                    _manager.startTorrent(snark);
                } else if (postParams.get("recheck") != null) {
                    _manager.recheckTorrent(snark);
                } else {
                    _manager.addMessage("Unknown command");
                }
            } else {
                _manager.addMessage("Please retry form submission (bad nonce)");
            }
        }
        return null;
    }
    File r;
    if (snark != null) {
        Storage storage = snark.getStorage();
        if (storage != null) {
            File sbase = storage.getBase();
            if (pathInTorrent.equals("/"))
                r = sbase;
            else
                r = new File(sbase, pathInTorrent);
        } else {
            // magnet, dummy
            r = new File("");
        }
    } else {
        // dummy
        r = new File("");
    }
    boolean showStopStart = snark != null;
    boolean showPriority = snark != null && snark.getStorage() != null && !snark.getStorage().complete() && r.isDirectory();
    StringBuilder buf = new StringBuilder(4096);
    buf.append(DOCTYPE).append("<html><head><title>");
    if (title.endsWith("/"))
        title = title.substring(0, title.length() - 1);
    final String directory = title;
    final int dirSlash = directory.indexOf('/');
    final boolean isTopLevel = dirSlash <= 0;
    title = _t("Torrent") + ": " + DataHelper.escapeHTML(title);
    buf.append(title);
    buf.append("</title>\n").append(HEADER_A).append(_themePath).append(HEADER_B).append("<noscript><style type=\"text/css\">.script {display: none;}</style></noscript>").append("<link rel=\"shortcut icon\" href=\"" + _themePath + "favicon.ico\">\n");
    if (showPriority)
        buf.append("<script src=\"").append(_contextPath).append(WARBASE + "js/folder.js\" type=\"text/javascript\"></script>\n");
    buf.append("</head><body");
    if (showPriority)
        buf.append(" onload=\"setupbuttons()\"");
    buf.append(">\n<center><div class=\"snarknavbar\"><a href=\"").append(_contextPath).append("/\" title=\"Torrents\"");
    buf.append(" class=\"snarkNav nav_main\">");
    if (_contextName.equals(DEFAULT_NAME))
        buf.append(_t("I2PSnark"));
    else
        buf.append(_contextName);
    buf.append("</a></div>\n");
    if (// always true
    parent)
        buf.append("<div class=\"page\">\n<div class=\"mainsection\">");
    // for stop/start/check
    final boolean er = isTopLevel && snark != null && _manager.util().ratingsEnabled();
    // global setting
    final boolean ec = isTopLevel && snark != null && _manager.util().commentsEnabled();
    // per-torrent setting
    final boolean esc = ec && _manager.getSavedCommentsEnabled(snark);
    final boolean includeForm = showStopStart || showPriority || er || ec;
    if (includeForm) {
        buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
        buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
        if (sortParam != null) {
            buf.append("<input type=\"hidden\" name=\"sort\" value=\"").append(DataHelper.stripHTML(sortParam)).append("\" >\n");
        }
    }
    if (snark != null) {
        // first table - torrent info
        buf.append("<table class=\"snarkTorrentInfo\">\n");
        buf.append("<tr><th></th><th><b>").append(_t("Torrent")).append(":</b> ").append(DataHelper.escapeHTML(snark.getBaseName())).append("</th></tr>\n");
        String fullPath = snark.getName();
        String baseName = encodePath((new File(fullPath)).getName());
        buf.append("<tr><td>");
        toThemeImg(buf, "file");
        buf.append("</td><td><b>").append(_t("Torrent file")).append(":</b> <a href=\"").append(_contextPath).append('/').append(baseName).append("\">").append(DataHelper.escapeHTML(fullPath)).append("</a></td></tr>\n");
        if (snark.getStorage() != null) {
            buf.append("<tr><td>");
            toThemeImg(buf, "file");
            buf.append("</td><td><b>").append(_t("Data location")).append(":</b> ").append(DataHelper.escapeHTML(snark.getStorage().getBase().getPath())).append("</td></tr>\n");
        }
        String hex = I2PSnarkUtil.toHex(snark.getInfoHash());
        buf.append("<tr><td>");
        toThemeImg(buf, "details");
        buf.append("</td><td><b>").append(_t("Info hash")).append(":</b> <span id=\"infohash\">").append(hex.toUpperCase(Locale.US)).append("</span></td></tr>\n");
        String announce = null;
        MetaInfo meta = snark.getMetaInfo();
        if (meta != null) {
            announce = meta.getAnnounce();
            if (announce == null)
                announce = snark.getTrackerURL();
            if (announce != null) {
                announce = DataHelper.stripHTML(announce);
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Primary Tracker")).append(":</b> <span class=\"info_tracker\">");
                buf.append(getShortTrackerLink(announce, snark.getInfoHash()));
                buf.append("</span></td></tr>");
            }
            List<List<String>> alist = meta.getAnnounceList();
            if (alist != null && !alist.isEmpty()) {
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Tracker List")).append(":</b> ");
                for (List<String> alist2 : alist) {
                    buf.append("<span class=\"info_tracker\">");
                    boolean more = false;
                    for (String s : alist2) {
                        if (more)
                            buf.append(' ');
                        else
                            more = true;
                        buf.append(getShortTrackerLink(DataHelper.stripHTML(s), snark.getInfoHash()));
                    }
                    buf.append("</span> ");
                }
                buf.append("</td></tr>\n");
            }
        }
        if (meta != null) {
            String com = meta.getComment();
            if (com != null && com.length() > 0) {
                if (com.length() > 1024)
                    com = com.substring(0, 1024);
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Comment")).append(":</b> ").append(DataHelper.stripHTML(com)).append("</td></tr>\n");
            }
            long dat = meta.getCreationDate();
            // needs locale configured for automatic translation
            SimpleDateFormat fmt = new SimpleDateFormat("HH:mm, EEEE dd MMMM yyyy");
            fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
            if (dat > 0) {
                String date = fmt.format(new Date(dat));
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Created")).append(":</b> ").append(date).append("</td></tr>\n");
            }
            String cby = meta.getCreatedBy();
            if (cby != null && cby.length() > 0) {
                if (cby.length() > 128)
                    cby = com.substring(0, 128);
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Created By")).append(":</b> ").append(DataHelper.stripHTML(cby)).append("</td></tr>\n");
            }
            long[] dates = _manager.getSavedAddedAndCompleted(snark);
            if (dates[0] > 0) {
                String date = fmt.format(new Date(dates[0]));
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Added")).append(":</b> ").append(date).append("</td></tr>\n");
            }
            if (dates[1] > 0) {
                String date = fmt.format(new Date(dates[1]));
                buf.append("<tr><td>");
                toThemeImg(buf, "details");
                buf.append("</td><td><b>").append(_t("Completed")).append(":</b> ").append(date).append("</td></tr>\n");
            }
        }
        if (meta == null || !meta.isPrivate()) {
            buf.append("<tr><td><a href=\"").append(MagnetURI.MAGNET_FULL).append(hex);
            if (announce != null)
                buf.append("&amp;tr=").append(announce);
            buf.append("\">").append(toImg("magnet", _t("Magnet link"))).append("</a></td><td><b>Magnet:</b> <a href=\"").append(MagnetURI.MAGNET_FULL).append(hex);
            if (announce != null)
                buf.append("&amp;tr=").append(announce);
            buf.append("\">").append(MagnetURI.MAGNET_FULL).append(hex);
            if (announce != null)
                buf.append("&amp;tr=").append(announce);
            buf.append("</a>").append("</td></tr>\n");
        } else {
            buf.append("<tr><td>");
            toThemeImg(buf, "details");
            buf.append("</td><td><b>").append(_t("Private torrent")).append("</td></tr>\n");
        }
        // We don't have the hash of the torrent file
        // buf.append("<tr><td>").append(_t("Maggot link")).append(": <a href=\"").append(MAGGOT).append(hex).append(':').append(hex).append("\">")
        // .append(MAGGOT).append(hex).append(':').append(hex).append("</a></td></tr>");
        buf.append("<tr id=\"torrentInfoStats\"><td colspan=\"2\"><span>");
        toThemeImg(buf, "size");
        buf.append("<b>").append(_t("Size")).append(":</b> ").append(formatSize(snark.getTotalLength()));
        int pieces = snark.getPieces();
        double completion = (pieces - snark.getNeeded()) / (double) pieces;
        buf.append("</span>&nbsp;<span>");
        toThemeImg(buf, "head_rx");
        buf.append("<b>");
        if (completion < 1.0)
            buf.append(_t("Completion")).append(":</b> ").append((new DecimalFormat("0.00%")).format(completion));
        else
            buf.append(_t("Complete")).append("</b>");
        // up ratio
        buf.append("</span>&nbsp;<span>");
        toThemeImg(buf, "head_tx");
        buf.append("<b>").append(_t("Upload ratio")).append(":</b> ");
        long uploaded = snark.getUploaded();
        if (uploaded > 0) {
            double ratio = uploaded / ((double) snark.getTotalLength());
            buf.append((new DecimalFormat("0.000")).format(ratio));
            buf.append("&#8239;x");
        } else {
            buf.append('0');
        }
        // not including skipped files, but -1 when not running
        long needed = snark.getNeededLength();
        if (needed < 0) {
            // including skipped files, valid when not running
            needed = snark.getRemainingLength();
        }
        if (needed > 0) {
            buf.append("</span>&nbsp;<span>");
            toThemeImg(buf, "head_rx");
            buf.append("<b>").append(_t("Remaining")).append(":</b> ").append(formatSize(needed));
        }
        long skipped = snark.getSkippedLength();
        if (skipped > 0) {
            buf.append("</span>&nbsp;<span>");
            toThemeImg(buf, "head_rx");
            buf.append("<b>").append(_t("Skipped")).append(":</b> ").append(formatSize(skipped));
        }
        if (meta != null) {
            List<List<String>> files = meta.getFiles();
            int fileCount = files != null ? files.size() : 1;
            buf.append("</span>&nbsp;<span>");
            toThemeImg(buf, "file");
            buf.append("<b>").append(_t("Files")).append(":</b> ").append(fileCount);
        }
        buf.append("</span>&nbsp;<span>");
        toThemeImg(buf, "file");
        buf.append("<b>").append(_t("Pieces")).append(":</b> ").append(pieces);
        buf.append("</span>&nbsp;<span>");
        toThemeImg(buf, "file");
        buf.append("<b>").append(_t("Piece size")).append(":</b> ").append(formatSize(snark.getPieceLength(0))).append("</span></td></tr>\n");
        // buttons
        if (showStopStart) {
            buf.append("<tr id=\"torrentInfoControl\"><td colspan=\"2\">");
            if (snark.isChecking()) {
                buf.append("<span id=\"fileCheck\"><b>").append(_t("Checking")).append("&hellip; ").append((new DecimalFormat("0.00%")).format(snark.getCheckingProgress())).append("&nbsp;<a href=\"").append(base).append("\">").append(_t("Refresh page for results")).append("</a></span>");
            } else if (snark.isStarting()) {
                buf.append("<b>").append(_t("Starting")).append("&hellip;</b>");
            } else if (snark.isAllocating()) {
                buf.append("<b>").append(_t("Allocating")).append("&hellip;</b>");
            } else {
                boolean isRunning = !snark.isStopped();
                buf.append("<input type=\"submit\" value=\"");
                if (isRunning)
                    buf.append(_t("Stop")).append("\" name=\"stop\" class=\"stoptorrent\">\n");
                else
                    buf.append(_t("Start")).append("\" name=\"start\" class=\"starttorrent\">\n");
                buf.append("<input type=\"submit\" name=\"recheck\" value=\"").append(_t("Force Recheck"));
                if (isRunning)
                    buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"").append(_t("Stop the torrent in order to check file integrity")).append("\">\n");
                else
                    buf.append("\" class=\"reload\" title=\"").append(_t("Check integrity of the downloaded file(s)")).append("\">\n");
            }
            buf.append("</td></tr>\n");
        }
    } else {
        // snark == null
        // shouldn't happen
        buf.append("<table class=\"resourceError\" id=\"NotFound\"><tr><th colspan=\"2\">").append(_t("Resource Not found")).append("</th></tr><tr><td><b>").append(_t("Resource")).append(":</b></td><td>").append(r.toString()).append("</td></tr><tr><td><b>").append(_t("Base")).append(":</b></td><td>").append(base).append("</td></tr><tr><td><b>").append(_t("Torrent")).append(":</b></td><td>").append(DataHelper.escapeHTML(torrentName)).append("</td></tr>\n");
    }
    buf.append("</table>\n");
    if (snark != null && !r.exists()) {
        // fixup TODO
        buf.append("<table class=\"resourceError\" id=\"DoesNotExist\"><tr><th colspan=\"2\">").append(_t("Resource Does Not Exist")).append("</th></tr><tr><td><b>").append(_t("Resource")).append(":</b></td><td>").append(r.toString()).append("</td></tr><tr><td><b>").append(_t("Base")).append(":</b></td><td>").append(base).append("</td></tr><tr><td><b>").append(_t("Torrent")).append(":</b></td><td>").append(DataHelper.escapeHTML(torrentName)).append("</td></tr></table></div></div></center>\n</body>\n</html>");
        return buf.toString();
    }
    File[] ls = null;
    if (r.isDirectory()) {
        ls = r.listFiles();
    }
    if (ls == null) {
        // We are only showing the torrent info section
        if (er || ec)
            displayComments(snark, er, ec, esc, buf);
        if (includeForm)
            buf.append("</form>");
        buf.append("</div></div>\n</body>\n</html>");
        return buf.toString();
    }
    Storage storage = snark != null ? snark.getStorage() : null;
    List<Sorters.FileAndIndex> fileList = new ArrayList<Sorters.FileAndIndex>(ls.length);
    // precompute remaining for all files for efficiency
    long[] remainingArray = (storage != null) ? storage.remaining() : null;
    for (int i = 0; i < ls.length; i++) {
        fileList.add(new Sorters.FileAndIndex(ls[i], storage, remainingArray));
    }
    boolean showSort = fileList.size() > 1;
    if (showSort) {
        int sort = 0;
        if (sortParam != null) {
            try {
                sort = Integer.parseInt(sortParam);
            } catch (NumberFormatException nfe) {
            }
        }
        Collections.sort(fileList, Sorters.getFileComparator(sort, this));
    }
    // second table - dir info
    buf.append("<table class=\"snarkDirInfo\"><thead>\n");
    buf.append("<tr>\n").append("<th colspan=2>");
    String tx = _t("Directory");
    // cycle through sort by name or type
    String sort;
    boolean isTypeSort = false;
    if (showSort) {
        if (sortParam == null || "0".equals(sortParam) || "1".equals(sortParam)) {
            sort = "-1";
        } else if ("-1".equals(sortParam)) {
            sort = "12";
            isTypeSort = true;
        } else if ("12".equals(sortParam)) {
            sort = "-12";
            isTypeSort = true;
        } else {
            sort = "";
        }
        buf.append("<a href=\"").append(base).append(getQueryString(sort)).append("\">");
    }
    toThemeImg(buf, "file", tx, showSort ? _t("Sort by {0}", (isTypeSort ? _t("File type") : _t("Name"))) : tx + ": " + directory);
    if (showSort)
        buf.append("</a>");
    if (!isTopLevel) {
        buf.append("&nbsp;");
        buf.append(DataHelper.escapeHTML(directory.substring(dirSlash + 1)));
    }
    buf.append("</th>\n<th align=\"right\">");
    if (showSort) {
        sort = ("5".equals(sortParam)) ? "-5" : "5";
        buf.append("<a href=\"").append(base).append(getQueryString(sort)).append("\">");
    }
    tx = _t("Size");
    toThemeImg(buf, "size", tx, showSort ? _t("Sort by {0}", tx) : tx);
    if (showSort)
        buf.append("</a>");
    buf.append("</th>\n<th class=\"headerstatus\">");
    boolean showRemainingSort = showSort && showPriority;
    if (showRemainingSort) {
        sort = ("10".equals(sortParam)) ? "-10" : "10";
        buf.append("<a href=\"").append(base).append(getQueryString(sort)).append("\">");
    }
    tx = _t("Download Status");
    toThemeImg(buf, "status", tx, showRemainingSort ? _t("Sort by {0}", _t("Remaining")) : tx);
    if (showRemainingSort)
        buf.append("</a>");
    if (showPriority) {
        buf.append("</th>\n<th class=\"headerpriority\">");
        if (showSort) {
            sort = ("13".equals(sortParam)) ? "-13" : "13";
            buf.append("<a href=\"").append(base).append(getQueryString(sort)).append("\">");
        }
        tx = _t("Download Priority");
        toThemeImg(buf, "priority", tx, showSort ? _t("Sort by {0}", tx) : tx);
        if (showSort)
            buf.append("</a>");
    }
    buf.append("</th>\n</tr>\n</thead>\n");
    buf.append("<tr><td colspan=\"" + (showPriority ? '5' : '4') + "\" class=\"ParentDir\"><A HREF=\"");
    URIUtil.encodePath(buf, addPaths(decodedBase, "../"));
    buf.append("\">");
    toThemeImg(buf, "up");
    buf.append(' ').append(_t("Up to higher level directory")).append("</A></td></tr>\n");
    // DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
    // DateFormat.MEDIUM);
    boolean showSaveButton = false;
    boolean rowEven = true;
    for (Sorters.FileAndIndex fai : fileList) {
        // String encoded = encodePath(ls[i].getName());
        // bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
        // http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
        // See resource.diff attachment
        // Resource item = addPath(encoded);
        File item = fai.file;
        String rowClass = (rowEven ? "snarkTorrentEven" : "snarkTorrentOdd");
        rowEven = !rowEven;
        buf.append("<tr class=\"").append(rowClass).append("\">");
        // Get completeness and status string
        boolean complete = false;
        String status = "";
        long length = item.length();
        int fileIndex = fai.index;
        int priority = 0;
        if (fai.isDirectory) {
            complete = true;
        // status = toImg("tick") + ' ' + _t("Directory");
        } else {
            if (snark == null || snark.getStorage() == null) {
                // Assume complete, perhaps he removed a completed torrent but kept a bookmark
                complete = true;
                status = toImg("cancel") + ' ' + _t("Torrent not found?");
            } else {
                long remaining = fai.remaining;
                if (remaining < 0) {
                    complete = true;
                    status = toImg("cancel") + ' ' + _t("File not found in torrent?");
                } else if (remaining == 0 || length <= 0) {
                    complete = true;
                    status = "<div class=\"priorityIndicator\">" + toImg("tick") + "</div>" + _t("Complete");
                } else {
                    priority = fai.priority;
                    if (priority < 0)
                        status = "<div class=\"priorityIndicator\">" + toImg("cancel") + "</div>";
                    else if (priority == 0)
                        status = "<div class=\"priorityIndicator\">" + toImg("clock") + "</div>";
                    else
                        status = "<div class=\"priorityIndicator\">" + toImg("clock_red") + "</div>";
                    long percent = 100 * (length - remaining) / length;
                    status += " <div class=\"percentBarOuter\">" + "<div class=\"percentBarInner\" style=\"width: " + percent + "%;\"><div class=\"percentBarText\" tabindex=\"0\" title=\"" + formatSize(remaining) + ' ' + _t("remaining") + "\">" + percent + "%</div></div></div>";
                }
            }
        }
        String path = addPaths(decodedBase, item.getName());
        if (item.isDirectory() && !path.endsWith("/"))
            path = addPaths(path, "/");
        path = encodePath(path);
        String icon = toIcon(item);
        String mime = getMimeType(path);
        if (mime == null)
            mime = "";
        buf.append("<td class=\"snarkFileIcon\">");
        if (complete) {
            buf.append("<a href=\"").append(path).append("\">");
            // thumbnail ?
            String plc = item.toString().toLowerCase(Locale.US);
            if (mime.startsWith("image/")) {
                buf.append("<img alt=\"\" border=\"0\" class=\"thumb\" src=\"").append(path).append("\"></a>");
            } else {
                buf.append(toImg(icon, _t("Open"))).append("</a>");
            }
        } else {
            buf.append(toImg(icon));
        }
        buf.append("</td><td class=\"snarkFileName\">");
        if (complete) {
            buf.append("<a href=\"").append(path);
            // send browser-viewable files to new tab to avoid potential display in iframe
            if (mime.startsWith("text/") || mime.startsWith("image/") || mime.startsWith("audio/") || mime.startsWith("video/") || mime.equals("application/ogg"))
                buf.append("\" target=\"_blank");
            buf.append("\">");
        }
        buf.append(DataHelper.escapeHTML(item.getName()));
        if (complete)
            buf.append("</a>");
        buf.append("</td><td align=right class=\"snarkFileSize\">");
        if (!item.isDirectory())
            buf.append(formatSize(length));
        buf.append("</td><td class=\"snarkFileStatus\">");
        // buf.append(dfmt.format(new Date(item.lastModified())));
        buf.append(status);
        buf.append("</td>");
        if (showPriority) {
            buf.append("<td class=\"priority\">");
            if ((!complete) && (!item.isDirectory())) {
                buf.append("<label class=\"priorityHigh\" title=\"").append(_t("Download file at high priority")).append("\">").append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"prihigh\" value=\"5\" name=\"pri.").append(fileIndex).append("\" ");
                if (priority > 0)
                    buf.append("checked=\"checked\"");
                buf.append('>').append(_t("High")).append("</label>");
                buf.append("<label class=\"priorityNormal\" title=\"").append(_t("Download file at normal priority")).append("\">").append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"prinorm\" value=\"0\" name=\"pri.").append(fileIndex).append("\" ");
                if (priority == 0)
                    buf.append("checked=\"checked\"");
                buf.append('>').append(_t("Normal")).append("</label>");
                buf.append("<label class=\"prioritySkip\" title=\"").append(_t("Do not download this file")).append("\">").append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"priskip\" value=\"-9\" name=\"pri.").append(fileIndex).append("\" ");
                if (priority < 0)
                    buf.append("checked=\"checked\"");
                buf.append('>').append(_t("Skip")).append("</label>");
                showSaveButton = true;
            }
            buf.append("</td>");
        }
        buf.append("</tr>\n");
    }
    if (showSaveButton) {
        buf.append("<thead><tr id=\"setPriority\"><th class=\"headerpriority\" colspan=\"5\">" + "<span class=\"script\"><a class=\"control\" id=\"setallhigh\" href=\"javascript:void(null);\" onclick=\"setallhigh();\">").append(toImg("clock_red")).append(_t("Set all high")).append("</a>\n" + "<a class=\"control\" id=\"setallnorm\" href=\"javascript:void(null);\" onclick=\"setallnorm();\">").append(toImg("clock")).append(_t("Set all normal")).append("</a>\n" + "<a class=\"control\" id=\"setallskip\" href=\"javascript:void(null);\" onclick=\"setallskip();\">").append(toImg("cancel")).append(_t("Skip all")).append("</a></span>\n" + "<input type=\"submit\" class=\"accept\" value=\"").append(_t("Save priorities")).append("\" name=\"savepri\" >\n" + "</th></tr></thead>\n");
    }
    buf.append("</table>\n");
    if (er || ec)
        displayComments(snark, er, ec, esc, buf);
    // for stop/start/check
    if (includeForm)
        buf.append("</form>");
    buf.append("</div></div>\n</body>\n</html>\n");
    return buf.toString();
}
Also used : DecimalFormat(java.text.DecimalFormat) MetaInfo(org.klomp.snark.MetaInfo) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) Date(java.util.Date) Storage(org.klomp.snark.Storage) Snark(org.klomp.snark.Snark) SecureFile(net.i2p.util.SecureFile) File(java.io.File) SimpleDateFormat(java.text.SimpleDateFormat)

Example 2 with Storage

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

the class I2PSnarkServlet method savePriorities.

/**
 * @since 0.8.1
 */
private void savePriorities(Snark snark, Map<String, String[]> postParams) {
    Storage storage = snark.getStorage();
    if (storage == null)
        return;
    for (Map.Entry<String, String[]> entry : postParams.entrySet()) {
        String key = entry.getKey();
        if (key.startsWith("pri.")) {
            try {
                int fileIndex = Integer.parseInt(key.substring(4));
                // jetty arrays
                String val = entry.getValue()[0];
                int pri = Integer.parseInt(val);
                storage.setPriority(fileIndex, pri);
            // System.err.println("Priority now " + pri + " for " + file);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }
    snark.updatePiecePriorities();
    _manager.saveTorrentStatus(snark);
}
Also used : Storage(org.klomp.snark.Storage) Map(java.util.Map) TreeMap(java.util.TreeMap)

Example 3 with Storage

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

the class I2PSnarkServlet method processRequest.

/**
 * Do what they ask, adding messages to _manager.addMessage as necessary
 */
private void processRequest(HttpServletRequest req) {
    String action = req.getParameter("action");
    if (action == null) {
        // http://www.onenaught.com/posts/382/firefox-4-change-input-type-image-only-submits-x-and-y-not-name
        // TODO-Java6: Remove cast, return type is correct
        @SuppressWarnings("unchecked") Map<String, String[]> params = req.getParameterMap();
        for (Object o : params.keySet()) {
            String key = (String) o;
            if (key.startsWith("action_") && key.endsWith(".x")) {
                action = key.substring(0, key.length() - 2).substring(7);
                break;
            }
        }
        if (action == null) {
            _manager.addMessage("No action specified");
            return;
        }
    }
    // }
    if ("Add".equals(action)) {
        String newURL = req.getParameter("nofilter_newURL");
        /**
         ****
         *            // NOTE - newFile currently disabled in HTML form - see below
         *            File f = null;
         *            if ( (newFile != null) && (newFile.trim().length() > 0) )
         *                f = new File(newFile.trim());
         *            if ( (f != null) && (!f.exists()) ) {
         *                _manager.addMessage(_t("Torrent file {0} does not exist", newFile));
         *            }
         *            if ( (f != null) && (f.exists()) ) {
         *                // NOTE - All this is disabled - load from local file disabled
         *                File local = new File(_manager.getDataDir(), f.getName());
         *                String canonical = null;
         *                try {
         *                    canonical = local.getCanonicalPath();
         *
         *                    if (local.exists()) {
         *                        if (_manager.getTorrent(canonical) != null)
         *                            _manager.addMessage(_t("Torrent already running: {0}", newFile));
         *                        else
         *                            _manager.addMessage(_t("Torrent already in the queue: {0}", newFile));
         *                    } else {
         *                        boolean ok = FileUtil.copy(f.getAbsolutePath(), local.getAbsolutePath(), true);
         *                        if (ok) {
         *                            _manager.addMessage(_t("Copying torrent to {0}", local.getAbsolutePath()));
         *                            _manager.addTorrent(canonical);
         *                        } else {
         *                            _manager.addMessage(_t("Unable to copy the torrent to {0}", local.getAbsolutePath()) + ' ' + _t("from {0}", f.getAbsolutePath()));
         *                        }
         *                    }
         *                } catch (IOException ioe) {
         *                    _log.warn("hrm: " + local, ioe);
         *                }
         *            } else
         ****
         */
        if (newURL != null) {
            newURL = newURL.trim();
            String newDir = req.getParameter("nofilter_newDir");
            File dir = null;
            if (newDir != null) {
                newDir = newDir.trim();
                if (newDir.length() > 0) {
                    dir = new SecureFile(newDir);
                    if (!dir.isAbsolute()) {
                        _manager.addMessage(_t("Data directory must be an absolute path") + ": " + dir);
                        return;
                    }
                    if (!dir.isDirectory() && !dir.mkdirs()) {
                        _manager.addMessage(_t("Data directory cannot be created") + ": " + dir);
                        return;
                    }
                    Collection<Snark> snarks = _manager.getTorrents();
                    for (Snark s : snarks) {
                        Storage storage = s.getStorage();
                        if (storage == null)
                            continue;
                        File sbase = storage.getBase();
                        if (isParentOf(sbase, dir)) {
                            _manager.addMessage(_t("Cannot add torrent {0} inside another torrent: {1}", dir.getAbsolutePath(), sbase));
                            return;
                        }
                    }
                }
            }
            File dd = _manager.getDataDir();
            if (!dd.canWrite()) {
                _manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
                return;
            }
            if (newURL.startsWith("http://")) {
                FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL, dir);
                _manager.addDownloader(fetch);
            } else if (newURL.startsWith(MagnetURI.MAGNET) || newURL.startsWith(MagnetURI.MAGGOT)) {
                addMagnet(newURL, dir);
            } else if (newURL.length() == 40 && newURL.replaceAll("[a-fA-F0-9]", "").length() == 0) {
                // hex
                newURL = newURL.toUpperCase(Locale.US);
                addMagnet(MagnetURI.MAGNET_FULL + newURL, dir);
            } else if (newURL.length() == 32 && newURL.replaceAll("[a-zA-Z2-7]", "").length() == 0) {
                // b32
                newURL = newURL.toUpperCase(Locale.US);
                addMagnet(MagnetURI.MAGNET_FULL + newURL, dir);
            } else {
                _manager.addMessage(_t("Invalid URL: Must start with \"http://\", \"{0}\", or \"{1}\"", MagnetURI.MAGNET, MagnetURI.MAGGOT));
            }
        } else {
        // no file or URL specified
        }
    } else if (action.startsWith("Stop_")) {
        String torrent = action.substring(5);
        if (torrent != null) {
            byte[] infoHash = Base64.decode(torrent);
            if ((infoHash != null) && (infoHash.length == 20)) {
                // valid sha1
                for (String name : _manager.listTorrentFiles()) {
                    Snark snark = _manager.getTorrent(name);
                    if ((snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash()))) {
                        _manager.stopTorrent(snark, false);
                        break;
                    }
                }
            }
        }
    } else if (action.startsWith("Start_")) {
        String torrent = action.substring(6);
        if (torrent != null) {
            byte[] infoHash = Base64.decode(torrent);
            if ((infoHash != null) && (infoHash.length == 20)) {
                // valid sha1
                _manager.startTorrent(infoHash);
            }
        }
    } else if (action.startsWith("Remove_")) {
        String torrent = action.substring(7);
        if (torrent != null) {
            byte[] infoHash = Base64.decode(torrent);
            if ((infoHash != null) && (infoHash.length == 20)) {
                // valid sha1
                for (String name : _manager.listTorrentFiles()) {
                    Snark snark = _manager.getTorrent(name);
                    if ((snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash()))) {
                        MetaInfo meta = snark.getMetaInfo();
                        if (meta == null) {
                            // magnet - remove and delete are the same thing
                            // Remove not shown on UI so we shouldn't get here
                            _manager.deleteMagnet(snark);
                            _manager.addMessage(_t("Magnet deleted: {0}", name));
                            return;
                        }
                        File f = new File(name);
                        File dd = _manager.getDataDir();
                        boolean canDelete = dd.canWrite() || !f.exists();
                        _manager.stopTorrent(snark, canDelete);
                        // TODO race here with the DirMonitor, could get re-added
                        if (f.delete()) {
                            _manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath()));
                        } else if (f.exists()) {
                            if (!canDelete)
                                _manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
                            _manager.addMessage(_t("Torrent file could not be deleted: {0}", f.getAbsolutePath()));
                        }
                        break;
                    }
                }
            }
        }
    } else if (action.startsWith("Delete_")) {
        String torrent = action.substring(7);
        if (torrent != null) {
            byte[] infoHash = Base64.decode(torrent);
            if ((infoHash != null) && (infoHash.length == 20)) {
                // valid sha1
                for (String name : _manager.listTorrentFiles()) {
                    Snark snark = _manager.getTorrent(name);
                    if ((snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash()))) {
                        MetaInfo meta = snark.getMetaInfo();
                        if (meta == null) {
                            // magnet - remove and delete are the same thing
                            _manager.deleteMagnet(snark);
                            if (snark instanceof FetchAndAdd)
                                _manager.addMessage(_t("Download deleted: {0}", name));
                            else
                                _manager.addMessage(_t("Magnet deleted: {0}", name));
                            return;
                        }
                        File f = new File(name);
                        File dd = _manager.getDataDir();
                        boolean canDelete = dd.canWrite() || !f.exists();
                        _manager.stopTorrent(snark, canDelete);
                        // TODO race here with the DirMonitor, could get re-added
                        if (f.delete()) {
                            _manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath()));
                        } else if (f.exists()) {
                            if (!canDelete)
                                _manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
                            _manager.addMessage(_t("Torrent file could not be deleted: {0}", f.getAbsolutePath()));
                            return;
                        }
                        Storage storage = snark.getStorage();
                        if (storage == null)
                            break;
                        List<List<String>> files = meta.getFiles();
                        if (files == null) {
                            // single file torrent
                            for (File df : storage.getFiles()) {
                                // should be only one
                                if (df.delete())
                                    _manager.addMessage(_t("Data file deleted: {0}", df.getAbsolutePath()));
                                else if (df.exists())
                                    _manager.addMessage(_t("Data file could not be deleted: {0}", df.getAbsolutePath()));
                            // else already gone
                            }
                            break;
                        }
                        // step 1 delete files
                        for (File df : storage.getFiles()) {
                            if (df.delete()) {
                            // _manager.addMessage(_t("Data file deleted: {0}", df.getAbsolutePath()));
                            } else if (df.exists()) {
                                _manager.addMessage(_t("Data file could not be deleted: {0}", df.getAbsolutePath()));
                            // else already gone
                            }
                        }
                        // step 2 delete dirs bottom-up
                        Set<File> dirs = storage.getDirectories();
                        if (dirs == null)
                            // directory deleted out from under us
                            break;
                        if (_log.shouldLog(Log.INFO))
                            _log.info("Dirs to delete: " + DataHelper.toString(dirs));
                        boolean ok = false;
                        for (File df : dirs) {
                            if (df.delete()) {
                                ok = true;
                            // _manager.addMessage(_t("Data dir deleted: {0}", df.getAbsolutePath()));
                            } else if (df.exists()) {
                                ok = false;
                                _manager.addMessage(_t("Directory could not be deleted: {0}", df.getAbsolutePath()));
                                if (_log.shouldLog(Log.WARN))
                                    _log.warn("Could not delete dir " + df);
                            // else already gone
                            }
                        }
                        // step 3 message for base (last one)
                        if (ok)
                            _manager.addMessage(_t("Directory deleted: {0}", storage.getBase()));
                        break;
                    }
                }
            }
        }
    } else if ("Save".equals(action)) {
        String dataDir = req.getParameter("nofilter_dataDir");
        boolean filesPublic = req.getParameter("filesPublic") != null;
        boolean autoStart = req.getParameter("autoStart") != null;
        boolean smartSort = req.getParameter("smartSort") != null;
        String seedPct = req.getParameter("seedPct");
        String eepHost = req.getParameter("eepHost");
        String eepPort = req.getParameter("eepPort");
        String i2cpHost = req.getParameter("i2cpHost");
        String i2cpPort = req.getParameter("i2cpPort");
        String i2cpOpts = buildI2CPOpts(req);
        String upLimit = req.getParameter("upLimit");
        String upBW = req.getParameter("upBW");
        String refreshDel = req.getParameter("refreshDelay");
        String startupDel = req.getParameter("startupDelay");
        String pageSize = req.getParameter("pageSize");
        boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
        boolean useDHT = req.getParameter("useDHT") != null;
        // String openTrackers = req.getParameter("openTrackers");
        String theme = req.getParameter("theme");
        String lang = req.getParameter("lang");
        boolean ratings = req.getParameter("ratings") != null;
        boolean comments = req.getParameter("comments") != null;
        // commentsName is filtered in SnarkManager.updateConfig()
        String commentsName = req.getParameter("nofilter_commentsName");
        boolean collapsePanels = req.getParameter("collapsePanels") != null;
        _manager.updateConfig(dataDir, filesPublic, autoStart, smartSort, refreshDel, startupDel, pageSize, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, upLimit, upBW, useOpenTrackers, useDHT, theme, lang, ratings, comments, commentsName, collapsePanels);
        // update servlet
        try {
            setResourceBase(_manager.getDataDir());
        } catch (ServletException se) {
        }
    } else if ("Save2".equals(action)) {
        String taction = req.getParameter("taction");
        if (taction != null)
            processTrackerForm(taction, req);
    } else if ("Create".equals(action)) {
        String baseData = req.getParameter("nofilter_baseFile");
        if (baseData != null && baseData.trim().length() > 0) {
            File baseFile = new File(baseData.trim());
            if (!baseFile.isAbsolute())
                baseFile = new File(_manager.getDataDir(), baseData);
            String announceURL = req.getParameter("announceURL");
            if (baseFile.exists()) {
                File dd = _manager.getDataDir();
                if (!dd.canWrite()) {
                    _manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
                    return;
                }
                String torrentName = baseFile.getName();
                if (torrentName.toLowerCase(Locale.US).endsWith(".torrent")) {
                    _manager.addMessage(_t("Cannot add a torrent ending in \".torrent\": {0}", baseFile.getAbsolutePath()));
                    return;
                }
                Snark snark = _manager.getTorrentByBaseName(torrentName);
                if (snark != null) {
                    _manager.addMessage(_t("Torrent with this name is already running: {0}", torrentName));
                    return;
                }
                if (isParentOf(baseFile, _manager.getDataDir()) || isParentOf(baseFile, _manager.util().getContext().getBaseDir()) || isParentOf(baseFile, _manager.util().getContext().getConfigDir())) {
                    _manager.addMessage(_t("Cannot add a torrent including an I2P directory: {0}", baseFile.getAbsolutePath()));
                    return;
                }
                Collection<Snark> snarks = _manager.getTorrents();
                for (Snark s : snarks) {
                    Storage storage = s.getStorage();
                    if (storage == null)
                        continue;
                    File sbase = storage.getBase();
                    if (isParentOf(sbase, baseFile)) {
                        _manager.addMessage(_t("Cannot add torrent {0} inside another torrent: {1}", baseFile.getAbsolutePath(), sbase));
                        return;
                    }
                    if (isParentOf(baseFile, sbase)) {
                        _manager.addMessage(_t("Cannot add torrent {0} including another torrent: {1}", baseFile.getAbsolutePath(), sbase));
                        return;
                    }
                }
                if (announceURL.equals("none"))
                    announceURL = null;
                _lastAnnounceURL = announceURL;
                List<String> backupURLs = new ArrayList<String>();
                Enumeration<?> e = req.getParameterNames();
                while (e.hasMoreElements()) {
                    Object o = e.nextElement();
                    if (!(o instanceof String))
                        continue;
                    String k = (String) o;
                    if (k.startsWith("backup_")) {
                        String url = k.substring(7);
                        if (!url.equals(announceURL))
                            backupURLs.add(DataHelper.stripHTML(url));
                    }
                }
                List<List<String>> announceList = null;
                if (!backupURLs.isEmpty()) {
                    // BEP 12 - Put primary first, then the others, each as the sole entry in their own list
                    if (announceURL == null) {
                        _manager.addMessage(_t("Error - Cannot include alternate trackers without a primary tracker"));
                        return;
                    }
                    backupURLs.add(0, announceURL);
                    boolean hasPrivate = false;
                    boolean hasPublic = false;
                    for (String url : backupURLs) {
                        if (_manager.getPrivateTrackers().contains(url))
                            hasPrivate = true;
                        else
                            hasPublic = true;
                    }
                    if (hasPrivate && hasPublic) {
                        _manager.addMessage(_t("Error - Cannot mix private and public trackers in a torrent"));
                        return;
                    }
                    announceList = new ArrayList<List<String>>(backupURLs.size());
                    for (String url : backupURLs) {
                        announceList.add(Collections.singletonList(url));
                    }
                }
                try {
                    // This may take a long time to check the storage, but since it already exists,
                    // it shouldn't be THAT bad, so keep it in this thread.
                    // TODO thread it for big torrents, perhaps a la FetchAndAdd
                    boolean isPrivate = _manager.getPrivateTrackers().contains(announceURL);
                    Storage s = new Storage(_manager.util(), baseFile, announceURL, announceList, null, isPrivate, null);
                    // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
                    s.close();
                    MetaInfo info = s.getMetaInfo();
                    File torrentFile = new File(_manager.getDataDir(), s.getBaseName() + ".torrent");
                    // FIXME is the storage going to stay around thanks to the info reference?
                    // now add it, but don't automatically start it
                    boolean ok = _manager.addTorrent(info, s.getBitField(), torrentFile.getAbsolutePath(), baseFile, true);
                    if (!ok)
                        return;
                    _manager.addMessage(_t("Torrent created for \"{0}\"", baseFile.getName()) + ": " + torrentFile.getAbsolutePath());
                    if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL))
                        _manager.addMessage(_t("Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\"", baseFile.getName()));
                } catch (IOException ioe) {
                    _manager.addMessage(_t("Error creating a torrent for \"{0}\"", baseFile.getAbsolutePath()) + ": " + ioe);
                    _log.error("Error creating a torrent", ioe);
                }
            } else {
                _manager.addMessage(_t("Cannot create a torrent for the nonexistent data: {0}", baseFile.getAbsolutePath()));
            }
        } else {
            _manager.addMessage(_t("Error creating torrent - you must enter a file or directory"));
        }
    } else if ("StopAll".equals(action)) {
        _manager.stopAllTorrents(false);
    } else if ("StartAll".equals(action)) {
        _manager.startAllTorrents();
    } else if ("Clear".equals(action)) {
        String sid = req.getParameter("id");
        if (sid != null) {
            try {
                int id = Integer.parseInt(sid);
                _manager.clearMessages(id);
            } catch (NumberFormatException nfe) {
            }
        }
    } else {
        _manager.addMessage("Unknown POST action: \"" + action + '\"');
    }
}
Also used : Enumeration(java.util.Enumeration) SecureFile(net.i2p.util.SecureFile) MetaInfo(org.klomp.snark.MetaInfo) ArrayList(java.util.ArrayList) IOException(java.io.IOException) ServletException(javax.servlet.ServletException) Storage(org.klomp.snark.Storage) Collection(java.util.Collection) Snark(org.klomp.snark.Snark) List(java.util.List) ArrayList(java.util.ArrayList) SecureFile(net.i2p.util.SecureFile) File(java.io.File)

Example 4 with Storage

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

the class I2PSnarkServlet method getResource.

/**
 *  We override this to set the file relative to the storage dirctory
 *  for the torrent.
 *
 *  @param pathInContext should always start with /
 */
@Override
public File getResource(String pathInContext) {
    if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") || !pathInContext.startsWith("/") || pathInContext.length() == 0 || pathInContext.equals("/index.html") || pathInContext.startsWith(WARBASE))
        return super.getResource(pathInContext);
    // files in the i2psnark/ directory
    // get top level
    pathInContext = pathInContext.substring(1);
    File top = new File(pathInContext);
    File parent;
    while ((parent = top.getParentFile()) != null) {
        top = parent;
    }
    Snark snark = _manager.getTorrentByBaseName(top.getPath());
    if (snark != null) {
        Storage storage = snark.getStorage();
        if (storage != null) {
            File sbase = storage.getBase();
            String child = pathInContext.substring(top.getPath().length());
            return new File(sbase, child);
        }
    }
    return new File(_resourceBase, pathInContext);
}
Also used : Storage(org.klomp.snark.Storage) Snark(org.klomp.snark.Snark) SecureFile(net.i2p.util.SecureFile) File(java.io.File)

Aggregations

Storage (org.klomp.snark.Storage)4 File (java.io.File)3 SecureFile (net.i2p.util.SecureFile)3 Snark (org.klomp.snark.Snark)3 ArrayList (java.util.ArrayList)2 List (java.util.List)2 MetaInfo (org.klomp.snark.MetaInfo)2 IOException (java.io.IOException)1 DecimalFormat (java.text.DecimalFormat)1 SimpleDateFormat (java.text.SimpleDateFormat)1 Collection (java.util.Collection)1 Date (java.util.Date)1 Enumeration (java.util.Enumeration)1 Map (java.util.Map)1 TreeMap (java.util.TreeMap)1 ServletException (javax.servlet.ServletException)1