java.lang.Object
i2p.susi.webmail.pop3.POP3MailBox
All Implemented Interfaces:
NewMailListener

public class POP3MailBoxextends Object implements NewMailListener
POP3 client for retrieving email from POP3 servers over I2P. Supports standard POP3 commands, background checking, and delayed deletion. Optimized for high-latency I2P connections with pipelining and idle timeout management.
  • Field Details

    • host

      private final String host
      The mail server host.
    • user

      private final String user
      The login user name.
    • pass

      private final String pass
      The login password.
    • _log

      private final Log _log
      Logger for the mailbox.
    • lastLine

      private String lastLine
      Last line received from the server.
    • lastError

      private String lastError
      Last error line received, null if none.
    • port

      private final int port
      The mail server port.
    • mails

      private int mails
      Messages the server reported.
    • connected

      private boolean connected
      True while connected to the server.
    • gotCAPA

      private boolean gotCAPA
      True once the server capabilities are known.
    • supportsPipelining

      private boolean supportsPipelining
      True if the server supports pipelining.
    • supportsTOP

      private boolean supportsTOP
      True if the server supports the TOP command.
    • supportsUIDL

      private boolean supportsUIDL
      True if the server supports the UIDL command.
    • sizes

      private final HashMap<Integer,Integer> sizes
      ID to size.
    • uidlToID

      private final HashMap<String,Integer> uidlToID
      UIDL to ID.
    • socket

      private Socket socket
      The connection to the server, null while closed.
    • lastActive

      private final AtomicLong lastActive
      When a webmail request last used the mailbox.
    • lastChecked

      private final AtomicLong lastChecked
      When the mailbox was last checked for new mail.
    • synchronizer

      private final Object synchronizer
      Lock for the mailbox operations.
    • delayedDeleter

      private final DelayedDeleter delayedDeleter
      Deletes the mailbox when it goes idle.
    • backgroundChecker

      private BackgroundChecker backgroundChecker
      Checker polling for new mail in the background.
    • idleCloser

      private IdleCloser idleCloser
      Timer closing the connection when idle.
    • newMailListener

      private volatile NewMailListener newMailListener
      Listener notified of new mail, may be null.
  • Constructor Details

    • POP3MailBox

      public POP3MailBox(String host, int port, String user, String pass)
      Does not connect. Caller must call connectToServer(NewMailListener) if desired.
      Parameters:
      host - hostname of the POP3 server to connect to
      port - TCP port of the POP3 server
      user - account name to log in with
      pass - password for the account, sent in plaintext unless TLS is used
  • Method Details

    • getHeader

      public Buffer getHeader(String uidl)
      Fetch the header. Does not cache.
      Parameters:
      uidl - the server-side unique message id to retrieve the header of
      Returns:
      Byte buffer containing header data or null
    • getHeader

      private Buffer getHeader(int id)
      Retrieves header from pop3 server (with TOP command and RETR as fallback) Caller must sync.
      Parameters:
      id - the 1-based message number from 1 to the mailbox size; out of range sets lastError
      Returns:
      Byte buffer containing header data or null
    • getBody

      public Buffer getBody(String uidl, Buffer buffer)
      Fetch the body. Does not cache.
      Parameters:
      uidl - the server-side unique message id to retrieve the body of
      buffer - the buffer the body data is read into
      Returns:
      the buffer containing body data or null
    • getBodies

      public void getBodies(Collection<POP3MailBox.FetchRequest> requests)
      Fetch headers and/or bodies. Does not cache. ReadBuffer objects are inserted into the requests. No total time limit.
      Parameters:
      requests - one FetchRequest per message, each holding the ReadBuffer to fill
    • getBody

      private Buffer getBody(int id, Buffer buffer)
      Retrieve message body from pop3 server (via RETR command) Caller must sync.
      Parameters:
      id - the 1-based message number from 1 to the mailbox size; out of range sets lastError
      buffer - where the retrieved body lines are written, left untouched when the id is out of range
      Returns:
      the buffer containing body data or null
    • queueForDeletion

      public void queueForDeletion(Collection<String> uidls)
      Queue for later deletion. Non-blocking.
      Parameters:
      uidls - the server-side unique ids of the mails to queue
    • queueForDeletion

      public void queueForDeletion(String uidl)
      Queue for later deletion. Non-blocking.
      Parameters:
      uidl - the server-side unique id of the mail to queue
    • deletePending

      void deletePending(boolean noWait)
      Delete all pending deletions at once. If previously connected, leaves connected. If not previously connected, closes connection when done.
      Parameters:
      noWait - fire-and-forget mode, only if connected
    • getSize

      public int getSize(String uidl)
      Get cached size of a message (via previous LIST command).
      Parameters:
      uidl - the server-side unique message id whose cached size is wanted
      Returns:
      Message size in bytes or 0 if not found
    • getSize

      private int getSize(int id)
      Get cached size of a message (via previous LIST command). Caller must sync.
      Parameters:
      id - the 1-based message number whose LIST size is wanted
      Returns:
      Message size in bytes or 0 if not found
    • isConnected

      boolean isConnected()
      Is the connection is still alive.
      Returns:
      true or false
    • checkConnection

      private void checkConnection() throws IOException
      If not connected, connect now. Should be called from all public methods before sending a command. Caller must sync.
      Throws:
      IOException - if a fresh connection cannot be opened either
    • updateActivity

      private void updateActivity()
      Records the current time as the last activity.
    • getLastActivity

      long getLastActivity()
      Timestamp of the last activity on the POP3 connection.
      Returns:
      milliseconds since the epoch
    • getLastChecked

      long getLastChecked()
      Timestamp. When we last successfully got the UIDL list.
      Returns:
      milliseconds since the epoch
    • updateMailCount

      private void updateMailCount(String response)
      Count the messages the server reports in the scan listing.
      Parameters:
      response - line starting with +OK
    • mailCountText

      static String mailCountText(int mails, String host, int port)
      Names the server the count came from, so a reader can tell which mailbox is being reported without parsing the sentence.
      Parameters:
      mails - message count the server reported in its STAT response
      host - hostname of the POP3 server
      port - TCP port of the POP3 server
      Returns:
      the log message
    • loggedInText

      static String loggedInText(String host, int port)
      Names the server the login succeeded against.
      Parameters:
      host - hostname of the POP3 server
      port - TCP port of the POP3 server
      Returns:
      the log message
    • capsText

      static String capsText(boolean pipelining, boolean uidl, boolean top)
      Reports what the server's CAPA (capability) response allowed, so a reader can tell why a listing used TOP or fell back to RETR. Each capability is spelled out in prose with its wire name beside it, and answered yes or no rather than with a question mark, which reads like an unanswered question.
      Parameters:
      pipelining - whether the server accepts pipelined commands
      uidl - whether the server supports the UIDL command
      top - whether the server supports the TOP command
      Returns:
      the multi-line log message
    • updateUIDLs

      private void updateUIDLs(List<String> lines)
      Caller must sync.
      Parameters:
      lines - the UIDL response, one "message number uidl" pair per line, null when the server returned no list
    • updateSizes

      private void updateSizes(List<String> lines)
      Caller must sync.
      Parameters:
      lines - the LIST response, one "message number octets" pair per line, null when the server returned no list
    • clear

      private void clear()
      Caller must sync.
    • connectToServer

      public boolean connectToServer(NewMailListener nml)
      Connect to pop3 server if not connected. Checks mail if already connected. Non-Blocking unless an action already in progress. This will NOT call any configured NewMailListener, only the one passed in. It will be called with the value true if the connect was successful, false if not. Call getNumMails() to see if there really was any new mail. After the callback is executed, the information on new mails, if any, is available via getNumMails(), getUIDLs(), and getSize(). The connection to the server will remain open, so that new emails may be retrieved via getHeader(String), getBody(), and getBodies(). Failure info is available via lastError().
      Parameters:
      nml - notified once the listing is retrieved, or never on a connect or scan failure
      Returns:
      true if nml will be called back, false on failure and nml will NOT be called back
    • blockingConnectToServer

      boolean blockingConnectToServer()
      Connect to pop3 server if not connected. Does nothing if already connected. Blocking. This will NOT call any configured NewMailListener. After the callback is executed, the information on new mails, if any, is available via getNumMails(), getUIDLs(), and getSize(String). The connection to the server will remain open, so that new emails may be retrieved via getHeader(), getBody(String, Buffer), and getBodies(). Failure info is available via lastError().
      Returns:
      true if connected
    • connect

      private void connect()
      Closes any existing connection first. Then, connect to pop3 server, login with USER and PASS and try STAT then. Caller must sync.
    • doHandshake

      private boolean doHandshake() throws IOException
      Check the initial response, send CAPA, check the CAPA result Caller must sync.
      Returns:
      true if successful
      Throws:
      IOException - if a handshake command cannot be written or answered
    • doCheckMail

      private boolean doCheckMail() throws IOException
      Send STAT, UIDL, LIST, and DELE for all pending. Must be connected. Caller must sync. Leaves socket connected. Caller must close on IOE.
      Returns:
      true if every command was sent and received an OK reply
      Throws:
      IOException - if not connected, or a command cannot be written or answered
    • doDelete

      private void doDelete(boolean noWait) throws IOException
      Send DELE for all pending deletions. Must be connected. Caller must sync. Leaves socket connected. Caller must close on IOE.
      Parameters:
      noWait - true to send each DELE and drop it from the pending set without waiting for a reply; false to batch them and wait, pruning only if the connection survives
      Throws:
      IOException - if a DELE command cannot be written to the server
    • sendCmd1a

      private boolean sendCmd1a(String cmd) throws IOException
      Send command to pop3 server (and expect single line answer) Response will be in lastLine. Does not read past the first line of the response. Caller must sync.
      Parameters:
      cmd - command to send
      Returns:
      true if command was successful (+OK)
      Throws:
      IOException - if the server closes the connection without answering
    • sendCmds

      private boolean sendCmds(List<POP3MailBox.SendRecv> cmds) throws IOException
      Send commands to pop3 server all at once (and expect answers). Sets lastError to the FIRST error. Caller must sync.
      Parameters:
      cmds - command to send
      Returns:
      true if ALL received lines were successful (+OK)
      Throws:
      IOException - if a command cannot be written, or the server sends no reply line
    • sendCmd1aNoWait

      private void sendCmd1aNoWait(String cmd) throws IOException
      Send command to pop3 server. Does NOT flush or read or wait. Caller must sync.
      Parameters:
      cmd - command to send non-null
      Throws:
      IOException - if the command cannot be written to the socket
    • sendCmdN

      private Buffer sendCmdN(String cmd, Buffer buffer)
      Tries twice Caller must sync.
      Parameters:
      cmd - the command to send, such as "RETR 3"
      buffer - where the multi-line result is written
      Returns:
      the buffer or null
    • sendCmdNa

      private Buffer sendCmdNa(String cmd, Buffer buffer) throws IOException
      No total timeout (result could be large) Caller must sync.
      Parameters:
      cmd - the command to send, such as "RETR 3"
      buffer - where the multi-line result is written
      Returns:
      the buffer or null
      Throws:
      IOException - if the server sends no reply, or the result cannot be read
    • sendCmdNl

      private List<String> sendCmdNl(String cmd) throws IOException
      Like sendCmdNa but returns a list of strings, one per line. Strings will have trailing \r but not \n. Total timeout 2 minutes. Caller must sync.
      Parameters:
      cmd - the command to send, such as "LIST"
      Returns:
      the lines or null on error
      Throws:
      IOException - if the server goes 2 minutes without sending the terminating dot
    • getResultNa

      private void getResultNa(Buffer buffer) throws IOException
      No total timeout (result could be large) Caller must sync.
      Parameters:
      buffer - non-null
      Throws:
      IOException - if a line cannot be read from the server or the buffer write fails
    • getResultNl

      private List<String> getResultNl() throws IOException
      Like getResultNa but returns a list of strings, one per line. Strings will have trailing \r but not \n. Total timeout 2 minutes. Caller must sync.
      Returns:
      the lines non-null
      Throws:
      IOException - if the server goes 2 minutes without sending the terminating dot
    • getNumMails

      public int getNumMails()
      Warning - forces a connection.
      Returns:
      The amount of e-mails available.
    • lastError

      public String lastError()
      The last error the server or the client reported, stripped of its "-ERR" prefix.
      Returns:
      The most recent error message. Probably not terminated with a newline.
    • setNewMailListener

      public void setNewMailListener(NewMailListener nml)
      Relay from the checker to the webmail session object, which relays to MailCache, which will fetch the mail from us in a big circle.
      Parameters:
      nml - the session-side listener that new-mail notifications are relayed to
    • foundNewMail

      public void foundNewMail(boolean yes)
      Relay from the checker to the webmail session object, which relays to MailCache, which will fetch the mail from us in a big circle.
      Specified by:
      foundNewMail in interface NewMailListener
      Parameters:
      yes - true if new mail was found
    • destroy

      public void destroy()
      Close without waiting for response, and remove any delayed tasks and resources.
    • getLock

      Object getLock()
      For helper threads to lock.
      Returns:
      the monitor serializing all command and connection activity
    • hasQueuedDeletions

      boolean hasQueuedDeletions()
      Do we have UIDLs to delete?
      Returns:
      true if at least one UIDL is waiting to be deleted from the server
    • close

      public void close()
      Close without waiting for response. Deletes all queued deletions.
    • close

      void close(boolean shouldWait)
      Close and optionally wait for response. Deletes all queued deletions.
      Parameters:
      shouldWait - true to send QUIT and block until the server acknowledges
    • getIDfromUIDL

      private int getIDfromUIDL(String uidl)
      Returns number of message with given UIDL Caller must sync.
      Parameters:
      uidl - the server-side unique message id to map to a message number
      Returns:
      Message number or -1
    • getUIDLs

      public Collection<String> getUIDLs()
      Only if connected. Does not force a connect. If not connected, returns null.
      Returns:
      A new array of the available UIDLs. No particular order.
    • maskCommand

      static String maskCommand(String cmd)
      The command as it may be written to the log. The login credentials are dropped so that neither the account name nor the password reaches the log files, which are readable through the web console.
      Parameters:
      cmd - the POP3 command to be logged, non-null
      Returns:
      the command with any credential argument replaced
    • _t

      private static String _t(String s)
      Looks up a localized string in the message bundle.
      Parameters:
      s - the English text used as the lookup key
      Returns:
      the translation for the current language, or s itself when untranslated