boolean idle(final ImapRequest req, final boolean begin) throws IOException { if (begin == ImapHandler.IDLE_STOP) { // check state -- don't want to send DONE if we're somehow not in IDLE ImapHandler handler = mHandler; if (handler == null) throw new IOException("proxy connection already closed"); Thread idle = mIdleThread; if (idle == null) throw new IOException("bad proxy state: no IDLE thread active when attempting DONE"); // send the DONE, which elicits the tagged response that causes the IDLE thread (below) to // exit writeRequest(req.toByteArray()); // make sure that the idle thread actually exits; otherwise we're in a bad place and we must // kill the whole session mIdleThread = null; try { idle.join(5 * Constants.MILLIS_PER_SECOND); } catch (InterruptedException ie) { } if (idle.isAlive()) handler.dropConnection(false); } else { final ImapHandler handler = mHandler; final ImapConnection conn = mConnection; if (conn == null) throw new IOException("proxy connection already closed"); ImapConfig config = conn.getImapConfig(); final int oldTimeout = config != null ? config.getReadTimeout() : LC.javamail_imap_timeout.intValue(); // necessary because of subsequent race condition with req.cleanup() final byte[] payload = req.toByteArray(); mIdleThread = new Thread() { @Override public void run() { boolean success = false; try { // the standard aggressive read timeout is inappropriate for IDLE conn.setReadTimeout(mHandler.getConfig().getAuthenticatedMaxIdleSeconds()); // send the IDLE command; this call waits until the subsequent DONE is acknowledged boolean ok = proxyCommand(req.getTag(), payload, true, true); // restore the old read timeout conn.setReadTimeout(oldTimeout); // don't set <code>success</code> until we're past things that can throw // IOExceptions success = ok; } catch (IOException e) { ZimbraLog.imap.warn("error encountered during IDLE; dropping connection", e); } if (!success) handler.dropConnection(); } }; mIdleThread.setName("Imap-Idle-Proxy-" + Thread.currentThread().getName()); mIdleThread.start(); } return true; }
ImapProxy(final ImapHandler handler, final ImapPath path) throws ServiceException { mHandler = handler; mPath = path; Account acct = handler.getCredentials().getAccount(); Server server = Provisioning.getInstance().getServer(path.getOwnerAccount()); String host = server.getServiceHostname(); if (acct == null) throw ServiceException.PROXY_ERROR( new Exception("no such authenticated user"), path.asImapPath()); ImapConfig config = new ImapConfig(); config.setAuthenticationId(acct.getName()); config.setMechanism(ZimbraAuthenticator.MECHANISM); config.setAuthenticatorFactory(sAuthenticatorFactory); config.setReadTimeout(LC.javamail_imap_timeout.intValue()); config.setConnectTimeout(config.getReadTimeout()); config.setHost(host); if (server.isImapServerEnabled()) { config.setPort(server.getIntAttr(Provisioning.A_zimbraImapBindPort, ImapConfig.DEFAULT_PORT)); } else if (server.isImapSSLServerEnabled()) { config.setPort( server.getIntAttr(Provisioning.A_zimbraImapSSLBindPort, ImapConfig.DEFAULT_SSL_PORT)); config.setSecurity(MailConfig.Security.SSL); } else { throw ServiceException.PROXY_ERROR( new Exception("no open IMAP port for server " + host), path.asImapPath()); } ZimbraLog.imap.info( "opening proxy connection (user="******", host=" + host + ", path=" + path.getReferent().asImapPath() + ')'); ImapConnection conn = mConnection = new ImapConnection(config); try { conn.connect(); conn.authenticate(AuthProvider.getAuthToken(acct).getEncoded()); } catch (Exception e) { dropConnection(); throw ServiceException.PROXY_ERROR(e, null); } }