use of com.zimbra.cs.mailclient.imap.ImapConnection in project zm-mailbox by Zimbra.
the class ImapProxy method proxyCommand.
private boolean proxyCommand(byte[] payload, boolean includeTaggedResponse, boolean isIdle) throws ImapProxyException {
ImapConnection conn = writeRequest(payload);
MailInputStream min = conn.getInputStream();
OutputStream out = handler.output;
if (out == null) {
dropConnection();
throw new ImapProxyException("client connection already closed");
}
// copy the response back to the handler's output (i.e. the original client)
boolean success = false;
int first;
try {
while ((first = min.peek()) != -1) {
// XXX: may want to check that the "tagged" response's tag actually matches the request's tag...
boolean tagged = first != '*' && first != '+';
boolean structured = first == '*';
boolean proxy = (first != '+' || isIdle) && (!tagged || includeTaggedResponse);
ByteArrayOutputStream line = proxy ? new ByteArrayOutputStream() : null;
StringBuilder debug = proxy && ZimbraLog.imap.isDebugEnabled() ? new StringBuilder(" pxy: ") : null;
StringBuilder condition = new StringBuilder(10);
boolean quoted = false;
boolean escaped = false;
boolean space1 = false;
boolean space2 = false;
int c;
int literal = -1;
while ((c = min.read()) != -1) {
// check for success and also determine whether we should be paying attention to structure
if (!space2) {
if (c == ' ' && !space1) {
space1 = true;
} else if (c == ' ') {
space2 = true;
String code = condition.toString().toUpperCase();
if ("BYE".equals(code)) {
// unexpected BYE
dropConnection();
throw new ImapProxyException("proxy connection already closed");
}
if (tagged) {
success = "OK".equals(code) || (isIdle && "BAD".equals(code));
}
structured &= !UNSTRUCTURED_CODES.contains(code);
} else if (space1) {
condition.append((char) c);
}
}
// if it's a structured response, pay attention to quoting, literals, etc.
if (structured) {
if (escaped)
escaped = false;
else if (quoted && c == '\\')
escaped = true;
else if (c == '"')
quoted = !quoted;
else if (!quoted && c == '{')
literal = 0;
else if (literal != -1 && c >= '0' && c <= '9')
literal = literal * 10 + (c - '0');
}
if (!quoted && c == '\r' && min.peek() == '\n') {
// skip the terminal LF
min.read();
// write the line back to the client
if (proxy) {
out.write(line.toByteArray());
out.write(ImapHandler.LINE_SEPARATOR_BYTES);
line.reset();
if (isIdle)
out.flush();
}
// if it's end of line (i.e. no literal), we're done
if (literal == -1)
break;
// if there's a literal, copy it and then handle the following line
byte[] buffer = literal == 0 ? null : new byte[Math.min(literal, 65536)];
while (literal > 0) {
int read = min.read(buffer, 0, Math.min(literal, buffer.length));
if (read == -1)
break;
if (proxy)
out.write(buffer, 0, read);
literal -= read;
}
literal = -1;
if (isIdle)
out.flush();
} else if (proxy) {
line.write(c);
if (debug != null)
debug.append((char) c);
}
}
if (debug != null)
ZimbraLog.imap.debug(debug.toString());
if (tagged)
break;
}
out.flush();
} catch (ImapProxyException e) {
throw e;
} catch (IOException e) {
throw new ImapProxyException(e);
}
return success;
}
use of com.zimbra.cs.mailclient.imap.ImapConnection in project zm-mailbox by Zimbra.
the class ImapFolderSync method refetchPurgedMsgsInConversation.
protected void refetchPurgedMsgsInConversation(ParsedMessage pm) throws ServiceException, IOException {
if (!mailbox.getAccount().isFeatureDataSourcePurgingEnabled()) {
return;
}
Threader threader = pm.getThreader(ds.getMailbox());
List<PurgedConversation> purgedConvs = threader.lookupPurgedConversations(ds);
if (purgedConvs.size() == 0) {
return;
} else {
for (PurgedConversation conv : purgedConvs) {
ImapConnection conn;
try {
conn = getRefetchConnection();
} catch (ServiceException e) {
ZimbraLog.datasource.warn("could not establish IMAP connection to refetch purged messages");
return;
}
for (PurgedMessage msg : conv.getMessages()) {
String remoteFolderId = msg.getRemoteFolder();
String msgUid = msg.getUid();
final Integer folderId = msg.getLocalFolderId();
ZimbraLog.datasource.info("restoring message " + msgUid + " in remote folder " + remoteFolderId);
conn.select(remoteFolderId);
final Map<Long, MessageData> msgFlags = conn.uidFetch(msgUid, "(FLAGS INTERNALDATE)");
FetchResponseHandler handler = new FetchResponseHandler() {
@Override
public void handleFetchResponse(MessageData md) throws Exception {
long uid = md.getUid();
IOExceptionHandler.getInstance().trackSyncItem(mailbox, uid);
try {
handleFetch(md, msgFlags, folderId, false, false);
clearError(uid);
} catch (OutOfMemoryError e) {
Zimbra.halt("Out of memory", e);
} catch (Exception e) {
if (!IOExceptionHandler.getInstance().isRecoverable(mailbox, uid, "Exception re-fetching UID " + uid, e)) {
syncFailed("re-fetch failed for uid " + uid, e);
SyncErrorManager.incrementErrorCount(ds, remoteId(uid));
}
}
}
};
conn.uidFetch(msgUid, "BODY.PEEK[]", handler);
}
conv.unpurge();
}
}
}
use of com.zimbra.cs.mailclient.imap.ImapConnection in project zm-mailbox by Zimbra.
the class ConnectionManager method closeConnection.
/**
* Closes any suspended connection associated with specified data source.
* This must be called whenever data source is modified or deleted in order
* to force a reconnect upon next use.
*
* @param dataSourceId the data source id for the connection
*/
public void closeConnection(String dataSourceId) {
ImapConnection ic = connections.remove(dataSourceId);
if (ic != null) {
LOG.debug("Closing connection: " + ic);
ic.close();
}
}
use of com.zimbra.cs.mailclient.imap.ImapConnection in project zm-mailbox by Zimbra.
the class ConnectionManager method openConnection.
/**
* Opens a new IMAP connection or reuses an existing one if available
* for specified data source. If a new connection is required the optional
* authenticator, if specified, will be used with AUTHENTICATE, otherwise
* the LOGIN command will be used. If a suspended connection is reused
* and has been idling, then IDLE will be automatically terminated.
*
* @param ds the data source for the connection
* @param auth optional authenticator, or null to use LOGIN
* @return the IMAP connection to use
* @throws ServiceException if an I/O or auth error occurred
*/
public ImapConnection openConnection(DataSource ds, Authenticator auth) throws ServiceException {
ImapConnection ic = connections.remove(ds.getId());
if (ic == null || !resumeConnection(ic)) {
ic = newConnection(ds, auth);
}
ic.getImapConfig().setMaxLiteralMemSize(ds.getMaxTraceSize());
return ic;
}
use of com.zimbra.cs.mailclient.imap.ImapConnection in project zm-mailbox by Zimbra.
the class ConnectionManager method releaseConnection.
/**
* Releases existing connection so that it can be reused again. If the
* IMAP server supports IDLE then the connection will go into IDLE state.
* Otherwise, the connection will be kept active for as long as possible.
*
* @param ds the data source for the connection
* @param ic the connection to release
*/
public void releaseConnection(DataSource ds, ImapConnection ic) {
LOG.debug("Releasing connection: " + ic);
if (isReuseConnections(ds) && suspendConnection(ds, ic)) {
ImapConnection conn = connections.put(ds.getId(), ic);
if (conn != null) {
LOG.debug("More than one suspended connection for: %s. closing the oldest: %s", ds, conn);
// there was another suspended connection; just close it
conn.close();
}
} else {
LOG.debug("Closing connection: " + ic);
ic.close();
}
}
Aggregations