Changeset 012fb4ca


Ignore:
Timestamp:
Feb 12, 2018 2:26:19 PM (3 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
2999638
Parents:
f13f4fc
Message:

SusiMail?: Thread the cache loading and email checking (ticket #2087)
Set Cache-Control header for attachments
Fix rotated attached images
Fix excess debug info in message view

Files:
12 edited

Legend:

Unmodified
Added
Removed
  • apps/susimail/src/src/i2p/susi/webmail/MailCache.java

    rf13f4fc r012fb4ca  
    4444
    4545import net.i2p.I2PAppContext;
     46import net.i2p.util.I2PAppThread;
    4647
    4748/**
     
    5354                HEADER, ALL, CACHE_ONLY
    5455        }
    55        
     56
    5657        private final POP3MailBox mailbox;
    5758        private final Hashtable<String, Mail> mails;
    5859        private final PersistentMailCache disk;
    5960        private final I2PAppContext _context;
     61        private NewMailListener _loadInProgress;
    6062       
    6163        /** Includes header, headers are generally 1KB to 1.5 KB,
     
    6466        private static final int FETCH_ALL_SIZE = 32*1024;
    6567
    66         /**
     68
     69        /**
     70         * Does NOT load the mails in. Caller MUST call loadFromDisk().
     71         *
    6772         * @param mailbox non-null
    6873         */
    6974        MailCache(I2PAppContext ctx, POP3MailBox mailbox,
    70                   String host, int port, String user, String pass) {
     75                  String host, int port, String user, String pass) throws IOException {
    7176                this.mailbox = mailbox;
    7277                mails = new Hashtable<String, Mail>();
    73                 PersistentMailCache pmc = null;
     78                disk = new PersistentMailCache(ctx, host, port, user, pass, PersistentMailCache.DIR_FOLDER);
     79                // TODO Drafts, Sent, Trash
     80                _context = ctx;
     81        }
     82
     83        /**
     84         * Threaded. Returns immediately.
     85         * This will not access the mailbox. Mailbox need not be ready.
     86         *
     87         * @return success false if in progress already and nml will NOT be called back, true if nml will be called back
     88         * @since 0.9.13
     89         */
     90        public synchronized boolean loadFromDisk(NewMailListener nml) {
     91                if (_loadInProgress != null)
     92                        return false;
     93                Thread t = new I2PAppThread(new LoadMailRunner(nml), "Email loader");
     94                _loadInProgress = nml;
    7495                try {
    75                         pmc = new PersistentMailCache(ctx, host, port, user, pass, PersistentMailCache.DIR_FOLDER);
    76                         // TODO Drafts, Sent, Trash
    77                 } catch (IOException ioe) {
    78                         Debug.debug(Debug.ERROR, "Error creating disk cache: " + ioe);
    79                 }
    80                 disk = pmc;
    81                 _context = ctx;
    82                 if (disk != null)
    83                         loadFromDisk();
    84         }
    85 
    86         /**
    87          *
    88          * @since 0.9.13
    89          */
    90         private void loadFromDisk() {
    91                 Collection<Mail> dmails = disk.getMails();
    92                 for (Mail mail : dmails) {
    93                         mails.put(mail.uidl, mail);
     96                        t.start();
     97                } catch (Throwable e) {
     98                        _loadInProgress = null;
     99                        return false;
     100                }
     101                return true;
     102        }
     103
     104        /** @since 0.9.34 */
     105        private class LoadMailRunner implements Runnable {
     106                private final NewMailListener _nml;
     107
     108                public LoadMailRunner(NewMailListener nml) {
     109                        _nml = nml;
     110                }
     111
     112                public void run() {
     113                        boolean result = false;
     114                        try {
     115                                blockingLoadFromDisk();
     116                                if(!mails.isEmpty())
     117                                        result = true;
     118                        } finally {
     119                                synchronized(MailCache.this) {
     120                                        if (_loadInProgress == _nml)
     121                                                _loadInProgress = null;
     122                                }
     123                                _nml.foundNewMail(result);
     124                        }
     125                }
     126        }
     127
     128        /**
     129         * Blocking. Only call once!
     130         * This will not access the mailbox. Mailbox need not be ready.
     131         *
     132         * @since 0.9.13
     133         */
     134        private void blockingLoadFromDisk() {
     135                synchronized(mails) {
     136                        if (!mails.isEmpty())
     137                                throw new IllegalStateException();
     138                        Collection<Mail> dmails = disk.getMails();
     139                        for (Mail mail : dmails) {
     140                                mails.put(mail.uidl, mail);
     141                        }
    94142                }
    95143        }
     
    98146         * The ones known locally, which will include any known on the server, if connected.
    99147         * Will not include any marked for deletion.
     148         *
     149         * This will not access the mailbox. Mailbox need not be ready.
     150         * loadFromDisk() must have been called first.
    100151         *
    101152         * @return non-null
     
    114165
    115166        /**
    116          * Fetch any needed data from pop3 server.
     167         * Fetch any needed data from pop3 server, unless mode is CACHE_ONLY.
     168         * Blocking unless mode is CACHE_ONLY.
    117169         *
    118170         * @param uidl message id to get
     
    173225         * MUST already be connected, otherwise returns false.
    174226         *
     227         * Blocking.
     228         *
    175229         * @param mode HEADER or ALL only
    176230         * @return true if any were fetched
  • apps/susimail/src/src/i2p/susi/webmail/NewMailListener.java

    rf13f4fc r012fb4ca  
    88public interface NewMailListener {
    99   
    10         public void foundNewMail();
     10        public void foundNewMail(boolean yes);
    1111
    1212}
  • apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java

    rf13f4fc r012fb4ca  
    8787        /**
    8888         *  Use the params to generate a unique directory name.
     89         *
     90         *  Does NOT load the mails in. Caller MUST call getMails().
     91         *
    8992         *  @param pass ignored
    9093         *  @param folder use DIR_FOLDER
  • apps/susimail/src/src/i2p/susi/webmail/WebMail.java

    rf13f4fc r012fb4ca  
    8484import net.i2p.servlet.util.ServletUtil;
    8585import net.i2p.servlet.util.WriterOutputStream;
     86import net.i2p.util.I2PAppThread;
    8687import net.i2p.util.SecureFileOutputStream;
    8788import net.i2p.util.Translate;
     
    108109        private static final int DEFAULT_SMTPPORT = 7659;
    109110       
    110         private enum State { AUTH, LIST, SHOW, NEW, CONFIG }
     111        private enum State { AUTH, LOADING, LIST, SHOW, NEW, CONFIG }
    111112       
    112113        // TODO generate from servlet name to allow for renaming or multiple instances
     
    251252
    252253        /**
    253          * sorts Mail objects by id field
    254          *
    255          * @author susi
    256          */
    257 /****
    258         private static class IDSorter implements Comparator<String> {
    259                 private final MailCache mailCache;
    260                
    261                 public IDSorter( MailCache mailCache )
    262                 {
    263                         this.mailCache = mailCache;
    264                 }
    265                
    266                 public int compare(String arg0, String arg1) {
    267                         Mail a = mailCache.getMail( arg0, MailCache.FETCH_HEADER );
    268                         Mail b = mailCache.getMail( arg1, MailCache.FETCH_HEADER );
    269                         if (a == null)
    270                                 return (b == null) ? 0 : 1;
    271                         if (b == null)
    272                                 return -1;
    273                         return a.id - b.id;
    274                 }               
    275         }
    276 ****/
    277 
    278         /**
    279254         * Base for the various sorters
    280255         *
     
    443418                MailCache mailCache;
    444419                Folder<String> folder;
     420                boolean isLoading, isFetching;
     421                /** Set by threaded connector. Error or null */
     422                String connectError;
     423                /** Set by threaded connector. -1 if nothing to report, 0 or more after fetch complete */
     424                int newMails = -1;
    445425                String user, pass, host, error, info;
    446426                String replyTo, replyCC;
     
    485465                 *  @since 0.9.13
    486466                 */
    487                 public void foundNewMail() {
     467                public void foundNewMail(boolean yes) {
     468                        if (!yes)
     469                                return;
    488470                        MailCache mc = mailCache;
    489471                        Folder<String> f = folder;
     
    797779         * prepare line for presentation between html tags
    798780         *
    799          * - quote html tags
     781         * Escapes html tags
    800782         *
    801          * @param line, null OK
     783         * @param line null OK
    802784         * @return escaped string or "" for null input
    803785         */
     
    892874                                }
    893875                                if( doContinue ) {
    894                                         POP3MailBox mailbox = new POP3MailBox( host, pop3PortNo, user, pass );
    895                                         if (offline || mailbox.connectToServer()) {
    896                                                 sessionObject.mailbox = mailbox;
    897                                                 sessionObject.user = user;
    898                                                 sessionObject.pass = pass;
    899                                                 sessionObject.host = host;
    900                                                 sessionObject.smtpPort = smtpPortNo;
    901                                                 state = State.LIST;
    902                                                 I2PAppContext ctx = I2PAppContext.getGlobalContext();
    903                                                 MailCache mc = new MailCache(ctx, mailbox, host, pop3PortNo, user, pass);
    904                                                 sessionObject.mailCache = mc;
    905                                                 sessionObject.folder = new Folder<String>();
    906                                                 if (!offline) {
    907                                                         // prime the cache, request all headers at once
    908                                                         // otherwise they are pulled one at a time
    909                                                         // during the sort in folder.setElements() below
    910                                                         mc.getMail(MailCache.FetchMode.HEADER);
     876                                        sessionObject.smtpPort = smtpPortNo;
     877                                        state = threadedStartup(sessionObject, offline, state,
     878                                                                host, pop3PortNo, user, pass);
     879                                }
     880                        }
     881                }
     882                return state;
     883        }
     884
     885        /**
     886         * Starts one thread to load the emails from disk,
     887         * and in parallel starts a second thread to connect to the POP3 server
     888         * (unless user clicked the 'read mail offline' at login).
     889         * Either could finish first, but unless the local disk cache is really big,
     890         * the loading will probably finish first.
     891         *
     892         * Once the POP3 connects, it waits for the disk loader to finish, and then
     893         * does the fetching of new emails.
     894         *
     895         * The user may view the local folder once the first (loader) thread is done.
     896         *
     897         * @since 0.9.34
     898         */
     899        private static State threadedStartup(SessionObject sessionObject, boolean offline, State state,
     900                                             String host, int pop3PortNo, String user, String pass) {
     901                POP3MailBox mailbox = new POP3MailBox(host, pop3PortNo, user, pass);
     902                I2PAppContext ctx = I2PAppContext.getGlobalContext();
     903                MailCache mc;
     904                try {
     905                        mc = new MailCache(ctx, mailbox, host, pop3PortNo, user, pass);
     906                } catch (IOException ioe) {
     907                        Debug.debug(Debug.ERROR, "Error creating disk cache", ioe);
     908                        sessionObject.error += ioe.toString() + '\n';
     909                        return State.AUTH;
     910                }
     911                Folder<String> folder = new Folder<String>();   
     912                // setElements() sorts, so configure the sorting first
     913                //sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) );
     914                folder.addSorter( SORT_SENDER, new SenderSorter(mc));
     915                folder.addSorter( SORT_SUBJECT, new SubjectSorter(mc));
     916                folder.addSorter( SORT_DATE, new DateSorter(mc));
     917                folder.addSorter( SORT_SIZE, new SizeSorter(mc));
     918                // reverse sort, latest mail first
     919                // TODO get user defaults from config
     920                folder.setSortBy(SORT_DEFAULT, SORT_ORDER_DEFAULT);
     921                sessionObject.folder = folder;
     922
     923                sessionObject.mailbox = mailbox;
     924                sessionObject.user = user;
     925                sessionObject.pass = pass;
     926                sessionObject.host = host;
     927                sessionObject.reallyDelete = false;
     928
     929                // Thread the loading and the server connection.
     930                // Either could finish first.
     931
     932                // With a mix of email (10KB median, 100KB average size),
     933                // about 20 emails per second per thread loaded.
     934                // thread 1: mc.loadFromDisk()
     935                sessionObject.mailCache = mc;
     936                sessionObject.isLoading = true;
     937                boolean ok = mc.loadFromDisk(new LoadWaiter(sessionObject));
     938                if (!ok)
     939                        sessionObject.isLoading = false;
     940
     941                // thread 2: mailbox.connectToServer()
     942                if (offline) {
     943                        Debug.debug(Debug.DEBUG, "OFFLINE MODE");
     944                } else {
     945                        sessionObject.isFetching = true;
     946                        if (mailbox.connectToServer(new ConnectWaiter(sessionObject)))
     947                                sessionObject.isFetching = false;
     948                }
     949
     950                // wait a little while so we avoid the loading page if we can
     951                if (sessionObject.isLoading) {
     952                        try {
     953                                sessionObject.wait(5000);
     954                        } catch (InterruptedException ie) {
     955                                Debug.debug(Debug.DEBUG, "Interrupted waiting for load", ie);
     956                        }
     957                }
     958                state = sessionObject.isLoading ? State.LOADING : State.LIST;
     959                return state;
     960        }
     961
     962        /**
     963         *  Callback from MailCache.loadFromDisk()
     964         *  @since 0.9.34
     965         */
     966        private static class LoadWaiter implements NewMailListener {
     967                private final SessionObject _so;
     968
     969                public LoadWaiter(SessionObject so) {
     970                        _so = so;
     971                }
     972
     973                public void foundNewMail(boolean yes) {
     974                        synchronized(_so) {
     975                                // get through cache so we have the disk-only ones too
     976                                MailCache mc = _so.mailCache;
     977                                Folder<String> f = _so.folder;
     978                                if (mc != null && f != null) {
     979                                        String[] uidls = mc.getUIDLs();
     980                                        int added = f.addElements(Arrays.asList(uidls));
     981                                        if (added > 0)
     982                                                _so.pageChanged = true;
     983                                        Debug.debug(Debug.DEBUG, "Folder loaded");
     984                                } else {
     985                                        Debug.debug(Debug.DEBUG, "MailCache/folder vanished?");
     986                                }
     987                                _so.isLoading = false;
     988                                _so.notifyAll();
     989                        }
     990                }
     991        }
     992
     993        /**
     994         *  Callback from POP3MailBox.connectToServer()
     995         *  @since 0.9.34
     996         */
     997        private static class ConnectWaiter implements NewMailListener, Runnable {
     998                private final SessionObject _so;
     999                private final POP3MailBox _mb;
     1000
     1001                public ConnectWaiter(SessionObject so) {
     1002                        _so = so;
     1003                        _mb = _so.mailbox;
     1004                }
     1005
     1006                /** run this way if already connected */
     1007                public void run() {
     1008                        foundNewMail(true);
     1009                }
     1010
     1011                /** @param connected are we? */
     1012                public void foundNewMail(boolean connected) {
     1013                        MailCache mc = null;
     1014                        Folder<String> f = null;
     1015                        boolean found = false;
     1016                        if (connected) {
     1017                                Debug.debug(Debug.DEBUG, "CONNECTED, YAY");
     1018                                // we do this whether new mail was found or not,
     1019                                // because we may already have UIDLs in the MailCache to fetch
     1020                                synchronized(_so) {
     1021                                        while (_so.isLoading) {
     1022                                                try {
     1023                                                        _so.wait(5000);
     1024                                                } catch (InterruptedException ie) {
     1025                                                       Debug.debug(Debug.DEBUG, "Interrupted waiting for load", ie);
     1026                                                        return;
    9111027                                                }
    912                                                
    913                                                 // setElements() sorts, so configure the sorting first
    914                                                 //sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) );
    915                                                 sessionObject.folder.addSorter( SORT_SENDER, new SenderSorter( sessionObject.mailCache ) );
    916                                                 sessionObject.folder.addSorter( SORT_SUBJECT, new SubjectSorter( sessionObject.mailCache ) );
    917                                                 sessionObject.folder.addSorter( SORT_DATE, new DateSorter( sessionObject.mailCache ) );
    918                                                 sessionObject.folder.addSorter( SORT_SIZE, new SizeSorter( sessionObject.mailCache ) );
    919                                                 // reverse sort, latest mail first
    920                                                 // TODO get user defaults from config
    921                                                 sessionObject.folder.setSortBy(SORT_DEFAULT, SORT_ORDER_DEFAULT);
    922                                                 // get through cache so we have the disk-only ones too
    923                                                 String[] uidls = mc.getUIDLs();
    924                                                 sessionObject.folder.setElements(uidls);
    925 
    926                                                 sessionObject.reallyDelete = false;
    927                                                 if (offline)
    928                                                         Debug.debug(Debug.DEBUG, "OFFLINE MODE");
    929                                                 else
    930                                                         Debug.debug(Debug.DEBUG, "CONNECTED, YAY");
    931                                                 // we do this after the initial priming above
    932                                                 mailbox.setNewMailListener(sessionObject);
    933                                         } else {
    934                                                 sessionObject.error += mailbox.lastError() + '\n';
    935                                                 Debug.debug(Debug.DEBUG, "LOGIN FAIL, REMOVING SESSION");
    936                                                 HttpSession session = request.getSession();
    937                                                 session.removeAttribute( "sessionObject" );
    938                                                 session.invalidate();
    939                                                 mailbox.destroy();
    940                                                 sessionObject.mailbox = null;
    941                                                 sessionObject.mailCache = null;
    942                                                 Debug.debug(Debug.DEBUG, "NOT CONNECTED, BOO");
    9431028                                        }
    944                                 }
    945                         }
    946                 }
    947                 return state;
    948         }
     1029                                        mc = _so.mailCache;
     1030                                        f = _so.folder;
     1031                                }
     1032                                Debug.debug(Debug.DEBUG, "Done waiting for folder load");
     1033                                // fetch the mail outside the lock
     1034                                // TODO, would be better to add each email as we get it
     1035                                if (mc != null && f != null) {
     1036                                        found = mc.getMail(MailCache.FetchMode.HEADER);
     1037                                }
     1038                        } else {
     1039                                Debug.debug(Debug.DEBUG, "NOT CONNECTED, BOO");
     1040                        }
     1041                        synchronized(_so) {
     1042                                if (!connected) {
     1043                                        String error = _mb.lastError();
     1044                                        if (error.length() > 0)
     1045                                                _so.connectError = error;
     1046                                        else
     1047                                                _so.connectError = _t("Error connecting to server");
     1048                                } else if (!found) {
     1049                                        Debug.debug(Debug.DEBUG, "No new emails");
     1050                                        _so.newMails = 0;
     1051                                        _so.connectError = null;
     1052                                } else if (mc != null && f != null) {
     1053                                        String[] uidls = mc.getUIDLs();
     1054                                        int added = f.addElements(Arrays.asList(uidls));
     1055                                        if (added > 0)
     1056                                                _so.pageChanged = true;
     1057                                        _so.newMails = added;
     1058                                        _so.connectError = null;
     1059                                        Debug.debug(Debug.DEBUG, "Added " + added + " new emails");
     1060                                } else {
     1061                                        Debug.debug(Debug.DEBUG, "MailCache/folder vanished?");
     1062                                }
     1063                                _mb.setNewMailListener(_so);
     1064                                _so.isFetching = false;
     1065                                _so.notifyAll();
     1066                        }
     1067                }
     1068        }
     1069
    9491070
    9501071        /**
     
    10011122                if (state == State.AUTH)
    10021123                        return state;
     1124                // if loading, we can't get to states LIST/SHOW or it will block
     1125                if (sessionObject.isLoading)
     1126                        return State.LOADING;
    10031127
    10041128                /*
     
    12931417                                return State.AUTH;
    12941418                        }
    1295                         mailbox.refresh();
    1296                         String error = mailbox.lastError();
    1297                         sessionObject.error += error + '\n';
    1298                         sessionObject.mailCache.getMail(MailCache.FetchMode.HEADER);
    1299                         // get through cache so we have the disk-only ones too
    1300                         String[] uidls = sessionObject.mailCache.getUIDLs();
    1301                         int added = sessionObject.folder.addElements(Arrays.asList(uidls));
    1302                         if (added > 0)
    1303                                 sessionObject.info += ngettext("{0} new message", "{0} new messages", added);
    1304                         else if (error.length() <= 0)
    1305                                 sessionObject.info += _t("No new messages");
    1306                         sessionObject.pageChanged = true;
     1419                        if (sessionObject.isFetching) {
     1420                                // shouldn't happen, button disabled
     1421                                return state;
     1422                        }
     1423                        sessionObject.isFetching = true;
     1424                        ConnectWaiter cw = new ConnectWaiter(sessionObject);
     1425                        if (mailbox.connectToServer(cw)) {
     1426                                // Already connected, start a thread ourselves
     1427                                // TODO - But if already connected, we aren't going to find anything new.
     1428                                // We used to call refresh() from here, which closes first,
     1429                                // but that isn't threaded.
     1430                                Debug.debug(Debug.DEBUG, "Already connected, running CW");
     1431                                Thread t = new I2PAppThread(cw, "Email fetcher");
     1432                                t.start();
     1433                        }
     1434                        // wait if it's going to be quick
     1435                        try {
     1436                                sessionObject.wait(3000);
     1437                        } catch (InterruptedException ie) {
     1438                                Debug.debug(Debug.DEBUG, "Interrupted waiting for connect", ie);
     1439                        }
    13071440                }
    13081441                return state;
     
    18321965                                }
    18331966                        }
     1967                        if (state == State.LOADING) {
     1968                                if (isPOST) {
     1969                                        sendRedirect(httpRequest, response, null);
     1970                                        return;
     1971                                }
     1972                        }
     1973
    18341974                        // Set in web.xml
    18351975                        //if (oldState == State.AUTH && newState != State.AUTH) {
     
    18571997                                        // LIST is from SHOW page, SEND and CANCEL are from NEW page
    18581998                                        // OFFLINE and LOGIN from login page
    1859                                         // TODO - REFRESH on list page
     1999                                        // REFRESH on list page
    18602000                                        if (newPage != page || buttonPressed(request, LIST) ||
    18612001                                            buttonPressed(request, SEND) || buttonPressed(request, CANCEL) ||
     2002                                            buttonPressed(request, REFRESH) ||
    18622003                                            buttonPressed(request, LOGIN) || buttonPressed(request, OFFLINE)) {
    18632004                                                // P-R-G
     
    19732114                                 * build subtitle
    19742115                                 */
    1975                                 if( state == State.AUTH )
     2116                                if (state == State.AUTH) {
    19762117                                        subtitle = _t("Login");
    1977                                 else if( state == State.LIST ) {
     2118                                } else if (state == State.LOADING) {
     2119                                        subtitle = _t("Loading emails, please wait...");
     2120                                } else if( state == State.LIST ) {
    19782121                                        // mailbox.getNumMails() forces a connection, don't use it
    19792122                                        // Not only does it slow things down, but a failure causes all our messages to "vanish"
     
    20242167                                } else if (state == State.LIST) {
    20252168                                        out.println("<script src=\"/susimail/js/folder.js\" type=\"text/javascript\"></script>");
     2169                                } else if (state == State.LOADING) {
     2170                                        // TODO JS?
     2171                                        out.println("<meta http-equiv=\"refresh\" content=\"5;url=" + myself + "\">");
     2172                                        // TODO we don't need the form below
    20262173                                }
    20272174                                out.print("</head>\n<body" + (state == State.LIST ? " onload=\"deleteboxclicked()\">" : ">"));
     
    20622209                                        out.println("<input type=\"hidden\" name=\"" + CURRENT_FOLDER + "\" value=\"" + PersistentMailCache.DIR_FOLDER + "\">");
    20632210                                }
    2064                                 if( sessionObject.error != null && sessionObject.error.length() > 0 ) {
     2211                                boolean showRefresh = false;
     2212                                if (sessionObject.isLoading) {
     2213                                        sessionObject.info += _t("Loading emails, please wait...") + '\n';
     2214                                        showRefresh = true;
     2215                                }
     2216                                if (sessionObject.isFetching) {
     2217                                        sessionObject.info += _t("Checking for new emails on server") + '\n';
     2218                                        showRefresh = true;
     2219                                } else if (state != State.LOADING && state != State.AUTH && state != State.CONFIG) {
     2220                                        String error = sessionObject.connectError;
     2221                                        if (error != null && error.length() > 0) {
     2222                                                sessionObject.error += error + '\n';
     2223                                                sessionObject.connectError = null;
     2224                                        }
     2225                                        int added = sessionObject.newMails;
     2226                                        if (added > 0) {
     2227                                                sessionObject.info += ngettext("{0} new message", "{0} new messages", added) + '\n';
     2228                                                sessionObject.newMails = -1;
     2229                                        } else if (added == 0) {
     2230                                                sessionObject.info += _t("No new messages") + '\n';
     2231                                                sessionObject.newMails = -1;
     2232                                        }
     2233                                }
     2234                                if (showRefresh) {
     2235                                        sessionObject.info += _t("Refresh the page for updates") + '\n';
     2236                                }
     2237                                if (sessionObject.error.length() > 0) {
    20652238                                        out.println( "<div class=\"notifications\" onclick=\"this.remove()\"><p class=\"error\">" + quoteHTML(sessionObject.error).replace("\n", "<br>") + "</p></div>" );
    20662239                                }
    2067                                 if( sessionObject.info != null && sessionObject.info.length() > 0 ) {
     2240                                if (sessionObject.info.length() > 0) {
    20682241                                        out.println( "<div class=\"notifications\" onclick=\"this.remove()\"><p class=\"info\"><b>" + quoteHTML(sessionObject.info).replace("\n", "<br>") + "</b></p></div>" );
    20692242                                }
     
    20732246                                if( state == State.AUTH )
    20742247                                        showLogin( out );
     2248
     2249                                else if (state == State.LOADING)
     2250                                        showLoading(out, sessionObject, request);
    20752251                               
    20762252                                else if( state == State.LIST )
     
    21042280                        url = url.substring(0, qq);
    21052281                buf.append(url);
    2106                 if (q.length() > 0)
     2282                if (q != null && q.length() > 0)
    21072283                        buf.append(q.replace("&amp;", "&"));  // no you don't html escape the redirect header
    21082284                resp.setHeader("Location", buf.toString());
     
    21412317                        String name2 = FilenameUtil.sanitizeFilename(name);
    21422318                        String name3 = FilenameUtil.encodeFilenameRFC5987(name);
     2319                        response.setHeader("Cache-Control", "public, max-age=604800");
    21432320                        if (isRaw) {
    21442321                                try {
     
    21502327                                                response.setContentLength(part.decodedLength);
    21512328                                        Debug.debug(Debug.DEBUG, "Sending raw attachment " + name + " length " + part.decodedLength);
    2152                                         // cache-control?
    2153                                         // was 2
    21542329                                        part.decode(0, new OutputStreamBuffer(response.getOutputStream()));
    21552330                                        shown = true;
     
    22082383                        response.setContentType("message/rfc822");
    22092384                        response.setContentLength(content.getLength());
    2210                         // cache-control?
     2385                        response.setHeader("Cache-Control", "public, max-age=604800");
    22112386                        response.addHeader("Content-Disposition", "attachment; filename=\"" + name2 + "\"; " +
    22122387                                           "filename*=" + name3);
     
    24992674
    25002675        /**
     2676         * @since 0.9.34
     2677         */
     2678        private static void showLoading(PrintWriter out, SessionObject sessionObject, RequestWrapper request) {
     2679                // TODO make it pretty
     2680                out.println("<p><b>");
     2681                out.println(_t("Loading emails, please wait..."));
     2682                out.println("</b><p><b>");
     2683                out.println(_t("Refresh the page for updates"));
     2684                out.println("</b>");
     2685        }
     2686
     2687        /**
    25012688         *
    25022689         * @param out
     
    25142701                        //button( FORWARD, _t("Forward") ) + spacer +
    25152702                        //button( DELETE, _t("Delete") ) + spacer +
    2516                 out.println(button( REFRESH, _t("Check Mail") ) + spacer);
     2703                out.println((sessionObject.isFetching ? button2(REFRESH, _t("Check Mail")) : button(REFRESH, _t("Check Mail"))) + spacer);
    25172704                //if (Config.hasConfigFile())
    25182705                //      out.println(button( RELOAD, _t("Reload Config") ) + spacer);
     
    26832870                }
    26842871                Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL);
    2685                 if(!RELEASE && mail != null && mail.hasBody() && mail.getBody().getLength() < 16384) {
     2872                if(!RELEASE && mail != null && mail.hasBody() && mail.getSize() < 16384) {
    26862873                        out.println( "<!--" );
    26872874                        out.println( "Debug: Mail header and body follow");
  • apps/susimail/src/src/i2p/susi/webmail/pop3/BackgroundChecker.java

    rf13f4fc r012fb4ca  
    106106                public void run() {
    107107                        try {
    108                                 if (mailbox.connectToServer()) {
     108                                if (mailbox.blockingConnectToServer()) {
    109109                                        int found = mailbox.getNumMails();
    110110                                        if (found > 0) {
    111111                                                Debug.debug(Debug.DEBUG, "Found " + found + " mails, calling listener");
    112112                                                // may not really be new
    113                                                 mailbox.foundNewMail();
     113                                                mailbox.foundNewMail(true);
    114114                                        }
    115115                                }
  • apps/susimail/src/src/i2p/susi/webmail/pop3/POP3MailBox.java

    rf13f4fc r012fb4ca  
    4545
    4646import net.i2p.data.DataHelper;
     47import net.i2p.util.I2PAppThread;
    4748import net.i2p.util.InternalSocket;
    4849
     
    440441         * @return true or false
    441442         */
    442         public boolean isConnected() {
     443        boolean isConnected() {
    443444                synchronized(synchronizer) {
    444445                        if (socket == null) {
     
    577578
    578579        /**
    579          *
    580          *
     580         * Close (why?) and connect to server.
     581         * Blocking.
    581582         */
    582583        public void refresh() {
     
    600601         * Connect to pop3 server if not connected.
    601602         * Does nothing if already connected.
     603         * Blocking.
     604         *
     605         * This will NOT call any configured NewMailListener,
     606         * only the one passed in. It will be called with the value
     607         * true if the connect was successful, false if not.
     608         * Call getNumMails() to see if there really was any new mail.
     609         *
     610         * After the callback is executed, the information on new mails, if any,
     611         * is available via getNumMails(), getUIDLs(), and getSize().
     612         * The connection to the server will remain open, so that
     613         * new emails may be retrieved via
     614         * getHeader(), getBody(), and getBodies().
     615         * Failure info is available via lastError().
     616         *
     617         * @return true if connected already and nml will NOT be called back, false if nml will be called back
     618         * @since 0.9.13
     619         */
     620        public boolean connectToServer(NewMailListener nml) {
     621                synchronized( synchronizer ) {
     622                        if (isConnected())
     623                                return true;
     624                }
     625                Thread t = new I2PAppThread(new ConnectRunner(nml), "POP3 Connector");
     626                try {
     627                        t.start();
     628                } catch (Throwable e) {
     629                        // not really, but we won't be calling the callback
     630                        return true;
     631                }
     632                return false;
     633
     634        }
     635
     636        /** @since 0.9.34 */
     637        private class ConnectRunner implements Runnable {
     638                private final NewMailListener _nml;
     639
     640                public ConnectRunner(NewMailListener nml) {
     641                        _nml = nml;
     642                }
     643
     644                public void run() {
     645                        boolean result = false;
     646                        try {
     647                                result = blockingConnectToServer();
     648                        } finally {
     649                                _nml.foundNewMail(result);
     650                        }
     651                }
     652        }
     653
     654        /**
     655         * Connect to pop3 server if not connected.
     656         * Does nothing if already connected.
     657         * Blocking.
     658         *
     659         * This will NOT call any configured NewMailListener.
     660         *
     661         * After the callback is executed, the information on new mails, if any,
     662         * is available via getNumMails(), getUIDLs(), and getSize().
     663         * The connection to the server will remain open, so that
     664         * new emails may be retrieved via
     665         * getHeader(), getBody(), and getBodies().
     666         * Failure info is available via lastError().
    602667         *
    603668         * @return true if connected
    604669         * @since 0.9.13
    605670         */
    606         public boolean connectToServer() {
     671        boolean blockingConnectToServer() {
    607672                synchronized( synchronizer ) {
    608673                        if (isConnected())
     
    614679
    615680        /**
    616          * connect to pop3 server, login with USER and PASS and try STAT then
     681         * Closes any existing connection first.
     682         * Then, connect to pop3 server, login with USER and PASS and try STAT then
    617683         *
    618684         * Caller must sync.
     
    631697                        socket = InternalSocket.getSocket(host, port);
    632698                } catch (IOException e) {
    633                         Debug.debug( Debug.DEBUG, "Error connecting: " + e);
     699                        Debug.debug(Debug.DEBUG, "Error connecting", e);
    634700                        lastError = _t("Cannot connect") + " (" + host + ':' + port + ") - " + e.getLocalizedMessage();
    635701                        return;
     
    641707                                socket.setSoTimeout(120*1000);
    642708                                boolean ok = doHandshake();
     709                                boolean loginOK = false;
    643710                                if (ok) {
    644711                                        // TODO APOP (unsupported by postman)
     
    647714                                        cmds.add(new SendRecv("PASS " + pass, Mode.A1));
    648715                                        socket.setSoTimeout(60*1000);
    649                                         ok =  sendCmds(cmds);
    650                                 }
    651                                 if (ok) {
     716                                        loginOK =  sendCmds(cmds);
     717                                }
     718                                if (loginOK) {
    652719                                        connected = true;
    653720                                        List<SendRecv> cmds = new ArrayList<SendRecv>(4);
     
    682749                                        if (lastError.equals(""))
    683750                                                lastError = _t("Error connecting to server");
     751                                        if (ok && !loginOK) {
     752                                                lastError += '\n' +
     753                                                             _t("Mail server login failed, wrong username or password.") +
     754                                                             '\n' +
     755                                                             _t("Logout and then login again with the correct username and password.");
     756                                        }
    684757                                        close();
    685758                                }
     
    10611134
    10621135        /**
    1063          * @return The most recent error message.
     1136         * @return The most recent error message. Probably not terminated with a newline.
    10641137         */
    10651138        public String lastError() {
     
    10721145                if (e.trim().equals("Login failed."))
    10731146                        e = _t("Login failed");
     1147                else if (e.startsWith("Login failed."))
     1148                        e = _t("Login failed") + e.substring(13);
    10741149                return e;
    10751150        }
     
    10941169         *  @since 0.9.13
    10951170         */
    1096         public void foundNewMail() {
     1171        public void foundNewMail(boolean yes) {
    10971172                NewMailListener  nml = newMailListener;
    10981173                if (nml != null)
    1099                         nml.foundNewMail();
     1174                        nml.foundNewMail(yes);
    11001175        }
    11011176
  • history.txt

    rf13f4fc r012fb4ca  
     12018-02-12 zzz
     2 * SusiMail:
     3   - Background email checking (ticket #2087)
     4   - Set Cache-Control header for attachments
     5   - Fix rotated attached images
     6
    172018-02-11 zzz
    28 * Util: Number formatting tweaks (ticket #1913)
  • installer/resources/themes/susimail/classic/susimail.css

    rf13f4fc r012fb4ca  
    167167     border: 1px solid #cfd6ff;
    168168     padding: 2px;
     169     image-orientation: from-image;
    169170}
    170171
  • installer/resources/themes/susimail/dark/susimail.css

    rf13f4fc r012fb4ca  
    916916     padding: 2px;
    917917     background: #010;
     918     image-orientation: from-image;
    918919}
    919920
  • installer/resources/themes/susimail/light/susimail.css

    rf13f4fc r012fb4ca  
    13911391     max-width: calc(100% - 25px);
    13921392     filter: drop-shadow(0 0 1px #999);
     1393     image-orientation: from-image;
    13931394}
    13941395
  • installer/resources/themes/susimail/midnight/susimail.css

    rf13f4fc r012fb4ca  
    856856     border: 1px solid #443da0;
    857857     border-radius: 2px;
     858     image-orientation: from-image;
    858859}
    859860
  • router/java/src/net/i2p/router/RouterVersion.java

    rf13f4fc r012fb4ca  
    1919    public final static String ID = "Monotone";
    2020    public final static String VERSION = CoreVersion.VERSION;
    21     public final static long BUILD = 4;
     21    public final static long BUILD = 5;
    2222
    2323    /** for example "-test" */
Note: See TracChangeset for help on using the changeset viewer.