java.lang.Object
i2p.susi.webmail.PersistentMailCache

class PersistentMailCacheextends Object
Manages persistent on-disk email cache with custom compressed format. This is a custom format with subdirectories, gzipped files, and the encoded UIDL in the file name. We store either the headers or the full message. No, it is not Maildir format but we could add Maildir-style status suffixes (e.g. ":2.SR") later. Exporting to a Maildir format would be just ungzipping each file to a flat directory. This class should only be accessed from MailCache.
  • Field Details

    • _locks

      private static final ConcurrentHashMap<String,Object> _locks
      One lock for each user in the whole JVM, to protect against multiple sessions. One big lock for the whole cache dir, not one for each file or subdir. Never expired. Sure, if we did a maildir format we wouldn't need this.
    • _lock

      private final Object _lock
      Lock for the cache state.
    • _cacheDir

      private final File _cacheDir
      Directory holding the cached mail.
    • _attachmentDir

      private final File _attachmentDir
      Directory for the drafts' attachments.
    • _context

      private final I2PAppContext _context
      The app context.
    • _log

      private final Log _log
      Logger for the mail cache.
    • _isDrafts

      private final boolean _isDrafts
      True for the drafts folder cache.
    • DIR_SUSI

      private static final String DIR_SUSI
      Name of the mail data directory.
      See Also:
    • DIR_CACHE

      private static final String DIR_CACHE
      Name of the cache subdirectory.
      See Also:
    • CACHE_PREFIX

      private static final String CACHE_PREFIX
      Prefix of the cache file names.
      See Also:
    • DIR_IMPORT

      public static final String DIR_IMPORT
      Directory beside the cache holding imported .eml files, debug only.
      See Also:
    • DIR_ATTACHMENTS

      public static final String DIR_ATTACHMENTS
      Directory holding draft attachment files, created only by the drafts cache.
      See Also:
    • DIR_PREFIX

      private static final String DIR_PREFIX
      Prefix of the per-user directory names.
      See Also:
    • FILE_PREFIX

      private static final String FILE_PREFIX
      Prefix of the mail file names.
      See Also:
    • HDR_SUFFIX

      private static final String HDR_SUFFIX
      Suffix of the header-only cache files.
      See Also:
    • FULL_SUFFIX

      private static final String FULL_SUFFIX
      Suffix of the full-message cache files.
      See Also:
    • B64

      private static final String B64
      Alphabet of the base64 mail uids.
      See Also:
  • Constructor Details

    • PersistentMailCache

      public PersistentMailCache(I2PAppContext ctx, String host, int port, String user, String pass, String folder) throws IOException
      Use the params to generate a unique directory name. Does NOT load the mails in. Caller MUST call getMails(String).
      Parameters:
      ctx - supplies the log manager and the base directory
      host - IMAP host, part of the directory name
      port - IMAP port, part of the directory name
      user - mail account name, part of the directory name
      pass - not used; the cache is keyed on account name alone
      folder - on-disk maildir name, one of the DIRS values, e.g. DIR_FOLDER; only DIR_DRAFTS gets an attachment directory
      Throws:
      IOException - if the cache directory cannot be created
  • Method Details

    • getMails

      public Collection<Mail> getMails(String folder)
      Fetch all mails from disk, naming the folder in the completion log so a reader can tell which mailbox the counts belong to.
      Parameters:
      folder - label for the log, null if the caller has none to give
      Returns:
      a new collection
    • locked_getMails

      private Collection<Mail> locked_getMails(String folder)
      Lists the cached mails, lock held.
      Parameters:
      folder - the folder to list
      Returns:
      the mails found in the cache
    • getMail

      public boolean getMail(Mail mail, boolean headerOnly)
      Fetch any needed data from disk, filling in the header, or the body if that is what was asked for and only a header file is on disk.
      Parameters:
      mail - the mail to populate, matched on its uidl
      headerOnly - true to load just the header, false to load the body too
      Returns:
      true if the mail was populated; false if no file exists for the uidl or it could not be read
    • locked_getMail

      private boolean locked_getMail(Mail mail, boolean headerOnly)
      Loads one cached mail, lock held.
      Parameters:
      mail - the mail to load
      headerOnly - true to load the headers only
      Returns:
      true if the cache had the mail
    • saveMail

      public boolean saveMail(Mail mail)
      Write the mail to disk as a full message when it has a body, otherwise as a header-only file. An existing file is left alone.
      Parameters:
      mail - the mail to save
      Returns:
      true if the file is on disk, either just written or already present; false if the mail carries neither body nor header, or the write failed
    • locked_saveMail

      private boolean locked_saveMail(Mail mail)
      Writes one mail to the cache, lock held.
      Parameters:
      mail - the mail to save
      Returns:
      true if the mail was written
    • deleteMail

      public void deleteMail(Mail mail)
      Delete both the full-message and the header-only file for this mail.
      Parameters:
      mail - the mail to remove
    • deleteMail

      public void deleteMail(String uidl)
      Delete both the full-message and the header-only file for this uidl.
      Parameters:
      uidl - the unique ID of the mail to remove
    • getLock

      private static Object getLock(String host, int port, String user)
      Returns the lock shared by one account.
      Parameters:
      host - the mail host
      port - the mail port
      user - the user name
      Returns:
      the lock for that account
    • makeCacheDirs

      private File makeCacheDirs(String host, int port, String user, String folder) throws IOException
      ~/.i2p/susimail/cache/cache-xxxxx/cur/s[b64char]/mail-xxxxx.full.txt.gz folder1 is the base.
      Parameters:
      host - the mail server host, part of the cache key
      port - the mail server port, part of the cache key
      user - the user name, part of the cache key
      folder - the folder base name, e.g. cur
      Returns:
      the base folder directory, with one subdirectory per base64 character
      Throws:
      IOException - if any of the directories cannot be created
    • getHeaderFile

      public File getHeaderFile(String uidl)
      The gzipped header-only file for this uidl. The file need not exist yet.
      Parameters:
      uidl - the unique ID of the mail
      Returns:
      the header-only file for this UIDL
    • getFullFile

      public File getFullFile(String uidl)
      The gzipped full-message file for this uidl. The file need not exist yet.
      Parameters:
      uidl - the unique ID of the mail
      Returns:
      the full message file for this UIDL
    • getFullBuffer

      public GzipFileBuffer getFullBuffer(String uidl)
      For reading or writing a new full mail (NOT headers only). For writing, caller MUST call writeComplete() on rv. Does not necessarily exist.
      Parameters:
      uidl - the unique ID of the mail
      Returns:
      a new buffer over the full message file
    • getAttachmentDir

      public File getAttachmentDir()
      Where draft attachments are stored, which only the drafts cache creates.
      Returns:
      non-null only for Drafts
    • getFile

      private File getFile(String uidl, String suffix)
      Builds the cache file name for a uid.
      Parameters:
      uidl - the mail uid
      suffix - the file suffix to use
      Returns:
      the file to use
    • write

      private boolean write(Buffer rb, File f)
      Save data to disk.
      Parameters:
      rb - the in-memory buffer whose data is written to disk
      f - the gzip cache file to write
      Returns:
      true if the buffer was written in full; false on an IOException, which also leaves the buffer marked incomplete
    • read

      private static Buffer read(File f)
      Wrap a cache file in a decompressing buffer.
      Parameters:
      f - the gzip cache file to read
      Returns:
      null on failure
    • load

      private static Mail load(File f, boolean isDrafts)
      This is for the initial load only. Others will use getMail(Mail, boolean).
      Parameters:
      f - the cache file to parse, in full or header form
      isDrafts - true if the file is in the drafts folder
      Returns:
      null on failure
    • importMail

      private void importMail()
      For debugging. Import .eml files from the import/ directory.