All Implemented Interfaces:
Serializable, Servlet, ServletConfig

public class I2PSnarkServletextends BasicServlet
Web interface servlet for I2PSnark torrent management: torrent listing and status, adding torrents by file/URL/magnet, torrent creation, peer and bandwidth management, DHT and tracker status, comments, file browsing and download, configuration, and theme/locale support. Works with standard servlet containers; CSRF protection is via nonces plus an Origin check on POST, and all pages carry a Content Security Policy.
See Also:
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      Fixed serial version for the servlet.
      See Also:
    • _contextPath

      private String _contextPath
      Context path, generally "/i2psnark"; set once in GenericServlet.init().
    • _contextName

      private String _contextName
      Context display name, generally "i2psnark"; set once in init().
    • _manager

      private transient SnarkManager _manager
      The manager holding this webapp's torrents.
    • _currentNonce

      private long _currentNonce
      Rotating CSRF nonce, rotates every 5 minutes.
    • _recentNonces

      private final long[] _recentNonces
      The two nonces before the current one, still accepted for in-flight forms.
    • _lastRotation

      private long _lastRotation
      When the current nonce was minted, ms since epoch; drives rotation.
    • NONCE_ROTATION_MS

      private static final long NONCE_ROTATION_MS
      Form nonces rotate every 5 minutes.
      See Also:
    • _bridgeVersion

      private static volatile String _bridgeVersion
      Version of the bundled I2PSnark Bridge XPI, read lazily from the war; null if unknown.
    • _bridgeUpdateLogged

      private static volatile boolean _bridgeUpdateLogged
      Set once when an outdated I2PSnark Bridge extension reported itself; one screen-log notice per webapp start.
    • INFOHASH_PAREN

      private static final Pattern INFOHASH_PAREN
      Splits a pasted info hash from a trailing parenthetical.
    • STAT_DOWNLOADED

      private static final int STAT_DOWNLOADED
      Cumulative stats array indices: [downloaded, uploaded, download rate, upload rate, peers, total size].
      See Also:
    • STAT_UPLOADED

      private static final int STAT_UPLOADED
      Sort key for the uploaded column.
      See Also:
    • STAT_DOWNLOAD_RATE

      private static final int STAT_DOWNLOAD_RATE
      Sort key for the download rate column.
      See Also:
    • STAT_UPLOAD_RATE

      private static final int STAT_UPLOAD_RATE
      Sort key for the upload rate column.
      See Also:
    • STAT_PEERS

      private static final int STAT_PEERS
      Sort key for the peers column.
      See Also:
    • STAT_TOTAL_SIZE

      private static final int STAT_TOTAL_SIZE
      Sort key for the total size column.
      See Also:
    • HEX_PATTERN

      private static final Pattern HEX_PATTERN
      Matches a hex info hash.
    • BASE32_PATTERN

      private static final Pattern BASE32_PATTERN
      Matches a base32 name without the suffix.
    • _themePath

      private String _themePath
      Web path of the active theme's resources; recomputed per request.
    • _resourcePath

      private String _resourcePath
      Web path of static resources; set once in init(), never mutated per request.
    • _imgPath

      private String _imgPath
      Icon image base path derived from _themePath.
    • _lastAnnounceURL

      private String _lastAnnounceURL
      Announce URL of the torrent being edited; preselects its tracker radio button.
    • DEFAULT_NAME

      private static final String DEFAULT_NAME
      Webapp name used in paths and config keys.
      See Also:
    • PROP_CONFIG_FILE

      public static final String PROP_CONFIG_FILE
      Config file path property.
      See Also:
    • WARBASE

      public static final String WARBASE
      Webapp resource base path.
      See Also:
    • HELLIP

      static final char HELLIP
      Ellipsis character used to truncate long names.
      See Also:
    • PROP_ADVANCED

      private static final String PROP_ADVANCED
      Config key for the console's advanced mode.
      See Also:
    • RC_PROP_ENABLE_SORA_FONT

      private static final String RC_PROP_ENABLE_SORA_FONT
      Config key for the console's Sora font option.
      See Also:
    • _DATE_FMT1

      private static final ThreadLocal<DateFormat> _DATE_FMT1
      Per-thread date format for the torrent list.
    • _DATE_FMT2

      private static final ThreadLocal<DateFormat> _DATE_FMT2
      Per-thread date format including the time.
    • _DATE_FMT3

      private static final ThreadLocal<DateFormat> _DATE_FMT3
      Per-thread date format for file timestamps.
    • searchResults

      private int searchResults
      Search results.
    • cspNonce

      String cspNonce
      Csp nonce.
    • configForms

      private final I2PSnarkConfigure configForms
      Settings form writer, which reads its settings back through this servlet's accessors.
    • MAX_DISPLAYED_FILENAME_LENGTH

      static final int MAX_DISPLAYED_FILENAME_LENGTH
      Longest file name shown before truncation.
      See Also:
    • DOCTYPE

      private static final String DOCTYPE
      Document type line opening every page.
      See Also:
    • TABLE_HEADER

      private static final String TABLE_HEADER
      Opening of the torrent list table.
      See Also:
    • IFRAME_FORM

      private static final String IFRAME_FORM
      Hidden iframe that receives form posts.
      See Also:
    • STREAM_MIN_TORRENT_ROWS

      private static final int STREAM_MIN_TORRENT_ROWS
      Minimum visible rows before a torrent-list page renders in streamed mode. Torrent rows are heavy (badges, progress bars, ~1-2 KB each), so this gate sits lower than the file-table gate.
      See Also:
    • STREAM_MIN_FILE_ROWS

      private static final int STREAM_MIN_FILE_ROWS
      Minimum visible rows before a directory file table renders in streamed mode. File rows are lighter than torrent rows, so buffering stays worthwhile up to a higher count; below the gate a single buffered write keeps Content-Length on the directory page.
      See Also:
    • STREAM_DRAIN_EVERY

      private static final int STREAM_DRAIN_EVERY
      Rows rendered per chunk in streamed mode; bounds the staging buffer near tens of KB.
      See Also:
    • DIRECTORY_POST_ACTIONS

      private static final String[] DIRECTORY_POST_ACTIONS
      Command keys accepted by executeDirectoryPostAction(Snark, Map, String), in precedence order.
    • POSTMAN_B64

      private static final String POSTMAN_B64
      Known postman tracker base64 announce, both old and new.
      See Also:
    • POSTMAN_B64_NEW

      private static final String POSTMAN_B64_NEW
      Base64 destination of the Postman tracker's new address.
      See Also:
    • BT_B32

      private static final String BT_B32
      Known tracker base32 announces.
      See Also:
    • CHUDO_B32

      private static final String CHUDO_B32
      Base32 address of the Chudo tracker.
      See Also:
    • CRYPT_B32

      private static final String CRYPT_B32
      Base32 address of the Cryptopolis tracker.
      See Also:
    • FREEDOM_B32

      private static final String FREEDOM_B32
      Base32 address of the Freedom tracker.
      See Also:
    • ICU812_B32

      private static final String ICU812_B32
      Base32 address of the ICU812 tracker.
      See Also:
    • LODIKON_B32

      private static final String LODIKON_B32
      Base32 address of the Lodikon tracker.
      See Also:
    • LYOKO_B32

      private static final String LYOKO_B32
      Base32 address of the Lyoko tracker.
      See Also:
    • ODIFT_B32

      private static final String ODIFT_B32
      Base32 address of the Odift tracker.
      See Also:
    • OMITRACK_B32

      private static final String OMITRACK_B32
      Base32 address of the Omirack tracker.
      See Also:
    • OTDG_B32

      private static final String OTDG_B32
      Base32 address of the OnionTracker tracker.
      See Also:
    • POSTMAN_B32_NEW

      private static final String POSTMAN_B32_NEW
      Base32 address of the Postman tracker's new address.
      See Also:
    • R4SAS_B32

      private static final String R4SAS_B32
      Base32 address of the R4SAS tracker.
      See Also:
    • SKANK_B32

      private static final String SKANK_B32
      Base32 address of the Skank tracker.
      See Also:
    • SIMP_B32

      private static final String SIMP_B32
      Base32 address of the SIMP tracker.
      See Also:
    • THEBLAND_B32

      private static final String THEBLAND_B32
      Base32 address of The Bland tracker.
      See Also:
    • SIGMA_B32

      private static final String SIGMA_B32
      Base32 address of the Sigma tracker.
      See Also:
    • ACTIX_B32

      private static final String ACTIX_B32
      Base32 address of the Actix tracker.
      See Also:
    • FATTYDOVE_B32

      private static final String FATTYDOVE_B32
      Base32 address of the FattyDove tracker.
      See Also:
    • LOCALCACHE_B32

      private static final String LOCALCACHE_B32
      Base32 address of the LocalCache tracker.
      See Also:
    • OBSERVATIONS_B32

      private static final String OBSERVATIONS_B32
      Base32 address of the Observations tracker.
      See Also:
    • PUBLIC_B32

      private static final String PUBLIC_B32
      Base32 address of the Public tracker.
      See Also:
    • YETANOTHER_B32

      private static final String YETANOTHER_B32
      Base32 address of the YetAnother tracker.
      See Also:
    • KNOWN_TRACKERS

      static final String[][] KNOWN_TRACKERS
      Known tracker announce keys (base64 postman blobs and base32 hostnames) mapped to their display names. Package-visible for testing. Matched literally - entries are plain strings, not regexes.
  • Constructor Details

    • I2PSnarkServlet

      public I2PSnarkServlet()
      Constructor called by the servlet container; all setup is deferred to init().
  • Method Details

    • getNonce

      private long getNonce()
      Current CSRF nonce, rotating every 5 minutes.
      Returns:
      the current CSRF nonce, rotated every NONCE_ROTATION_MS
    • isValidNonce

      private boolean isValidNonce(String nonce)
      Validate nonce against current and recent nonces (backward compatibility).
      Parameters:
      nonce - the nonce to validate
      Returns:
      true if valid
    • allowOrigin

      private boolean allowOrigin(HttpServletRequest request)
      Validate Origin header for POST requests. Allows requests with matching Origin (same-origin), or no Origin header. Rejects cross-origin POST requests to prevent CSRF attacks.
      Parameters:
      request - the HTTP request
      Returns:
      true if allowed
    • manager

      SnarkManager manager()
      The torrent manager behind this servlet.
      Returns:
      the SnarkManager holding the torrents this servlet renders
    • contextPath

      String contextPath()
      The context path this servlet is mounted under.
      Returns:
      the context path, always starting with '/'
    • contextName

      String contextName()
      The name identifying this instance in config and titles.
      Returns:
      the context name, defaulting to "i2psnark"
    • resourcePath

      String resourcePath()
      Prefix for links to resources bundled in the war.
      Returns:
      the context path plus WARBASE
    • warBase

      String warBase()
      War-relative prefix under which static resources are served.
      Returns:
      the resource prefix, "/.res/"
    • context

      I2PAppContext context()
      The I2P context this servlet runs in.
      Returns:
      the app context passed to init()
    • configForms

      I2PSnarkConfigure configForms()
      The settings form writer.
      Returns:
      the writer backing the /configure pages
    • writeConfigForm

      private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException
      Writes the download and torrent settings form.
      Parameters:
      out - where to write the form
      req - the request being served
      Throws:
      IOException - if the write fails
    • writeTorrentCreateFilterForm

      private void writeTorrentCreateFilterForm(PrintWriter out, HttpServletRequest req) throws IOException
      Writes the torrent creation filter form.
      Parameters:
      out - where to write the form
      req - the request being served
      Throws:
      IOException - if the write fails
    • writeTrackerForm

      private void writeTrackerForm(PrintWriter out, HttpServletRequest req) throws IOException
      Writes the tracker announce settings form.
      Parameters:
      out - where to write the form
      req - the request being served
      Throws:
      IOException - if the write fails
    • init

      public void init(ServletConfig cfg) throws ServletException
      Initialize the servlet.
      Specified by:
      init in interface Servlet
      Overrides:
      init in class BasicServlet
      Parameters:
      cfg - the servlet config
      Throws:
      ServletException - if initialization fails
    • destroy

      public void destroy()
      Destroy the servlet.
      Specified by:
      destroy in interface Servlet
      Overrides:
      destroy in class GenericServlet
    • getResource

      public File getResource(String pathInContext)
      We override this to set the file relative to the storage directory for the torrent. Deliberately unsynchronized: this runs for every snark request, and the only shared field it reads (_resourceBase) is volatile.
      Overrides:
      getResource in class BasicServlet
      Parameters:
      pathInContext - should always start with /
      Returns:
      the resolved file, or null if it does not exist
    • resolveTorrentPath

      private static File resolveTorrentPath(Storage storage, String pathInTorrent)
      The on-disk location of a path within a torrent. While an incomplete download is staged in the temp dir, the data-directory tree may not exist yet, so the path falls back to the staging tree, which mirrors the data-directory layout.
      Parameters:
      storage - the torrent's storage
      pathInTorrent - the path within the torrent, "/" or empty for the root
      Returns:
      the physical file
    • doGet

      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
      Handle what we can here, calling super.doGet(HttpServletRequest, HttpServletResponse) for the rest.
      Overrides:
      doGet in class BasicServlet
      Throws:
      ServletException
      IOException
    • doPost

      public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
      Handle what we can here, calling super.doPost(HttpServletRequest, HttpServletResponse) for the rest.
      Overrides:
      doPost in class BasicServlet
      Parameters:
      request - the HTTP request
      response - the HTTP response
      Throws:
      ServletException - if a servlet error occurs
      IOException - if an I/O error occurs
    • isAdvanced

      public boolean isAdvanced()
      Returns whether advanced mode is enabled in the router console.
      Returns:
      true if advanced mode is enabled
    • useSoraFont

      public boolean useSoraFont()
      Returns whether the Sora display font should be used.
      Returns:
      true if the Sora font is enabled or running in standalone mode
    • doGetAndPost

      private void doGetAndPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
      Handle what we can here, calling super.doGet() or super.doPost() for the rest. Routing, in order: gatekeeping (Origin check on POST, CSP, static WARBASE); handleAjaxRequest(String, boolean, String, HttpServletRequest, HttpServletResponse) for the XHR fragment endpoints; /_add; /magnet; handleUnmanagedPath(HttpServletRequest, HttpServletResponse, String, String) for listings, playlists and passthrough; handleFormSubmission(HttpServletRequest, HttpServletResponse, String, String) for nonce'd POST actions; then page assembly - head, navbar, search scaffolding, either writeConfigurePanels(PrintWriter, HttpServletRequest) or writeTorrentsSection(PrintWriter, HttpServletRequest, List, List), and writePageTail(PrintWriter, boolean).
      Parameters:
      req - the request whose method, servlet path and parameters select the handler
      resp - the response the selected handler writes to
      Throws:
      ServletException - if a passthrough to the container's doGet/doPost or a sendError fails
      IOException - if writing or flushing the response fails
    • isAjaxPath

      static boolean isAjaxPath(String path)
      The two XHR fragment endpoints polled by the console JS.
      Parameters:
      path - servlet path after the context, may be null
      Returns:
      true if the path is one of the polled fragments
    • isIndexPath

      static boolean isIndexPath(String path)
      Whether path is the torrent list landing page.
      Parameters:
      path - servlet path after the context, may be null
      Returns:
      true for "", "/", or "index.jsp"
    • isUnmanagedPath

      static boolean isUnmanagedPath(String path, boolean isIndex, boolean isConfigure)
      Paths that bypass normal page rendering and are answered by handleUnmanagedPath: everything outside the index page(s), the POST target, and the configuration UI.
      Parameters:
      path - servlet path after the context, may be null
      isIndex - whether path is the torrent list landing page
      isConfigure - whether the request selected the configuration UI
      Returns:
      true if handleUnmanagedPath should answer the request
    • handleAjaxRequest

      private boolean handleAjaxRequest(String path, boolean isConfigure, String peerString, HttpServletRequest req, HttpServletResponse resp) throws IOException
      Serves the XHR fragment endpoints; unknown paths are ignored so the caller's fall-through routing stays in charge.
      Parameters:
      path - servlet path after the context, may be null
      isConfigure - whether the request selected the configuration UI
      peerString - the peer parameter to carry into message rendering, may be null
      req - the HTTP request
      resp - the HTTP response
      Returns:
      true if the request was handled here
      Throws:
      IOException - if writing the response fails
    • handleUnmanagedPath

      private void handleUnmanagedPath(HttpServletRequest req, HttpServletResponse resp, String method, String path) throws ServletException, IOException
      Answers everything outside the managed pages: torrent directory listings (with optional playlist export) under trailing-slash paths, and container delegation for the rest.
      Parameters:
      req - the request whose path info and playlist/sort parameters drive the listing
      resp - the response written to
      method - the HTTP method; GET/HEAD/POST delegate to the container, anything else gets 405
      path - the servlet path; a trailing slash selects the directory listing branch
      Throws:
      ServletException - if a passthrough to the container's doGet/doPost or a sendError fails
      IOException - if writing the listing or an error page fails
    • handleFormSubmission

      private boolean handleFormSubmission(HttpServletRequest req, HttpServletResponse resp, String method, String peerString) throws IOException
      Dispatches a nonce-bearing form submission to its action and finishes with the P-R-G redirect (or G-R-G to hide params from the address bar).
      Parameters:
      req - the submitted form request; its nonce parameter decides whether this method takes over
      resp - the response redirected once the action has run
      method - the HTTP method; only POST, or GET with action=Clear, is accepted
      peerString - the peer query string carried into the redirect
      Returns:
      true if a form was submitted and the response is complete; false when there is no nonce and normal rendering should proceed
      Throws:
      IOException - if the redirect response cannot be sent
    • appendNavbar

      private void appendNavbar(StringBuilder buf, boolean isConfigure, String peerString, List<Tracker> sortedTrackers)
      Renders the navbar: configure variant links back to the torrent list; list variant adds tracker links from the sorted tracker list.
      Parameters:
      buf - destination for the navbar markup
      isConfigure - true on the configuration page, which renders only the link back to the torrent list
      peerString - the peer query string appended to the main link's href
      sortedTrackers - trackers whose Postman base URLs become extra nav links; non-http and known-open-tracker entries are skipped
    • appendSearchForm

      private void appendSearchForm(StringBuilder buf, HttpServletRequest req)
      Renders the search form when multiple torrents exist; hidden until realtimeSearch.js activates it, functional without JS via GET submit.
      Parameters:
      buf - destination for the form markup; nothing is appended when one or fewer torrents exist
      req - the request whose search parameter pre-fills the input
    • writeConfigurePanels

      private void writeConfigurePanels(PrintWriter out, HttpServletRequest req) throws IOException
      Configuration page panels below the messages area.
      Parameters:
      out - writer for the configuration page body
      req - the request passed through to the config, filter and tracker forms
      Throws:
      IOException - if writing the forms fails
    • writeTorrentsSection

      private void writeTorrentsSection(PrintWriter out, HttpServletRequest req, List<Tracker> sortedTrackers, List<TorrentCreateFilter> sortedFilters) throws IOException
      Torrent table plus the add/create forms of the lower section.
      Parameters:
      out - writer for the torrents page body
      req - the request passed through to the torrent table and the add/create forms
      sortedTrackers - trackers offered in the create form's announce list
      sortedFilters - torrent-create filters offered in the create form
      Throws:
      IOException - if writing the table or forms fails
    • writePageTail

      private void writePageTail(PrintWriter out, boolean isConfigure) throws IOException
      Page scripts and footer, shared by index and configuration pages.
      Parameters:
      out - writer positioned just before the footer
      isConfigure - true on the configuration page, which omits the toggleLinks and toggleAddCreate scripts
      Throws:
      IOException - if writing the scripts or footer fails
    • renderHead

      private String renderHead(HttpServletRequest req, boolean isConfigure, boolean isIndex, boolean noCollapse, boolean collapsePanels, boolean showStatusFilter, String peerString, String lang, int delay, int pageSize)
      Renders the full <head> section of the main HTML page.
      Parameters:
      req - the request being rendered; currently unused
      isConfigure - true for the configuration page, which skips the torrent-list scripts and preloads
      isIndex - true for the main index page, which adds the click and alert scripts
      noCollapse - true forces nocollapse.css regardless of the collapsePanels setting
      collapsePanels - whether panels are collapsible per the current settings
      showStatusFilter - true adds the filter-bar script preloads
      peerString - the peer query string embedded in the noscript refresh redirect
      lang - the UI language code; zh, ja and ko get a larger-font stylesheet
      delay - auto-refresh interval in seconds; > 0 adds a noscript meta-refresh, floored at 60
      pageSize - torrents per page, exported to JS as snarkPageSize
      Returns:
      the doctype, the opening <html> tag and the complete <head> section
    • setHTMLHeaders

      private void setHTMLHeaders(HttpServletResponse resp, String cspNonce, boolean allowMedia)
      Handles the standard HTTP headers for all HTML pages.
      Parameters:
      resp - the HttpServletResponse object to which headers will be added
      cspNonce - the nonce value for Content Security Policy (CSP)
      allowMedia - whether to allow media sources in CSP
    • setXHRHeaders

      private void setXHRHeaders(HttpServletResponse resp, String cspNonce, boolean allowMedia)
      Cross-Origin Resource Sharing (CORS) and Content Security Policy (CSP) headers for XMLHttpRequest (XHR) responses.
      Parameters:
      resp - the HttpServletResponse object to which headers will be added
      cspNonce - the nonce value for the Content Security Policy (CSP)
      allowMedia - indicates whether media sources should be allowed in CSP
    • screenLogStamp

      private String screenLogStamp()
      Cheap change-detection stamp for the screen log, emitted with every xhr1 refresh payload so the client can skip the xhrscreenlog.html round trip while the log is unchanged. Combines the last message id (which advances on every add) with the list size (which moves on clears and cap-driven trims), so both growth and shrinkage are detected without rendering anything.
      Returns:
      "lastMessageId:messageCount", or "0:0" when the log is empty
    • emitGraphData

      private void emitGraphData(HttpServletRequest req, PrintWriter out)
      Emits the bandwidth graph element for xhr1 payloads. The client passes its last seen sample version as the "gv" parameter; the full sample CSV travels only when the client is behind, so steady-state ticks cost a few bytes.
      Parameters:
      req - the refresh request, may carry a "gv" parameter
      out - the PrintWriter to write the element to
    • writeMessages

      private void writeMessages(PrintWriter out, boolean isConfigure, String peerString) throws IOException
      Writes the logging messages to the HTML screenlog.
      Parameters:
      out - the PrintWriter to which the HTML output will be written
      isConfigure - a boolean indicating whether the current page is the configuration page
      peerString - a string containing the peer parameters for the URL
      Throws:
      IOException - if an I/O error occurs while writing to the output stream
    • writeTorrents

      private boolean writeTorrents(PrintWriter out, HttpServletRequest req, boolean canWrite) throws IOException
      Writes the HTML representation of the torrents to the output stream. This method generates the HTML structure for displaying torrents, including headers, sorting options, pagination controls, and the torrent list itself. It also updates the statistics array with aggregated torrent data. Output is gated by row count: pages with fewer than STREAM_MIN_TORRENT_ROWS visible rows stage the whole table into one buffer written in a single call, larger pages stream rows to the writer as today. Markup order is identical in both modes. Note the page scaffold upstream of this method is flushed before the gate is evaluated, so buffered mode does not restore Content-Length here.
      Parameters:
      out - the PrintWriter to which the HTML output will be written
      req - the HttpServletRequest containing the request parameters
      canWrite - a boolean indicating whether the data directory is writable
      Returns:
      true if the current page is the first page of the torrent list
      Throws:
      IOException - if an I/O error occurs while writing to the output stream
    • appendSnarkHeader

      private String appendSnarkHeader(HttpServletRequest req, List<Snark> snarks, int start, int pageSize, String filterParam, String peerParam, String currentSort, String contextPath)
      Builds the HTML table header row for the torrent list display, including sortable column headers, peer toggling links, and activity indicators.

      Dispatcher only: prepares request-derived inputs into a SortHeaderContext plus an activity scan, then delegates each column to its own builder so per-column markup stays independently reviewable.

      Parameters:
      req - the HttpServletRequest containing the current request parameters
      snarks - the list of torrent objects currently shown or filtered
      start - the starting index of the current page slice within the torrent list
      pageSize - the maximum number of torrents displayed per page
      filterParam - the current filter parameter controlling torrent visibility
      peerParam - the current peer parameter controlling peer display toggling
      currentSort - the current sorting parameter for the torrent list
      contextPath - the context path prefix for constructing URLs
      Returns:
      a String containing the fully constructed HTML for the torrent table header row
    • scanTorrentActivity

      private I2PSnarkServlet.TorrentActivityScan scanTorrentActivity(List<Snark> snarks, int start, int pageSize, int total)
      Scans the page slice for peer/rate activity, exiting early once every flag is set. Extracted from appendSnarkHeader; write-only counters that were computed here previously are gone.
      Parameters:
      snarks - full sorted list
      start - first row of the rendered slice
      pageSize - rows per page
      total - list size bounding the slice
      Returns:
      flags for the slice, never null
    • nextStatusSort

      static String nextStatusSort(String currentSort, boolean poolSort)
      Next value of the Status column sort cycle: toggles status asc/desc, and in multi-destination mode continues into status+pool asc/desc before returning to status desc. Unknown keys restart at desc.
      Parameters:
      currentSort - current sort key, may be null
      poolSort - true when multi-destination mode is active
      Returns:
      the next sort key, never null
    • nextTorrentNameTypeSort

      static String nextTorrentNameTypeSort(String currentSort)
      Name/type ladder for the torrent column. Unlike the directory-page cycle (nextNameTypeSort(String)) the fallback after foreign keys restarts at name ascending ("1"), not unsorted - both behaviors are pinned by tests.
      Parameters:
      currentSort - current sort key, may be null
      Returns:
      the next sort key, never null
    • nextRXSort

      static String nextRXSort(String currentSort)
      Next value of the RX column cycle: download total and rate alternate asc/desc across four states; unknown keys restart at "-5".
      Parameters:
      currentSort - current sort key, may be null
      Returns:
      the next sort key, never null
    • nextTXSort

      static String nextTXSort(String currentSort)
      Next value of the TX column cycle: upload total and share ratio alternate asc/desc across four states; unknown keys restart at "-7".
      Parameters:
      currentSort - current sort key, may be null
      Returns:
      the next sort key, never null
    • appendStatusHeader

      private void appendStatusHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, boolean poolSort, String txtStatus)
      Status column: sortable always; pool-aware via nextStatusSort(String, boolean).
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      poolSort - true when multi-destination mode is active, which extends the sort cycle with the pool keys
      txtStatus - localized label for the status column
    • appendPeerToggleHeader

      private void appendPeerToggleHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String peerParam, boolean isConnected, boolean noSnarks, I2PSnarkServlet.TorrentActivityScan scan)
      Peer-count column: renders the show/hide peers toggle when connected with peers present; empty cell otherwise.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      peerParam - the p request parameter when peers are currently shown, null when they are hidden
      isConnected - the router is connected to the I2P network
      noSnarks - no torrents are loaded
      scan - activity flags computed from the listed torrents
    • appendNameTypeHeader

      private void appendNameTypeHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtTorrent)
      Torrent name/type column: cycles via nextTorrentNameTypeSort(String); active-state detection mirrors the five name/type keys.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtTorrent - localized label for the torrent name column
    • appendEtaHeader

      private void appendEtaHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtETA, boolean isConnected, boolean noSnarks, I2PSnarkServlet.TorrentActivityScan scan)
      ETA column: sortable only while something on the page is downloading.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtETA - localized label for the ETA column
      isConnected - the router is connected to the I2P network
      noSnarks - no torrents are loaded
      scan - activity flags computed from the listed torrents; the column is sortable only when isDownloading is set
    • appendRxHeader

      private void appendRxHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtRX, boolean noSnarks)
      RX column: four-state cycle via nextRXSort(String) whenever the list is sortable; empty cell otherwise.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtRX - localized label for the downloaded-bytes column
      noSnarks - no torrents are loaded
    • appendRateDownHeader

      private void appendRateDownHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtRXRate, String txtRX, String peerParam, boolean isConnected, boolean noSnarks, I2PSnarkServlet.TorrentActivityScan scan)
      Download-rate column: sortable only while downloading; direction icon reflects the current key.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtRXRate - localized label for the download-rate column
      txtRX - localized label for the downloaded-bytes column, used in the sort tooltip
      peerParam - the p request parameter when peers are shown, null when they are hidden
      isConnected - the router is connected to the I2P network
      noSnarks - no torrents are loaded
      scan - activity flags computed from the listed torrents; the column is sortable only when isDownloading is set
    • appendTxHeader

      private void appendTxHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtTX)
      TX column: four-state cycle via nextTXSort(String) whenever the list is sortable; empty cell otherwise.
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtTX - localized label for the uploaded-bytes column
    • appendRateUpHeader

      private void appendRateUpHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String txtTXRate, boolean isConnected, boolean noSnarks, I2PSnarkServlet.TorrentActivityScan scan)
      Upload-rate column: sortable only while uploading; ascending icon when currently on "9" (note the inverted default versus rateDown).
      Parameters:
      buf - destination for the <th> markup
      hc - shared request-derived inputs: request, path, sort state and query parts
      txtTXRate - localized label for the upload-rate column
      isConnected - the router is connected to the I2P network
      noSnarks - no torrents are loaded
      scan - activity flags computed from the listed torrents; the column is sortable only when isUploading is set
    • appendActionsHeader

      private void appendActionsHeader(StringBuilder buf, List<Snark> snarks, boolean isConnected, boolean noSnarks, String txtStartAll, String txtStopAll, String txtStopAllTitle, String txtStartAllTitle, String txtStartStoppedTitle)
      Action column: Stop All always when connected; Start All variants depending on whether any listed torrent is stopped.
      Parameters:
      buf - destination for the <th> markup
      snarks - the torrents on the page; the first stopped one triggers the Start All button
      isConnected - the router is connected to the I2P network
      noSnarks - no torrents are loaded
      txtStartAll - label of the Start All button
      txtStopAll - label of the Stop All button
      txtStopAllTitle - title attribute for the Stop All button
      txtStartAllTitle - title attribute when no torrent is stopped
      txtStartStoppedTitle - title attribute when a stopped torrent exists
    • appendSortHeader

      private void appendSortHeader(StringBuilder buf, I2PSnarkServlet.SortHeaderContext hc, String newSort, String cssClass, String iconName, String title, boolean showSort, boolean currentSortMatches, boolean isDescending)
      Appends a sortable table header cell including sort icons and links. Shows ascending or descending icon only if this header matches current sorting.
      Parameters:
      buf - the StringBuilder used to append HTML content
      hc - shared request-derived inputs (request, path, sort state, query parts)
      newSort - the sort parameter value to link to for toggling sorting
      cssClass - CSS class to apply to the <th> element
      iconName - icon identifier for rendering
      title - localized title text for the header cell
      showSort - if true, render sorting link and icons; otherwise render plain header
      currentSortMatches - true if this header corresponds to the current sort parameter (active sorted column)
      isDescending - true if the current sorting direction for this header is descending
    • multiDestActive

      boolean multiDestActive()
      Whether multi-destination mode is active: enabled in the config with at least one active destination. Gates everything pool-related - the per-row destination badges, status+pool sorting, and the clamp that keeps StatusPoolComparator out of single-dest requests.
      Returns:
      true when multi-dest mode is in use
    • appendSnarkFooter

      private void appendSnarkFooter(PrintWriter out, StringBuilder buf, I2PSnarkServlet.FooterContext fc) throws IOException
      Appends the footer section for the torrent list table, displaying overall statistics, counters, and status indicators with associated icons. If the manager is in a connecting state, outputs a placeholder footer indicating initialization. Otherwise, outputs detailed stats including disk usage, torrent count, file size, peer counts, active downloads/uploads, tunnel counts (if applicable), and connection speed metrics.
      Parameters:
      out - the PrintWriter to write HTML output to
      buf - a reusable StringBuilder used for generating icon HTML snippets
      fc - aggregated footer state: counters, totals and flags
      Throws:
      IOException - if writing to the output stream fails
    • appendDebugRow

      private void appendDebugRow(PrintWriter out, DHT dht, String peerParam) throws IOException
      Renders the DHT debug row as the second row of the snark footer, hidden unless debug mode (?p=2) is active. Kept inside the single #snarkFoot tfoot so browsers lay it out below the totals row, as multiple tfoot elements render out of order.
      Parameters:
      out - target for the HTML
      dht - the DHT, or null if not running
      peerParam - the p request parameter; the row is visible only when it equals 2
      Throws:
      IOException - if writing the row fails
    • buildLink

      static String buildLink(String path, String qs, String extra)
      Composes a link URL from a path, a servlet-generated query string, and extra query text, choosing the '?'/'&' separators by inspection rather than by caller-supplied guesses about the raw request. Robust against every historical producer form: qs may begin with '?' (getQueryString contract), be bare ("k=v"), or be empty; extra may be empty or carry multiple "&"-joined pairs without a leading mark. Replaces six hand-rolled joins whose separator logic silently broke sort/filter persistence on parameterless page loads.
      Parameters:
      path - leading URL path including trailing '/'
      qs - query portion from getQueryString(HttpServletRequest, String, String, String, String): '', 'k=v' or '?k=v...'
      extra - additional pairs joined by '&', no leading mark, may be empty
      Returns:
      composed URL, never null
    • renderFilterBar

      private void renderFilterBar(PrintWriter out, HttpServletRequest req) throws IOException
      Renders the torrent filter bar with links for filtering and searching torrents. Constructs URLs preserving existing query parameters except the current filter, then appends updated filter criteria. Displays badges with counts for some filters.
      Parameters:
      out - the PrintWriter to write the generated HTML output
      req - the current HttpServletRequest providing query parameters and URL
      Throws:
      IOException - if an I/O error occurs during writing to the output stream
    • buildFilterLink

      private String buildFilterLink(String baseUrl, String filterId, boolean visible, String title, String badgeText)
      Builds an HTML filter link anchor element with an optional badge and visibility toggle.
      Parameters:
      baseUrl - the base URL to which the filter id is appended as a parameter
      filterId - the id and filter name used in the link
      visible - whether the link should be visible or hidden (via CSS)
      title - the display text for the filter link
      badgeText - optional badge content to display inside the link
      Returns:
      the constructed HTML string of the filter link
    • buildSimpleFilterLink

      private String buildSimpleFilterLink(String baseUrl, String filterId, String title, String badge)
      Builds a simple HTML filter link anchor element with a badge but no visibility toggle.
      Parameters:
      baseUrl - the base URL to which the filter id is appended as a parameter
      filterId - the id and filter name used in the link
      title - the display text for the filter link
      badge - the badge HTML to append inside the link
      Returns:
      the constructed HTML string of the filter link
    • search

      private static List<Snark> search(String search, Collection<Snark> snarks)
      Search torrents for matching terms.
      Parameters:
      search - non-null
      snarks - torrents to search; not modified
      Returns:
      null if no valid search, or matching torrents in same order, empty if no match
    • writeHiddenInputs

      void writeHiddenInputs(PrintWriter out, HttpServletRequest req, String action)
      Hidden inputs for the nonce and parameters p, st, and sort.
      Parameters:
      out - writes to it
      req - the HTTP request, read for the p, st, sort and search parameters
      action - if non-null, add it as the action
    • writeHiddenInputs

      void writeHiddenInputs(StringBuilder buf, HttpServletRequest req, String action)
      Hidden inputs for the nonce and parameters p, st, and sort. Emitted in a fixed order; absent request values are omitted.
      Parameters:
      buf - appends to it
      req - the HTTP request, read for the p, st, sort and search parameters
      action - if non-null, add it as the action
    • appendHiddenInput

      private static void appendHiddenInput(StringBuilder buf, String name, String value, boolean esc)
      Append a single hidden form input.
      Parameters:
      buf - destination for the input markup
      name - the form field name, written unquoted
      value - the field value, sanitized per esc
      esc - true to HTML-escape the value (free-text fields such as search), false to strip HTML (numeric and enum fields)
    • normalizeSortParam

      private static String normalizeSortParam(String sort)
      Normalize a human-readable sort parameter to its legacy numeric form, e.g. "pool-desc" to "-13", so readable URLs survive header cycling, hidden inputs and query-string rebuilds. Numeric and unrecognized values pass through unchanged.
      Parameters:
      sort - the raw sort parameter, or null
      Returns:
      the normalized sort parameter, or null
    • getQueryString

      private static String getQueryString(HttpServletRequest req, String p, String st, String so, String search)
      Query string with an optional search override. Parameters are emitted in fixed p, sort, st, search order. Only values that are exactly signed integers are emitted, so a free-text search override never round-trips through these URLs.
      Parameters:
      req - the current request, for inherited values
      p - page override, or null to inherit and sanitize from the request
      st - start-index override, or null to inherit and sanitize
      so - sort override, or null to inherit and normalize
      search - search override, or null to inherit and HTML-escape
      Returns:
      the query string, beginning with '?' or empty
    • appendQueryParam

      private static void appendQueryParam(StringBuilder buf, HttpServletRequest req, String name, String override)
      Append one query pair to buf if it resolves to an emittable value. A null override inherits the value from the request, applying the per-name sanitization the legacy map-based implementation used: sort is normalized, p/sort/st are HTML-stripped, search is HTML-escaped. Explicit overrides are trusted as caller-provided constants. Emission requires an exact signed integer, matching the validation applied when such links are later parsed.
      Parameters:
      buf - destination buffer, prefix '?' or '&' chosen by its state
      req - the current request, for inherited values
      name - parameter name to emit
      override - caller-supplied value, or null to inherit
    • paginator

      private void paginator(PrintWriter out, HttpServletRequest req, int start, int pageSize, int total, String filter, boolean noThinsp, boolean isForm, boolean searchActive, int searchLength) throws IOException
      Renders the page navigation controls with visibility handling.
      Parameters:
      out - the PrintWriter to write HTML output to
      req - the HttpServletRequest containing request parameters
      start - the starting index of the current page view
      pageSize - the number of items per page
      total - the total number of items available
      filter - the current filter parameter string
      noThinsp - flag indicating special rendering for certain browsers
      isForm - whether the page is rendering as a form (affects visibility logic)
      searchActive - whether a search is currently active on the page
      searchLength - length of the active search string
      Throws:
      IOException - if an error occurs writing output
    • processRequest

      private boolean processRequest(HttpServletRequest req, HttpServletResponse resp)
      Process HTTP request to handle torrent-related actions. Delegates to specific handlers based on the action parameter.
      Parameters:
      req - the request whose action parameter selects the handler
      resp - the response, used by handlers that write it directly
      Returns:
      true if a handler fully consumed the response and the caller must not render; false to continue with normal rendering
    • extractAction

      private String extractAction(HttpServletRequest req)
      Extracts the action parameter from the request. Checks "action" parameter and fallback to keys starting with "do_".
      Parameters:
      req - the HTTP request
      Returns:
      the extracted action or null if none found
    • handleAdd

      private void handleAdd(HttpServletRequest req)
      Handles the "Add" action, processes file uploads or URLs to add torrents.
      Parameters:
      req - the HTTP request
    • handleBrowserApiAdd

      private void handleBrowserApiAdd(HttpServletRequest req, HttpServletResponse resp) throws IOException
      Nonce-free browser API: adds a magnet link, torrent URL, info hash, or local torrent file path. POST only. Authorized when the browser API is enabled and the client is loopback, in the configured allowed hosts list, or presents a valid API key. Replies with a single text/plain status line.
      Parameters:
      req - the HTTP request
      resp - the HTTP response
      Throws:
      IOException - on write failure
    • handleMagnetPage

      private void handleMagnetPage(HttpServletRequest req, HttpServletResponse resp) throws IOException
      The page the browser opens when a magnet: link is handed to the I2PSnark Bridge extension (registered via protocol_handlers in the extension manifest). The page itself performs no server-side action. The external script magnetHandler.js reads the magnet from the URL, POSTs it to the browser API /_add on the same origin, dispatches a CustomEvent the extension's content script forwards to the background script for a browser notification, and closes the tab immediately. The page body is intentionally blank so the tab the protocol handler opens is never noticed; the result is only shown in the browser notification.

      The magnet arrives in the URL, so the nofilter_ parameter name is required: the XSS filter strips '&' from filtered parameter values, and every real magnet link contains '&'.

      GET only; the existing /_add authorization applies to the actual add.

      Parameters:
      req - the HTTP request
      resp - the HTTP response
      Throws:
      IOException - on write failure
    • buildBridgeMetaTag

      public static String buildBridgeMetaTag(String baseUrl)
      The meta element advertising the base URL of the I2PSnark webapp that served this page, so the I2PSnark Bridge extension's content script can discover the exact origin + context path (host, port, https) and route subsequent magnet: handoffs to it. This is authoritative even when the context path or listening port differs from the extension's manifest default (e.g. standalone I2PSnark on port 8002, custom console port).
      Parameters:
      baseUrl - the webapp base URL, e.g. http://127.0.0.1:8002/i2psnark
      Returns:
      a single HTML meta line, or empty string when no URL is available
    • browserApiAuthorized

      private boolean browserApiAuthorized(HttpServletRequest req)
      Whether the browser API request is authorized. Requires the API to be enabled and the client to be loopback, on the allowed hosts list, or to present the configured API key.
      Parameters:
      req - the request, for the client address and apiKey parameter
      Returns:
      true if the request may use the browser API
    • browserApiAuthorized

      public static boolean browserApiAuthorized(boolean enabled, boolean hostAllowed, String providedKey, String configuredKey)
      Pure decision function for browser API authorization (unit-testable): requires the API to be enabled and the client to be an allowed host or to present the configured API key.
      Parameters:
      enabled - the browser API enable flag
      hostAllowed - whether the client address is loopback or on the allowlist
      providedKey - the apiKey request parameter, may be null
      configuredKey - the configured API key, may be null or empty
      Returns:
      whether the request is authorized
    • browserApiName

      private String browserApiName(String url)
      Validate the browser API payload and extract a display name for the OK reply. Mirrors the formats accepted by handleAddURL(String, File, RequestWrapper).
      Parameters:
      url - the URL, magnet, info hash, or file path
      Returns:
      a display name, or null if unsupported
    • handleAddFile

      private void handleAddFile(String newFile, File torrentDir, File dataDir, RequestWrapper reqw)
      Handles adding a torrent from an uploaded file.
      Parameters:
      newFile - filename from upload
      torrentDir - directory where uploaded .torrent files are kept
      dataDir - data directory where torrents are stored
      reqw - wrapped request for accessing multipart inputs
    • handleAddURL

      private void handleAddURL(String newURL, File dataDir, RequestWrapper reqw)
      Handles adding a torrent from a URL or magnet link.
      Parameters:
      newURL - the URL or magnet link string
      dataDir - directory the new torrent's files are created under
      reqw - the wrapped request, read for the nofilter_ parameters
    • isValidHexInfoHash

      public static boolean isValidHexInfoHash(String s)
      Validates if a string is a valid 40-hex character info hash.
      Parameters:
      s - candidate v1 info hash: exactly 40 hex characters
      Returns:
      whether valid hex info hash
    • isValidBase32InfoHash

      public static boolean isValidBase32InfoHash(String s)
      Validates if a string is a valid 32-base32 character info hash.
      Parameters:
      s - candidate v1 info hash: exactly 32 base32 characters
      Returns:
      whether valid base32 info hash
    • isValidV2InfoHash

      public static boolean isValidV2InfoHash(String s)
      Validates if string is version 2 hex multihash (68 characters starting with "1220").
      Parameters:
      s - candidate v2 info hash: 68 hex characters starting with "1220"
      Returns:
      whether valid v2 info hash
    • handleAddFromFilePath

      private void handleAddFromFilePath(String newURL, File dataDir)
      Handles adding a torrent from a file path.
      Parameters:
      newURL - file path to .torrent file, file:// prefix tolerated
      dataDir - directory the torrent's storage is resolved against
    • resolveTorrentByToken

      private Snark resolveTorrentByToken(String token)
      Resolves a short action token to its torrent by unique-prefix match over the loaded torrents' base64 info-hash names. Ambiguous or unknown tokens return null, making stale submissions a safe no-op.
      Parameters:
      token - the token extracted from a submitted control name
      Returns:
      the matching Snark, or null when none or several match
    • handleStop

      private void handleStop(String action)
      Handles the "Stop_" action to stop a torrent by action token.
      Parameters:
      action - the tracker action to perform, as submitted by the form string starting with "Stop_"
    • handleStart

      private void handleStart(String action)
      Handles the "Start_" action to start a torrent by action token.
      Parameters:
      action - the tracker action to perform, as submitted by the form string starting with "Start_"
    • handleRemove

      private void handleRemove(String action)
      Handles the "Remove_" action to remove a torrent by action token.
      Parameters:
      action - the action string starting with "Remove_"
    • unlinkTorrentFile

      private boolean unlinkTorrentFile(Snark snark)
      Unlinks a torrent's .torrent file, reporting the outcome as user messages. Magnet torrents (no metainfo) are deleted outright, since for them the .torrent file is the data.

      Shared by the Remove and Delete actions so both report identical messages for identical outcomes.

      Parameters:
      snark - the torrent whose .torrent file should be removed
      Returns:
      true when no .torrent file remains, so the caller may proceed to remove downloaded data; false when the torrent is still present (magnet deleted, or the file could not be removed)
    • handleDelete

      private void handleDelete(String action)
      Handles the "Delete_" action to delete a torrent and its data by action token.
      Parameters:
      action - the action string starting with "Delete_"
    • handleSave

      private boolean handleSave(HttpServletRequest req, HttpServletResponse resp)
      Handles saving configuration updates.
      Parameters:
      req - the HTTP request with config parameters
      resp - the response, used only when an extension install redirect is issued
      Returns:
      true if the response was fully handled (install redirect sent), false to continue with normal rendering
    • installBrowserApi

      private boolean installBrowserApi(HttpServletRequest req, HttpServletResponse resp)
      Installs the I2PSnark Bridge extension that forwards magnet links to the browser API endpoint. Called from the main config save when the install button was used; enables the API if it is off.

      The server writes no files: the browser is redirected to the bundled extension XPI, which opens the install prompt in the logged-in user's own browser session.

      Parameters:
      req - the HTTP request
      resp - the HTTP response
      Returns:
      true if the response was fully handled (redirect sent)
    • getBridgeVersion

      String getBridgeVersion()
      The version of the I2PSnark Bridge extension bundled in this war, read once from the XPI's manifest.json; null if it cannot be determined.
      Returns:
      the bundled extension version, "" if the XPI carries none
    • browserApiBaseUrl

      private String browserApiBaseUrl(HttpServletRequest req)
      The console base URL for this request, e.g. http://127.0.0.1:7657/i2psnark (no trailing slash), used as the default endpoint of the installed handler.
      Parameters:
      req - the request whose URL and scheme identify the console origin
      Returns:
      the scheme, host, port and context path, without a trailing slash
    • handleSaveTrackers

      private void handleSaveTrackers(HttpServletRequest req)
      Handles saving tracker form updates.
      Parameters:
      req - the HTTP request
    • handleSaveCreateFilters

      private void handleSaveCreateFilters(HttpServletRequest req)
      Handles saving torrent creation filter form updates.
      Parameters:
      req - the HTTP request
    • validateBaseFile

      static I2PSnarkServlet.BaseFileValidationResult validateBaseFile(String baseData, SnarkManager manager)
      Validates the base file for torrent creation. Checks: not empty, exists, not .torrent, not duplicate, not in I2P dirs, not nested.
      Parameters:
      baseData - the raw base file path from request
      manager - the SnarkManager for context (data dir, existing torrents)
      Returns:
      validation result with resolved File and error message (null if valid)
    • parseAnnounceParams

      static I2PSnarkServlet.AnnounceParams parseAnnounceParams(HttpServletRequest req)
      Parses announce URL and backup tracker URLs from the request. Recognized: "announceURL", "backup_*" parameters.
      Parameters:
      req - the HTTP request
      Returns:
      parsed announce parameters, never null
    • buildAnnounceList

      static I2PSnarkServlet.CreateAnnounceListResult buildAnnounceList(I2PSnarkServlet.AnnounceParams params, List<String> privateTrackers)
      Builds the tiered announce list from primary and backup URLs. Validates no mixing of private and public trackers.
      Parameters:
      params - parsed announce parameters
      privateTrackers - the configured private tracker URLs
      Returns:
      announce list result with tiered list and private flag, or error
    • parseCreateFilters

      static List<TorrentCreateFilter> parseCreateFilters(HttpServletRequest req, Map<String, TorrentCreateFilter> filterMap)
      Parses the selected torrent creation filters from the request.
      Parameters:
      req - the HTTP request
      filterMap - the available filters from manager
      Returns:
      list of selected filters (never null)
    • handleCreate

      private void handleCreate(HttpServletRequest req)
      Handles creating a new torrent from provided base file or directory.
      Parameters:
      req - the HTTP request
    • handleStopAll

      private void handleStopAll(HttpServletRequest req)
      Handles stopping all torrents or search-filtered torrents.
      Parameters:
      req - the HTTP request with "search" parameter
    • handleStartAll

      private void handleStartAll(HttpServletRequest req)
      Handles starting all torrents or search-filtered torrents.
      Parameters:
      req - the HTTP request with "search" parameter
    • handleClearMessages

      private void handleClearMessages(HttpServletRequest req)
      Handles clearing messages by id.
      Parameters:
      req - the HTTP request with "id" parameter
    • sendRedirect

      private void sendRedirect(HttpServletRequest req, HttpServletResponse resp, String p) throws IOException
      Redirect a POST to a GET (P-R-G), preserving the query string. The query string must only contain the numeric params that getQueryString(HttpServletRequest, String, String, String, String) emits, so the redirect target can never be an attacker-supplied URL.
      Parameters:
      req - request whose URL supplies the redirect target
      resp - response the 302 is written to
      p - the query string, may be null or empty
      Throws:
      IOException - on write failure
    • processTrackerForm

      private void processTrackerForm(String action, HttpServletRequest req)
      Process tracker form submission.
      Parameters:
      action - the tracker action to perform, as submitted by the form
      req - the request carrying the submitted tracker parameters
    • processTorrentCreateFilterForm

      private void processTorrentCreateFilterForm(String action, HttpServletRequest req)
      Process torrent create filter form submission.
      Parameters:
      action - the tracker action to perform, as submitted by the form
      req - the request carrying the submitted tracker parameters
    • getSortedSnarks

      private List<Snark> getSortedSnarks(HttpServletRequest req)
      Returns the list of torrents sorted according to the current request's sort parameter.
      Parameters:
      req - the HTTP request containing the "sort" parameter
      Returns:
      a new sorted list of Snark instances
    • snarkMatchesFilter

      private boolean snarkMatchesFilter(Snark s, String filter, String snarkStatus)
      Checks whether a torrent matches the given filter based on its status string.
      Parameters:
      s - the Snark torrent to check
      filter - the filter name (e.g., "active", "downloading", "stopped")
      snarkStatus - the status keyword string for the torrent
      Returns:
      true if the torrent matches the filter, or if filter is null/empty
    • badgeInfo

      Look up (or compute) the badge values for a torrent. The base64 destination encoding is by far the most expensive part of building the badge, and pool-sorted rows repeat the same destinations, so results are cached per render in the context's cache.
      Parameters:
      rc - row context carrying the shared cache
      snark - the torrent being rendered
      Returns:
      never-null cached badge info
    • displaySnark

      private void displaySnark(PrintWriter out, I2PSnarkServlet.RowContext rc, StringBuilder buf) throws IOException
      Displays a single snark (torrent) as an HTML table row, including optional peer rows. Updates I2PSnarkServlet.RowContext.stats with cumulative data (uploaded, downloaded, rates, peer counts, and size). Filtering is applied here: rows not matching the active filter contribute to stats but emit no HTML. Appends generated HTML to the provided StringBuilder buffer instead of writing directly to output, allowing batching and improved rendering performance.
      Parameters:
      out - the PrintWriter the completed row buffer is appended to
      rc - per-row and per-render inputs; stats contents are updated in place
      buf - scratch buffer reset by the caller between rows
      Throws:
      IOException - if an I/O error occurs during output operations
    • injectPoolBadge

      private String injectPoolBadge(String statusHtml, Snark snark, I2PSnarkServlet.RowContext rc)
      Injects the multi-dest pool badge into the status cell HTML. The badge carries the sequential pool number (or D for dedicated) and the destination prefix as tooltip, inserted before the closing </td>.
      Parameters:
      statusHtml - the status cell inner HTML, modified by this call
      snark - the torrent whose destination to display
      rc - row context carrying the shared badge cache; must not be null
      Returns:
      the status HTML with the pool badge injected
    • appendTorrentActionButton

      private void appendTorrentActionButton(StringBuilder buf, String action, String token, Snark snark, String title)
      Append a Remove or Delete submit button for a torrent row. Both carry the JS-escaped torrent names consumed by the row's confirmation script.
      Parameters:
      buf - destination buffer for the row HTML
      action - action suffix appended to the request parameter name
      token - base64 info hash forming the parameter name with the action
      snark - the torrent the button acts on
      title - tooltip text describing what will be removed or deleted
    • classifyStatus

      static I2PSnarkServlet.StatusKind classifyStatus(boolean allocating, boolean checking, boolean starting, boolean trackerProblems, boolean running, boolean complete, int curPeers, int knownPeers, boolean uploading, boolean downloading)
      Pure decision function mirroring the historical if/else cascade of buildStatusString(Snark, int, int, long, long, boolean, long, long, boolean), extracted so state classification can be tested without rendering. Branch order here is load-bearing: earlier states win, exactly as in the original cascade.
      Parameters:
      allocating - torrent is allocating disk space
      checking - torrent is being checked or rechecked
      starting - start requested, tunnels not built yet
      trackerProblems - tracker unreachable for over an hour while idle
      running - torrent is neither stopped nor errored
      complete - all pieces present (remaining or needed == 0)
      curPeers - connected peer count
      knownPeers - total peers known from trackers/DHT/PEX
      uploading - current upload rate above zero
      downloading - current download rate above zero
      Returns:
      the single display state for this torrent
    • buildStatusString

      private I2PSnarkServlet.StatusResult buildStatusString(Snark snark, int curPeers, int knownPeers, long downBps, long upBps, boolean isRunning, long remaining, long needed, boolean noThinsp)
      Generates HTML status string and status code for a Snark based on its state and peer info. Classification is delegated to classifyStatus(boolean, boolean, boolean, boolean, boolean, boolean, int, int, boolean, boolean); this method only renders.
      Parameters:
      snark - the torrent whose state and peers are being rendered
      curPeers - current connected peers
      knownPeers - total known peers
      downBps - current download rate in bytes per second
      upBps - current upload rate in bytes per second
      isRunning - true if the torrent is started
      remaining - data left to download
      needed - data needed for completion
      noThinsp - spacing control flag
      Returns:
      StatusResult containing the status HTML and status keyword
    • peerCountHtml

      static String peerCountHtml(int curPeers, int knownPeers, boolean noThinsp)
      The peer-count cell shared by most status branches: current peers on the right, known peers in the swarm on the left.

      Called from buildStatusString(Snark, int, int, long, long, boolean, long, long, boolean) per rendered row rather than hoisted to a single local, because four of its fourteen branches emit their own peer-count markup (em dash, or an empty cell) and would throw the shared value away.

      Parameters:
      curPeers - currently connected peers
      knownPeers - peers known from trackers, DHT, and PEX
      noThinsp - true to substitute a plain slash for the thin-space pair
      Returns:
      the peer-count cell markup, ending the preceding status cell
    • classifyPeerStatus

      static I2PSnarkServlet.PeerStatus classifyPeerStatus(Peer peer)
      Determines the peer status (active/inactive with TX/RX flags).
      Parameters:
      peer - the peer to check
      Returns:
      peer status with flags
    • appendPeerRow

      private void appendPeerRow(StringBuilder buf, Peer peer, Snark snark, MetaInfo meta, boolean noThinsp)
      Appends HTML for a single peer row to the given StringBuilder.
      Parameters:
      buf - the StringBuilder to append to
      peer - the Peer object to render
      snark - the Snark (torrent) the peer belongs to
      meta - the MetaInfo of the torrent (may be null)
      noThinsp - whether to suppress thin space characters
    • renderPeerIdentity

      private void renderPeerIdentity(StringBuilder buf, Peer peer, I2PSnarkServlet.PeerRowContext ctx)
      Renders the peer identity/client section.
      Parameters:
      buf - the StringBuilder to append to
      peer - the peer to render
      ctx - the rendering context
    • renderPeerInactivity

      private void renderPeerInactivity(StringBuilder buf, long inactiveTime)
      Renders the peer inactivity bar if applicable.
      Parameters:
      buf - the StringBuilder to append to
      inactiveTime - the peer's inactive time in milliseconds
    • renderPeerProgress

      private float renderPeerProgress(StringBuilder buf, Peer peer, I2PSnarkServlet.PeerRowContext ctx)
      Renders the peer progress bar or seed indicator.
      Parameters:
      buf - the StringBuilder to append to
      peer - the peer to render
      ctx - the rendering context
      Returns:
      the completion percentage (101.0f if unknown)
    • renderDownloadRate

      private void renderDownloadRate(StringBuilder buf, Peer peer, I2PSnarkServlet.PeerRowContext ctx, long needed)
      Renders the download rate cell.
      Parameters:
      buf - the StringBuilder to append to
      peer - the peer to render
      ctx - the rendering context
      needed - the needed length
    • renderUploadRate

      private void renderUploadRate(StringBuilder buf, Peer peer, I2PSnarkServlet.PeerRowContext ctx, float pct, boolean isValid)
      Renders the upload rate cell.
      Parameters:
      buf - the StringBuilder to append to
      peer - the peer to render
      ctx - the rendering context
      pct - the completion percentage
      isValid - whether meta is valid
    • getClientName

      private String getClientName(Peer peer)
      Returns a human-readable name of the client associated with the given Peer. <p>Most BitTorrent clients identify themselves using a 4-character prefix in the PeerID, often based on the client's identifier in the BitTorrent protocol handshake. These prefixes typically encode client-specific information (e.g., "-LT" for libtorrent, "-qB" for qBittorrent).</p> <p>This method maps known PeerID prefixes to client names using standardized conventions and attempts to extract client names from the handshake data if no match is found.</p> <p>Special cases: <ul> <li>I2PSnark PeerID starts with "AwMD" (Base64 encoding of \3\3\3)</li> <li>Handshake "v" field is used as a fallback to identify unknown clients</li> </ul>
      Parameters:
      peer - The Peer object to analyze.
      Returns:
      A string representing the detected client name, or "Unknown" if undetermined.
    • formatSizeSpans

      private String formatSizeSpans(String formattedSize, boolean closeLeftSpan)
      Formats a human-readable size string by wrapping unit characters (B, K, M, G, T) in HTML span tags with CSS classes for styling. <p> This method removes any "iB" substring and replaces each unit character with a corresponding closing and opening span tag sequence. The caller controls whether the left span is closed immediately by the closeLeftSpan flag.
      Parameters:
      formattedSize - the size string already formatted (e.g. "123.4 MiB")
      closeLeftSpan - whether to append a closing </span> tag after each unit
      Returns:
      the formatted string with added HTML span tags for styling unit characters
    • escapeJSString

      static String escapeJSString(String s)
      Escape a string for embedding in an HTML attribute value that page scripts read back into JavaScript strings, e.g. the data-name and client attributes on the torrent action buttons. The backslash is escaped first so the generated escape sequences are not re-escaped, followed by the quote characters, the HTML-significant angle brackets and ampersand, and the line-break characters that cannot appear literally inside a JavaScript string literal. Output is ASCII-only. Escapes are emitted as JS short escapes for line breaks and two-digit hex escapes for everything else; they are deliberately spelled without the four-hex-digit unicode form because java's lexer processes such sequences before string escapes, even inside this comment. Not for use in URL or CSS contexts.
      Parameters:
      s - non-null string to escape
      Returns:
      the escaped string, ASCII-only
    • thinsp

      private static String thinsp(boolean disable)
      Thin-space markup, disabled if requested.
      Parameters:
      disable - true to emit a plain " / " separator instead of the thin-space markup
      Returns:
      the separator markup, never null
    • getTrackerLinkUrl

      private String getTrackerLinkUrl(String announce, byte[] infohash)
      Link to the details page if the tracker supports it. Start of anchor only; caller must add anchor text or img and close the anchor.
      Parameters:
      announce - the tracker announce URL, may be null
      infohash - the 20-byte info hash, url-encoded into the link
      Returns:
      the opening anchor tag, or null if the tracker has no details page support
    • getTrackerLink

      private String getTrackerLink(String announce, byte[] infohash)
      Full link to the details page with an image.
      Parameters:
      announce - the tracker announce URL
      infohash - the 20-byte info hash, url-encoded into the link
      Returns:
      the complete anchor with icon, or null when the tracker has no details page support
    • getShortTrackerLink

      private String getShortTrackerLink(String announce, byte[] infohash)
      Full anchor to the home page or details page with shortened hostname as anchor text.
      Parameters:
      announce - the tracker announce URL; the scheme, path and port are stripped for the anchor text
      infohash - the 20-byte info hash, used only for the details-page link
      Returns:
      the complete anchor, never null
    • writeAddForm

      private void writeAddForm(PrintWriter out, HttpServletRequest req) throws IOException
      Writes the HTML form for adding new torrents via URL or file upload.
      Parameters:
      out - the PrintWriter to write the HTML output
      req - the HTTP request containing query parameters
      Throws:
      IOException - if an I/O error occurs during writing
    • writeSeedForm

      private void writeSeedForm(PrintWriter out, HttpServletRequest req, List<Tracker> sortedTrackers, List<TorrentCreateFilter> sortedFilters) throws IOException
      Writes the HTML form for creating new torrents from local files.
      Parameters:
      out - the PrintWriter to write the HTML output
      req - the HTTP request containing query parameters
      sortedTrackers - the list of available trackers for selection
      sortedFilters - the list of available torrent create filters
      Throws:
      IOException - if an I/O error occurs during writing
    • isFirefoxFamilyUserAgent

      public static boolean isFirefoxFamilyUserAgent(String ua)
      Whether the given User-Agent header indicates a Firefox-family browser (Firefox, LibreWolf, Waterfox, Pale Moon, Tor Browser), which can install the I2PSnark Bridge extension. Unit-testable; other browsers get the installer-script fallback instead.
      Parameters:
      ua - the User-Agent header, may be null
      Returns:
      true if the UA names Firefox
    • isWindowsUserAgent

      public static boolean isWindowsUserAgent(String ua)
      Whether the given User-Agent header indicates Windows, which gets the PowerShell installer script instead of the shell one.
      Parameters:
      ua - the User-Agent header, may be null
      Returns:
      true if the UA names Windows
    • addMagnet

      private void addMagnet(String url, File dataDir)
      Add a torrent from a magnet URL.
      Parameters:
      url - in base32 or hex
      dataDir - null to default to snark data directory
    • _t

      String _t(String s)
      Translate a string.
      Parameters:
      s - the message key
      Returns:
      the translation in the current language, or the key itself if untranslated
    • _t

      String _t(String s, Object o)
      Translate a string with one argument.
      Parameters:
      s - the message key
      o - substituted for the {0} placeholder
      Returns:
      the translation in the current language
    • _t

      String _t(String s, Object o, Object o2)
      Translate a string with two arguments.
      Parameters:
      s - the message key
      o - substituted for the {0} placeholder
      o2 - substituted for the {1} placeholder
      Returns:
      the translation in the current language
    • ngettext

      String ngettext(String s, String p, int n)
      Translate a pluralized string. @since 0.7.14.
      Parameters:
      s - the singular message key
      p - the plural message key
      n - the count selecting between them
      Returns:
      the translation for the count's plural form
    • formatSize

      private static String formatSize(long bytes)
      Format the file size.
      Parameters:
      bytes - the size in bytes
      Returns:
      the size with a binary unit suffix and a 'B'
    • urlify

      static String urlify(String s)
      This is for a full URL. For a path only, use encodePath().
      Parameters:
      s - the URL to wrap in an anchor
      Returns:
      an anchor tag, truncated to 100 characters of display text
    • urlify

      static String urlify(String s, int max)
      This is for a full URL. For a path only, use encodePath().
      Parameters:
      s - the URL to wrap in an anchor
      max - the display text is cut to this many characters and elided
      Returns:
      an anchor tag opening in a new window
    • urlEncode

      private static String urlEncode(String s)
      This is for a full URL. For a path only, use encodePath().

      Every caller embeds the result inside a double-quoted href attribute, so a literal quote would close the attribute and let the rest of the URL become new attributes - a stored XSS through any URL the user or a tracker supplied.

      Parameters:
      s - the full URL to encode
      Returns:
      the URL with ; & < > quote and brackets encoded, safe inside a double-quoted href attribute
    • cssLink

      private static String cssLink(String filename, String themePath, String... attributes)
      Generates a CSS link tag for the given filename.
      Parameters:
      filename - the CSS filename (e.g., "snark.css")
      themePath - the theme path
      attributes - optional additional attributes (e.g., "id=snarkTheme")
      Returns:
      complete link tag
    • shouldStreamTorrentRows

      static boolean shouldStreamTorrentRows(int visibleRows)
      Whether a torrent list rendering visibleRows entries uses streamed output.
      Parameters:
      visibleRows - entries on the rendered page after filtering, non-negative
      Returns:
      true once STREAM_MIN_TORRENT_ROWS is reached
    • shouldStreamFileRows

      static boolean shouldStreamFileRows(int visibleRows)
      Whether a directory file table rendering visibleRows entries uses streamed output.
      Parameters:
      visibleRows - entries actually rendered after filtering, non-negative
      Returns:
      true once STREAM_MIN_FILE_ROWS is reached
    • drainTo

      static boolean drainTo(PrintWriter out, StringBuilder buf)
      Pushes staged markup to the response writer and empties the buffer, forcing bytes toward the client so large pages render progressively. Called only in streamed mode; buffered mode never drains.
      Parameters:
      out - non-null response writer
      buf - staging buffer, emptied regardless of outcome
      Returns:
      false if the client disconnected (writer error); callers should skip further drains, rendering continues only to keep counters and closing markup consistent
    • getListHTML

      String getListHTML(File xxxr, String base, boolean parent, Map<String,String[]> postParams, String sortParam, PrintWriter out) throws IOException
      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. Get the resource list as a HTML directory listing. Section map, in output order:
      1. POST dispatch - torrent actions (priorities, comments, stop/start/ recheck, edit) via handleDirectoryPost(Snark, Map); P-R-G means no rendering after a POST
      2. renderHeader(StringBuilder, String) - doctype, head, theme/font CSS, navbar
      3. form open, appendTorrentInfo(StringBuilder, Snark, String, String, boolean), displayTorrentEdit()
      4. appendResourceError(StringBuilder, File, String, String) when the path does not exist on disk
      5. no-listing branch - appendMediaSection(StringBuilder, String, String, String, boolean, String) player, comments section, footer, return
      6. file listing - wrapFileList(File[], Storage, long[], long[], boolean) + sort, appendFileTableHead(), appendParentDirRow(StringBuilder, String, String, String, boolean, boolean, boolean), renderFileRow() loop, per-counter scripts; streamed when out != null and the table reaches STREAM_MIN_FILE_ROWS
      7. renderCommentsSection(Snark, boolean, boolean, boolean, String, StringBuilder), form close, lightbox/refresh scripts, footer
      Parameters:
      xxxr - The Resource unused
      base - The encoded base URL
      parent - True if the parent directory should be included
      postParams - map of POST parameters or null if not a POST
      sortParam - the file sort key from the request, or null
      out - the response writer for streamed mode, or null to buffer everything into the returned string
      Returns:
      buffered mode: the full page; streamed mode: the tail remaining after the last drained chunk (possibly empty); null only when postParams != null (P-R-G)
      Throws:
      IOException - if the response writer or resource lookups fail
    • handleDirectoryPost

      private void handleDirectoryPost(Snark snark, Map<String,String[]> postParams)
      Dispatch a POST on the torrent directory page to its action, validating the anti-CSRF nonce first. Never renders; the caller responds with a redirect (P-R-G). A missing nonce is ignored silently; an invalid nonce or an unrecognized command adds a console message instead. Extracted from getListHTML.
      Parameters:
      snark - non-null target torrent
      postParams - the POST parameter map, non-null
    • findPostAction

      static String findPostAction(Map<String,String[]> postParams)
      Returns the first matching command key from a directory-page POST, in the fixed precedence order of DIRECTORY_POST_ACTIONS. Static and side-effect-free for testability.
      Parameters:
      postParams - the POST parameter map, non-null
      Returns:
      the matched action key, or null if none is present
    • executeDirectoryPostAction

      private void executeDirectoryPostAction(Snark snark, Map<String,String[]> postParams, String action)
      Performs the side effects of a validated directory-page POST command.
      Parameters:
      snark - non-null target torrent
      postParams - the full POST parameter map, needed by form handlers
      action - a key returned by findPostAction(Map), non-null
    • renderHeader

      private void renderHeader(StringBuilder buf, String directory)
      Renders the doctype, HTML head (theme and font CSS, CSP'd theme script, favicon) and the navbar. All inputs come from servlet state so page assembly stays allocation-light; only the directory display name is passed in. Extracted from getListHTML.
      Parameters:
      buf - StringBuilder the markup is appended to
      directory - decoded directory display name for the page title, escaped here; may be empty
    • appendResourceError

      private void appendResourceError(StringBuilder buf, File r, String base, String tName)
      Renders the error table shown when the requested path within the torrent does not exist on disk. Extracted from getListHTML.
      Parameters:
      buf - StringBuilder the markup is appended to
      r - the missing resource path
      base - the encoded base URL
      tName - the torrent name, escaped here
    • appendMediaSection

      private static void appendMediaSection(StringBuilder buf, String base, String tName, String mime, boolean isAudio, String imgPath)
      Renders the HTML5 audio/video player section for a complete single-media torrent. Static: no servlet state required beyond the parameters. Extracted from getListHTML.
      Parameters:
      buf - StringBuilder the markup is appended to
      base - the encoded base URL with trailing slash
      tName - the torrent name, escaped here
      mime - the non-null media MIME type
      isAudio - true for audio, false for video
      imgPath - web-visible path to the console icons
    • isPaddingDir

      static boolean isPaddingDir(File f)
      Whether a filesystem entry is a legacy padding directory that should be hidden in top-level listings. Old versions created ".pad"/"_pad" directories. Storage no longer does, but they may still exist on disk. Extracted from getListHTML for testability.
      Parameters:
      f - the entry, non-null
      Returns:
      true if it is a padding directory
    • wrapFileList

      static List<Sorters.FileAndIndex> wrapFileList(File[] ls, Storage storage, long[] remainingArray, long[] previewArray, boolean skipPaddingDirs)
      Wraps a raw directory listing in Sorters.FileAndIndex entries carrying the precomputed per-file remaining/preview arrays, optionally hiding legacy padding directories. Pure wrapping; sorting is the caller's job. Extracted from getListHTML for testability.
      Parameters:
      ls - the raw listing, non-null
      storage - may be null (magnet/dummy); arrays must then be null too
      remainingArray - precomputed by Storage.remaining2(), null iff storage is null
      previewArray - may be null
      skipPaddingDirs - true hides ".pad"/"_pad" directories
      Returns:
      wrapped entries, possibly empty, never null
    • nextNameTypeSort

      static String nextNameTypeSort(String sortParam)
      Next value of the name/file-type sort cycle used by the Directory column header: unset/0/1 sorts name descending ("-1"), which offers the type sort ("12"), whose reverse ("-12") restarts unsorted (""). Must stay consistent with isTypeSortNext(String). Extracted from getListHTML for testability.
      Parameters:
      sortParam - current sort key from the request, may be null
      Returns:
      the next sort key, possibly empty, never null
    • isTypeSortNext

      static boolean isTypeSortNext(String sortParam)
      Whether the Directory column's next sort target is the file type, i.e. the current key sits in the name half ("-1"/"12") of the cycle. Drives only the link tooltip; must stay consistent with nextNameTypeSort(String).
      Parameters:
      sortParam - current sort key, may be null
      Returns:
      true if the tooltip should advertise type sorting
    • appendSortToggleLink

      private void appendSortToggleLink(StringBuilder buf, String base, String icon, String label, String tooltip, String nextSort, String id)
      Appends an icon-only column-header link that toggles to nextSort on click. Shared by all sortable columns of the file table; dedupes four identical link/icon sequences in getListHTML.
      Parameters:
      buf - StringBuilder the link markup is appended to
      base - the encoded base URL
      icon - icon name (without extension)
      label - accessible text for the icon
      tooltip - text for the title attribute
      nextSort - precomputed next sort key
      id - anchor id, or null for none
    • appendFileTableHead

      private void appendFileTableHead(StringBuilder buf, String base, String directory, int dirSlash, boolean isTopLevel, boolean showSort, boolean showPriority, String sortParam)
      Renders the sortable column headers of the files table: Directory/ Name-type, Size, Download Status (only when priorities are shown) and Download Priority (same condition). Non-sortable state falls back to bare icons with descriptive tooltips. Extracted from getListHTML.
      Parameters:
      buf - target buffer, positioned inside the first <th>
      base - the encoded base URL
      directory - decoded directory display name
      dirSlash - index of '/' in directory (-1 at top level)
      isTopLevel - true in the torrent root
      showSort - true when more than one entry is listed
      showPriority - true when priority controls are shown
      sortParam - current sort key, may be null
    • appendParentDirRow

      private void appendParentDirRow(StringBuilder buf, String base, String sortParam, String decodedBase, boolean isTopLevel, boolean hasAudio, boolean showPriority)
      Renders the dirNav row below the column headers: the parent-directory link (subdirectories only) and the audio-playlist button when complete audio exists at or below this point. The row itself is emitted only when at least one of the two applies. The gate lives in getListHTML. hasCompleteAudio is evaluated once per view and passed in, replacing two recursive walks. Extracted from getListHTML.
      Parameters:
      buf - StringBuilder the markup is appended to
      base - the encoded base URL with trailing slash
      sortParam - current sort key, may be null
      decodedBase - the decoded base URL
      isTopLevel - true in the torrent root (no parent link)
      hasAudio - result of hasCompleteAudio(fileList, ...)
      showPriority - true when the priority column shifts colspans
    • renderCommentsSection

      private void renderCommentsSection(Snark snark, boolean er, boolean ec, boolean esc, String tabLabel, StringBuilder buf)
      Renders the collapsible comments/ratings section (opener div, toggle, tab label, body, close) when ratings or comments are enabled globally; renders nothing otherwise. Shared by both return paths of getListHTML. Previously the main path emitted stray markup outside the section and forced a comment-file load even with the feature disabled.
      Parameters:
      snark - non-null torrent
      er - ratings enabled globally
      ec - comments enabled globally
      esc - comments enabled for this torrent
      tabLabel - translated label for the section tab
      buf - StringBuilder the markup is appended to
    • prettyAnnounce

      static String prettyAnnounce(String announce)
      Rewrite known tracker b64 and b32 announces to their domain names for display. Literal matching only; no per-call regex compilation, and unmatched announces pass through allocation-free.
      Parameters:
      announce - the raw announce URL, non-null
      Returns:
      the rewritten URL
    • appendTorrentInfo

      private void appendTorrentInfo(StringBuilder buf, Snark snark, String base, String tName, boolean showStopStart)
      Appends detailed torrent information as HTML to the provided StringBuilder buffer.

      This includes torrent metadata such as torrent name, size, pieces, trackers, web seeds, completion status, download speed, share ratio, and control buttons. It handles cases where the torrent (snark) or metadata may be null.

      Parameters:
      buf - the StringBuilder to append HTML to
      snark - the Snark object representing the torrent; may be null
      base - the base URL or path used in links
      tName - the torrent display name used in error case
      showStopStart - whether to show start/stop/recheck buttons in torrent info
    • appendPriority

      private void appendPriority(StringBuilder buf, boolean showPriority, boolean complete, boolean isDirectory, int priority, int fileIndex)
      Appends the HTML markup for the file download priority options in torrent file listings. This includes radio buttons for selecting high, normal, or skip priority, conditioned on whether priority display is enabled, the file is not complete, and the file is not a directory.
      Parameters:
      buf - The StringBuilder to append the HTML to.
      showPriority - Flag indicating if priority options should be shown.
      complete - Flag indicating if the file download is complete.
      isDirectory - Flag indicating if the file is a directory.
      priority - The current priority value of the file (<0 is skip, 0 normal, >0 high).
      fileIndex - The identifier index for the file, used to ensure unique input names.
    • buildProgressBar

      private String buildProgressBar(long total, long remaining, boolean includePercent, boolean includeTooltip, boolean noThinsp, boolean formatSize)
      Builds an HTML progress bar with optional percentage text and a tooltip showing the remaining size and completion percentage.
      Parameters:
      total - total size in bytes
      remaining - remaining size in bytes
      includePercent - whether to include the percentage text inside the bar
      includeTooltip - whether to provide tooltip / value on hover
      noThinsp - whether to avoid using thin space
      formatSize - whether to format the size in human-readable format
      Returns:
      String containing the HTML for the progress bar
    • extractTorrentNameAndPath

      private String[] extractTorrentNameAndPath(String title)
      Extracts the torrent name and path-in-torrent from the given title.
      Parameters:
      title - The full path/title string.
      Returns:
      An array where index 0 is tName and index 1 is pathInTorrent.
    • isI2PTracker

      static boolean isI2PTracker(String url, boolean udpEnabled)
      Basic checks only, not as comprehensive as what TrackerClient does. Just to hide non-i2p trackers from the details page.
      Parameters:
      url - the tracker URL to classify
      udpEnabled - whether i2p udp trackers count as acceptable
      Returns:
      whether i2 p tracker
    • isAudio

      private static boolean isAudio(String mime)
      Whether the MIME type is audio.
      Parameters:
      mime - non-null
      Returns:
      true for browser-playable audio, excluding playlists
    • isVideo

      private static boolean isVideo(String mime)
      Whether the MIME type is video.
      Parameters:
      mime - non-null
      Returns:
      true for browser-playable video, excluding the excluded types
    • listFileAndIndex

      private static List<Sorters.FileAndIndex> listFileAndIndex(File dir, Storage storage, long[] remainingArray)
      List the files in a directory and wrap each in a FileAndIndex with the given remaining array.
      Parameters:
      dir - the directory to list
      storage - the storage for the torrent
      remainingArray - the precomputed remaining byte counts
      Returns:
      the wrapped file list, or null if the directory cannot be listed or is empty
    • findCompleteFile

      static String findCompleteFile(List<String> names, long[] remaining, String prefix, java.util.function.Predicate<String> match)
      First entry of names at or under prefix whose remaining byte count is zero and whose name satisfies match, or null. names must be index-aligned with remaining (Storage.getFileNames() paired with Storage.remaining2()). Prefix is '' at the torrent root or a directory path ending with '/' - matching is a plain startsWith across all descendant depths.
      Parameters:
      names - the torrent file names, index-aligned with remaining
      remaining - the remaining byte count of each name
      prefix - '' at the torrent root, else a directory path ending with '/'
      match - caller-supplied name filter, e.g. the audio MIME check
      Returns:
      the first matching name, or null when none qualifies
    • hasCompleteAudio

      private boolean hasCompleteAudio(Storage storage, long[] remainingArray, String prefix)
      Whether any complete audio file exists at or below the browsed directory. Answered from Storage metadata instead of recursively listing directories on every page view.
      Parameters:
      storage - the torrent's storage, or null when the torrent has none
      remainingArray - precomputed remaining byte counts, index-aligned with the storage file names
      prefix - '' at the torrent root, else a directory path ending with '/'
      Returns:
      true when at least one fully downloaded audio file matches the prefix
    • getPlaylist

      private String getPlaylist(String reqURL, String base, String sortParam) throws IOException
      The audio files in the resource list as an m3u playlist. https://en.wikipedia.org/wiki/M3U
      Parameters:
      reqURL - the encoded request URL for the browsed directory, with trailing slash, used as the base for playlist entries
      base - The encoded base URL
      sortParam - may be null
      Returns:
      String of HTML or null if no files or on error
      Throws:
      IOException - if the base URL cannot be percent-decoded
    • getPlaylist

      private void getPlaylist(StringBuilder buf, List<Sorters.FileAndIndex> fileList, String reqURL, int sort, Storage storage, long[] remainingArray)
      Append playlist entries in m3u format to buf. Recursive.
      Parameters:
      buf - StringBuilder the m3u lines are appended to
      fileList - the files and subdirectories of one directory
      reqURL - encoded, WITH trailing slash
      sort - sort key passed to Sorters.getFileComparator for subdirectory listings
      storage - the torrent's storage, for per-file remaining counts
      remainingArray - precomputed remaining byte counts, index-aligned with each directory listing
    • displayComments

      private void displayComments(Snark snark, boolean er, boolean ec, boolean esc, StringBuilder buf)
      Display the ratings and comments section.
      Parameters:
      snark - the torrent whose ratings and comments are rendered
      er - ratings enabled globally
      ec - comments enabled globally
      esc - comments enabled this torrent
      buf - destination for the comment table rows
    • renderCommentsHeader

      private void renderCommentsHeader(I2PSnarkServlet.CommentsContext ctx, StringBuilder buf)
      Renders the commentInfo table header: section title plus the author-name span (the configured name, or a "configure" hint when comments are on for the torrent but no author name is set). Extracted from displayComments.
      Parameters:
      ctx - non-null rendering context
      buf - StringBuilder the markup is appended to
    • readCommentState

      private static I2PSnarkServlet.CommentsHeaderResult readCommentState(CommentSet comments, boolean er, boolean ec)
      Reads the user's rating, community rating statistics, and the comment iterator from a torrent's comment set within a single lock scope, so markup rendering happens outside synchronized blocks. Extracted from displayComments.
      Parameters:
      comments - may be null when none are saved yet
      er - ratings enabled globally
      ec - comments enabled globally
      Returns:
      state snapshot, never null; iter non-null only when comments exist
    • sortQueryString

      private static String sortQueryString(String so)
      Sort query string for torrent file-list links, where the value is a servlet-generated numeric sort key. Distinct from the request-based getQueryString() builders: no request context, and the value is HTML-stripped unconditionally rather than validated numerically.
      Parameters:
      so - sort key, may be null or empty for no sorting
      Returns:
      "?sort=..." or ""
    • toIcon

      private String toIcon(File item)
      Pick an icon; try to catch the common types in an i2p environment.
      Parameters:
      item - the file or directory to choose an icon for
      Returns:
      file name not including ".png"
    • suffixIcon

      static String suffixIcon(String lowerPath)
      Icon name registered for a lower-cased path's file extension.

      A single map lookup replaces a scan of every registered suffix. The two forms agree because no key in IconMaps.SUFFIX_ICON_MAP is a suffix of another, so at most one key can match and it must start at the path's last dot - exactly the substring taken here. A dotless path matches nothing either way, and a dot-leading name such as ".exe" yields itself.

      Parameters:
      lowerPath - the file path, already lower-cased
      Returns:
      the registered icon name, or null when the extension has none
    • toIcon

      String toIcon(String path)
      Returns the icon name representing the file type or mime type of the given file path. Determines custom icons for certain special cases like i2p install executables.
      Parameters:
      path - the file path or file name to analyze
      Returns:
      a string representing the icon name matching the file type
    • appendIcon

      void appendIcon(StringBuilder buf, String name, String alt, String title, boolean fromTheme, boolean isSvg, boolean addDimensions)
      Appends a <img> tag for an icon to the given StringBuilder.
      Parameters:
      buf - the StringBuilder to append to (must not be null)
      name - the icon name without file extension (e.g., "magnet", "folder")
      alt - the alt text (should already be HTML-escaped if needed)
      title - the tooltip title (optional; if empty, no title attribute is added)
      fromTheme - if true, uses the current theme's image path (_imgPath); otherwise uses the WARBASE/icons/ path
      isSvg - if true, uses .svg extension; otherwise uses .png
      addDimensions - if true, hardcodes width=16 height=16 instead of relying on CSS
    • appendIcon

      void appendIcon(StringBuilder buf, String name, String alt, String title, boolean fromTheme, boolean isSvg)
      Overloaded method that defaults addDimensions to false.
      Parameters:
      buf - the StringBuilder to append to (must not be null)
      name - the icon name without file extension (e.g., "magnet", "folder")
      alt - the alt text (should already be HTML-escaped if needed)
      title - the tooltip title (optional; if empty, no title attribute is added)
      fromTheme - if true, uses the current theme's image path (_imgPath); otherwise uses the WARBASE/icons/ path
      isSvg - if true, uses .svg extension; otherwise uses .png
    • toSVGWithDataTooltip

      private String toSVGWithDataTooltip(String icon, String altText, String titleText)
      Icon file (svg) in the .war. Always 16x16. Wrapped in a tooltip span.
      Parameters:
      icon - name without the ".svg"
      altText - non-null
      titleText - non-null (used as data-tooltip)
      Returns:
      the complete tooltip span with the icon image inside
    • savePriorities

      private void savePriorities(Snark snark, Map<String,String[]> postParams)
      Save file priority changes for a torrent.
      Parameters:
      snark - torrent the change applies to
      postParams - the form params
    • saveComments

      private void saveComments(Snark snark, Map<String,String[]> postParams)
      Save comment changes for a torrent.
      Parameters:
      snark - torrent the change applies to
      postParams - the form params
    • deleteComments

      private void deleteComments(Snark snark, Map<String,String[]> postParams)
      Delete comments for a torrent.
      Parameters:
      snark - torrent the change applies to
      postParams - the form params
    • saveCommentsSetting

      private void saveCommentsSetting(Snark snark, Map<String,String[]> postParams)
      Save comment enabled setting for a torrent.
      Parameters:
      snark - torrent the change applies to
      postParams - the form params
    • displayTorrentEdit

      private void displayTorrentEdit(Snark snark, String base, StringBuilder buf)
      Display the edit-torrent section.
      Parameters:
      snark - non-null
      base - the base path
      buf - StringBuilder the edit section is appended to
    • saveTorrentEdit

      private void saveTorrentEdit(Snark snark, Map<String,String[]> postParams)
      Save edits from the torrent edit form.
      Parameters:
      snark - the stopped torrent being edited
      postParams - the edit form parameters, parsed by parseEditParams
    • parseEditParams

      static I2PSnarkServlet.EditParams parseEditParams(Map<String,String[]> postParams)
      Parses the edit form POST parameters into an I2PSnarkServlet.EditParams object. Recognized keys: "addTracker-{id}", "removeTracker-{id}", "primary", "nofilter_newTorrentComment", "nofilter_newTorrentCreatedBy".
      Parameters:
      postParams - the request parameter map (Jetty-style String[] values)
      Returns:
      parsed parameters, never null
    • warnIcon

      private String warnIcon(String alt, String tooltip)
      Warn icon for missing/unrecognized files.
      Parameters:
      alt - the img alt text
      tooltip - the hover title text
      Returns:
      the complete img element markup
    • tickIcon

      private String tickIcon()
      Tick icon for complete files.
      Returns:
      the complete img element markup, localized alt and title
    • priorityIcon

      private String priorityIcon(int priority)
      Priority indicator icon (block/skip, clock/normal, red clock/high).
      Parameters:
      priority - the file priority: negative is skipped, 0 is normal, positive is high priority
      Returns:
      the indicator div with the matching icon inside
    • renderFileRow

      private boolean renderFileRow(StringBuilder buf, I2PSnarkServlet.FileRowContext ctx, Sorters.FileAndIndex fai, boolean rowEven, I2PSnarkServlet.FileRowCounters counters)
      Renders a single file row for the torrent file browser table. Extracted from getListHTML for testability and UI responsiveness.
      Parameters:
      buf - the StringBuilder to append HTML to
      ctx - immutable context (decoded base path, storage, priority flag, top-level flag)
      fai - the file and its metadata from the sorted file list
      rowEven - true for even row (alternating row class)
      counters - mutable accumulators (video/img/text counts, save button flag)
      Returns:
      the next rowEven value (toggled)
    • hasChanges

      static boolean hasChanges(MetaInfo meta, I2PSnarkServlet.EditParams ep)
      Determines whether the edit parameters represent any actual change to the torrent's metadata. Avoids a save cycle when the form is submitted without modifications.
      Parameters:
      meta - current torrent MetaInfo
      ep - parsed edit parameters
      Returns:
      true if any field differs, false if identical
    • buildAnnounceList

      static I2PSnarkServlet.AnnounceListResult buildAnnounceList(MetaInfo meta, I2PSnarkServlet.EditParams ep, List<Tracker> trackers, boolean udpEnabled)
      Builds the new announce list and primary tracker from the current MetaInfo and edit parameters.
      Parameters:
      meta - current torrent MetaInfo
      ep - parsed edit parameters
      trackers - the sorted list of known trackers (for resolving add IDs)
      udpEnabled - whether UDP trackers are enabled in config
      Returns:
      new announce list (may be null) and primary tracker URL
    • noCollapsePanels

      static boolean noCollapsePanels(HttpServletRequest req)
      Whether the user agent cannot handle collapsible panels.
      Parameters:
      req - request whose user-agent header is inspected
      Returns:
      true if panels should not be collapsed
    • isParentOf

      private static boolean isParentOf(File a, File b)
      Whether "a" equals "b", or "a" is a directory and a parent of file or directory "b", canonically speaking.
      Parameters:
      a - the parent directory candidate
      b - the file or directory to check
      Returns:
      true if a contains b or they are the same
    • checkNestedTorrent

      private boolean checkNestedTorrent(File dataDir, boolean checkContained)
      Reject a torrent whose data directory lies inside another torrent's data directory, or (when checkContained) would contain another torrent's data directory. Adds a message explaining the rejection.
      Parameters:
      dataDir - the data directory for the torrent being added or created
      checkContained - also reject when it would contain another torrent's data
      Returns:
      true to reject
    • isStandalone

      boolean isStandalone()
      Whether we are running in standalone mode.
      Returns:
      true if there is no router context to connect through