use of com.zimbra.cs.mailbox.DeliveryOptions in project zm-mailbox by Zimbra.
the class ZimbraLmtpBackend method deliverMessageToLocalMailboxes.
private void deliverMessageToLocalMailboxes(Blob blob, BlobInputStream bis, byte[] data, MimeMessage mm, LmtpEnvelope env) throws ServiceException, IOException {
List<LmtpAddress> recipients = env.getLocalRecipients();
String envSender = env.getSender().getEmailAddress();
boolean shared = recipients.size() > 1;
List<Integer> targetMailboxIds = new ArrayList<Integer>(recipients.size());
Map<LmtpAddress, RecipientDetail> rcptMap = new HashMap<LmtpAddress, RecipientDetail>(recipients.size());
try {
// Examine attachments indexing option for all recipients and
// prepare ParsedMessage versions needed. Parsing is done before
// attempting delivery to any recipient. Therefore, parse error
// will result in non-delivery to all recipients.
// ParsedMessage for users with attachments indexing
ParsedMessage pmAttachIndex = null;
// ParsedMessage for users without attachments indexing
ParsedMessage pmNoAttachIndex = null;
// message id for logging
String msgId = null;
for (LmtpAddress recipient : recipients) {
String rcptEmail = recipient.getEmailAddress();
Account account;
Mailbox mbox;
boolean attachmentsIndexingEnabled;
try {
account = Provisioning.getInstance().get(AccountBy.name, rcptEmail);
if (account == null) {
ZimbraLog.mailbox.warn("No account found delivering mail to " + rcptEmail);
continue;
}
mbox = MailboxManager.getInstance().getMailboxByAccount(account);
if (mbox == null) {
ZimbraLog.mailbox.warn("No mailbox found delivering mail to " + rcptEmail);
continue;
}
attachmentsIndexingEnabled = mbox.attachmentsIndexingEnabled();
} catch (ServiceException se) {
if (se.isReceiversFault()) {
ZimbraLog.mailbox.info("Recoverable exception getting mailbox for " + rcptEmail, se);
rcptMap.put(recipient, new RecipientDetail(null, null, null, false, DeliveryAction.defer));
} else {
ZimbraLog.mailbox.warn("Unrecoverable exception getting mailbox for " + rcptEmail, se);
}
continue;
}
if (account != null && mbox != null) {
ParsedMessageOptions pmo;
if (mm != null) {
pmo = new ParsedMessageOptions().setContent(mm).setDigest(blob.getDigest()).setSize(blob.getRawSize());
} else {
pmo = new ParsedMessageOptions(blob, data);
}
ParsedMessage pm;
if (attachmentsIndexingEnabled) {
if (pmAttachIndex == null) {
pmo.setAttachmentIndexing(true);
ZimbraLog.lmtp.debug("Creating ParsedMessage from %s with attachment indexing enabled", data == null ? "file" : "memory");
pmAttachIndex = new ParsedMessage(pmo);
}
pm = pmAttachIndex;
} else {
if (pmNoAttachIndex == null) {
pmo.setAttachmentIndexing(false);
ZimbraLog.lmtp.debug("Creating ParsedMessage from %s with attachment indexing disabled", data == null ? "file" : "memory");
pmNoAttachIndex = new ParsedMessage(pmo);
}
pm = pmNoAttachIndex;
}
msgId = pm.getMessageID();
if (account.isPrefMailLocalDeliveryDisabled()) {
ZimbraLog.lmtp.debug("Local delivery disabled for account %s", rcptEmail);
rcptMap.put(recipient, new RecipientDetail(account, mbox, pm, false, DeliveryAction.discard));
continue;
}
// For non-shared delivery (i.e. only one recipient),
// always deliver regardless of backup mode.
DeliveryAction da = DeliveryAction.deliver;
boolean endSharedDelivery = false;
if (shared) {
if (mbox.beginSharedDelivery()) {
endSharedDelivery = true;
} else {
// Skip delivery to mailboxes in backup mode.
da = DeliveryAction.defer;
}
}
rcptMap.put(recipient, new RecipientDetail(account, mbox, pm, endSharedDelivery, da));
if (da == DeliveryAction.deliver) {
targetMailboxIds.add(mbox.getId());
}
}
}
ZimbraLog.removeAccountFromContext();
if (ZimbraLog.lmtp.isInfoEnabled()) {
ZimbraLog.lmtp.info("Delivering message: size=%s, nrcpts=%d, sender=%s, msgid=%s", env.getSize() == 0 ? "unspecified" : Integer.toString(env.getSize()) + " bytes", recipients.size(), env.getSender(), msgId == null ? "" : msgId);
}
DeliveryContext sharedDeliveryCtxt = new DeliveryContext(shared, targetMailboxIds);
sharedDeliveryCtxt.setIncomingBlob(blob);
// version each recipient needs. Deliver!
for (LmtpAddress recipient : recipients) {
String rcptEmail = recipient.getEmailAddress();
LmtpReply reply = LmtpReply.TEMPORARY_FAILURE;
RecipientDetail rd = rcptMap.get(recipient);
if (rd.account != null)
ZimbraLog.addAccountNameToContext(rd.account.getName());
if (rd.mbox != null)
ZimbraLog.addMboxToContext(rd.mbox.getId());
boolean success = false;
try {
if (rd != null) {
switch(rd.action) {
case discard:
ZimbraLog.lmtp.info("accepted and discarded message from=%s,to=%s: local delivery is disabled", envSender, rcptEmail);
if (rd.account.getPrefMailForwardingAddress() != null) {
// mail forwarding is set up
for (LmtpCallback callback : callbacks) {
ZimbraLog.lmtp.debug("Executing callback %s", callback.getClass().getName());
callback.forwardWithoutDelivery(rd.account, rd.mbox, envSender, rcptEmail, rd.pm);
}
}
reply = LmtpReply.DELIVERY_OK;
break;
case deliver:
Account account = rd.account;
Mailbox mbox = rd.mbox;
ParsedMessage pm = rd.pm;
List<ItemId> addedMessageIds = null;
ReentrantLock lock = mailboxDeliveryLocks.get(mbox.getId());
boolean acquiredLock;
try {
// Wait for the lock, up to the timeout
acquiredLock = lock.tryLock(LC.zimbra_mailbox_lock_timeout.intValue(), TimeUnit.SECONDS);
} catch (InterruptedException e) {
acquiredLock = false;
}
if (!acquiredLock) {
ZimbraLog.lmtp.info("try again for message from=%s,to=%s: another mail delivery in progress.", envSender, rcptEmail);
reply = LmtpReply.TEMPORARY_FAILURE;
break;
}
try {
if (dedupe(pm, mbox)) {
// message was already delivered to this mailbox
ZimbraLog.lmtp.info("Not delivering message with duplicate Message-ID %s", pm.getMessageID());
} else if (mbox.dedupeForSelfMsg(pm)) {
ZimbraLog.mailbox.info("not delivering message, because it is a duplicate of sent message %s", pm.getMessageID());
} else if (recipient.getSkipFilters()) {
msgId = pm.getMessageID();
int folderId = Mailbox.ID_FOLDER_INBOX;
if (recipient.getFolder() != null) {
try {
Folder folder = mbox.getFolderByPath(null, recipient.getFolder());
folderId = folder.getId();
} catch (ServiceException se) {
if (se.getCode().equals(MailServiceException.NO_SUCH_FOLDER)) {
Folder folder = mbox.createFolder(null, recipient.getFolder(), new Folder.FolderOptions().setDefaultView(MailItem.Type.MESSAGE));
folderId = folder.getId();
} else {
throw se;
}
}
}
int flags = Flag.BITMASK_UNREAD;
if (recipient.getFlags() != null) {
flags = Flag.toBitmask(recipient.getFlags());
}
DeliveryOptions dopt = new DeliveryOptions().setFolderId(folderId);
dopt.setFlags(flags).setTags(recipient.getTags()).setRecipientEmail(rcptEmail);
Message msg = mbox.addMessage(null, pm, dopt, sharedDeliveryCtxt);
addedMessageIds = Lists.newArrayList(new ItemId(msg));
} else if (!DebugConfig.disableIncomingFilter) {
// Get msgid first, to avoid having to reopen and reparse the blob
// file if Mailbox.addMessageInternal() closes it.
pm.getMessageID();
addedMessageIds = RuleManager.applyRulesToIncomingMessage(null, mbox, pm, (int) blob.getRawSize(), rcptEmail, env, sharedDeliveryCtxt, Mailbox.ID_FOLDER_INBOX, false, true);
} else {
pm.getMessageID();
DeliveryOptions dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX);
dopt.setFlags(Flag.BITMASK_UNREAD).setRecipientEmail(rcptEmail);
Message msg = mbox.addMessage(null, pm, dopt, sharedDeliveryCtxt);
addedMessageIds = Lists.newArrayList(new ItemId(msg));
}
success = true;
if (addedMessageIds != null && addedMessageIds.size() > 0) {
addToDedupeCache(pm, mbox);
}
} finally {
lock.unlock();
}
if (addedMessageIds != null && addedMessageIds.size() > 0) {
// Execute callbacks
for (LmtpCallback callback : callbacks) {
for (ItemId id : addedMessageIds) {
if (id.belongsTo(mbox)) {
// Message was added to the local mailbox, as opposed to a mountpoint.
ZimbraLog.lmtp.debug("Executing callback %s", callback.getClass().getName());
try {
Message msg = mbox.getMessageById(null, id.getId());
callback.afterDelivery(account, mbox, envSender, rcptEmail, msg);
} catch (Throwable t) {
if (t instanceof OutOfMemoryError) {
Zimbra.halt("LMTP callback failed", t);
} else {
ZimbraLog.lmtp.warn("LMTP callback threw an exception", t);
}
}
}
}
}
}
reply = LmtpReply.DELIVERY_OK;
break;
case defer:
// Delivery to mailbox skipped. Let MTA retry again later.
// This case happens for shared delivery to a mailbox in
// backup mode.
ZimbraLog.lmtp.info("try again for message from=%s,to=%s: mailbox skipped", envSender, rcptEmail);
reply = LmtpReply.TEMPORARY_FAILURE;
break;
}
} else {
// Account or mailbox not found.
ZimbraLog.lmtp.info("rejecting message from=%s,to=%s: account or mailbox not found", envSender, rcptEmail);
reply = LmtpReply.PERMANENT_FAILURE;
}
} catch (DeliveryServiceException e) {
ZimbraLog.lmtp.info("rejecting message from=%s,to=%s: sieve filter rule", envSender, rcptEmail);
reply = LmtpReply.PERMANENT_MESSAGE_REFUSED;
} catch (ServiceException e) {
if (e.getCode().equals(MailServiceException.QUOTA_EXCEEDED)) {
ZimbraLog.lmtp.info("rejecting message from=%s,to=%s: overquota", envSender, rcptEmail);
if (config.isPermanentFailureWhenOverQuota()) {
reply = LmtpReply.PERMANENT_FAILURE_OVER_QUOTA;
} else {
reply = LmtpReply.TEMPORARY_FAILURE_OVER_QUOTA;
}
} else if (e.isReceiversFault()) {
ZimbraLog.lmtp.info("try again for message from=%s,to=%s", envSender, rcptEmail, e);
reply = LmtpReply.TEMPORARY_FAILURE;
} else {
ZimbraLog.lmtp.info("rejecting message from=%s,to=%s", envSender, rcptEmail, e);
reply = LmtpReply.PERMANENT_FAILURE;
}
} catch (Exception e) {
reply = LmtpReply.TEMPORARY_FAILURE;
ZimbraLog.lmtp.warn("try again for message from=%s,to=%s", envSender, rcptEmail, e);
} finally {
if (rd.action == DeliveryAction.deliver && !success) {
// Message was not delivered. Remove it from the dedupe
// cache so we don't dedupe it on LMTP retry.
removeFromDedupeCache(msgId, rd.mbox);
}
recipient.setDeliveryStatus(reply);
if (shared && rd != null && rd.esd) {
rd.mbox.endSharedDelivery();
rd.esd = false;
}
}
}
// If this message is being streamed from disk, cache it
ParsedMessage mimeSource = pmAttachIndex != null ? pmAttachIndex : pmNoAttachIndex;
MailboxBlob mblob = sharedDeliveryCtxt.getMailboxBlob();
if (mblob != null && mimeSource != null) {
if (bis == null) {
bis = mimeSource.getBlobInputStream();
}
if (bis != null) {
try {
// Update the MimeMessage with the blob that's stored inside the mailbox,
// since the incoming blob will be deleted.
Blob storedBlob = mblob.getLocalBlob();
bis.fileMoved(storedBlob.getFile());
MessageCache.cacheMessage(mblob.getDigest(), mimeSource.getOriginalMessage(), mimeSource.getMimeMessage());
} catch (IOException e) {
ZimbraLog.lmtp.warn("Unable to cache message for " + mblob, e);
}
}
}
} finally {
// called, we check and fix those here.
if (shared) {
for (RecipientDetail rd : rcptMap.values()) {
if (rd.esd && rd.mbox != null)
rd.mbox.endSharedDelivery();
}
}
}
}
use of com.zimbra.cs.mailbox.DeliveryOptions in project zm-mailbox by Zimbra.
the class AddMsg method handle.
@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Mailbox mbox = getRequestedMailbox(zsc);
OperationContext octxt = getOperationContext(zsc, context);
ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
Element msgElem = request.getElement(MailConstants.E_MSG);
String flagsStr = msgElem.getAttribute(MailConstants.A_FLAGS, null);
String[] tags = TagUtil.parseTags(msgElem, mbox, octxt);
String folderStr = msgElem.getAttribute(MailConstants.A_FOLDER);
boolean noICal = msgElem.getAttributeBool(MailConstants.A_NO_ICAL, false);
long date = msgElem.getAttributeLong(MailConstants.A_DATE, System.currentTimeMillis());
boolean filterSent = request.getAttributeBool(MailConstants.A_FILTER_SENT, false);
if (mLog.isDebugEnabled()) {
StringBuffer toPrint = new StringBuffer("<AddMsg ");
if (tags != null) {
toPrint.append(" tags=\"").append(TagUtil.encodeTags(tags)).append("\"");
}
if (folderStr != null) {
toPrint.append(" folder=\"").append(folderStr).append("\"");
}
toPrint.append(">");
mLog.debug(toPrint);
}
int folderId = -1;
Folder folder = null;
try {
folderId = new ItemId(folderStr, zsc).getId();
} catch (ServiceException e) {
}
if (folderId > 0) {
folder = mbox.getFolderById(octxt, folderId);
} else {
folder = mbox.getFolderByPath(octxt, folderStr);
folderId = folder.getId();
}
mLog.debug("folder = %s", folder.getName());
// check to see whether the entire message has been uploaded under separate cover
String attachment = msgElem.getAttribute(MailConstants.A_ATTACHMENT_ID, null);
ParseMimeMessage.MimeMessageData mimeData = new ParseMimeMessage.MimeMessageData();
Message msg;
try {
MimeMessage mm;
if (attachment != null) {
mm = SendMsg.parseUploadedMessage(zsc, attachment, mimeData);
} else {
mm = ParseMimeMessage.importMsgSoap(msgElem);
}
int flagsBitMask = Flag.toBitmask(flagsStr);
ParsedMessage pm = new ParsedMessage(mm, date, mbox.attachmentsIndexingEnabled());
if (filterSent && !DebugConfig.disableOutgoingFilter && folderId == MailSender.getSentFolderId(mbox) && (flagsBitMask & Flag.BITMASK_FROM_ME) != 0) {
List<ItemId> addedItemIds = RuleManager.applyRulesToOutgoingMessage(octxt, mbox, pm, folderId, noICal, flagsBitMask, tags, Mailbox.ID_AUTO_INCREMENT);
msg = addedItemIds.isEmpty() ? null : mbox.getMessageById(octxt, addedItemIds.get(0).getId());
} else {
DeliveryOptions dopt = new DeliveryOptions().setFolderId(folderId).setNoICal(noICal).setFlags(flagsBitMask).setTags(tags);
msg = mbox.addMessage(octxt, pm, dopt, null);
}
} catch (IOException ioe) {
throw ServiceException.FAILURE("Error While Delivering Message", ioe);
} finally {
// purge the messages fetched from other servers.
if (mimeData.fetches != null) {
FileUploadServlet.deleteUploads(mimeData.fetches);
}
}
// we can now purge the uploaded attachments
if (mimeData.uploads != null) {
FileUploadServlet.deleteUploads(mimeData.uploads);
}
Element response = zsc.createElement(MailConstants.ADD_MSG_RESPONSE);
if (msg != null) {
ToXML.encodeMessageSummary(response, ifmt, octxt, msg, null, GetMsgMetadata.SUMMARY_FIELDS);
}
return response;
}
use of com.zimbra.cs.mailbox.DeliveryOptions in project zm-mailbox by Zimbra.
the class SyncTest method conversations.
@Test
public void conversations() throws Exception {
Account acct = Provisioning.getInstance().get(Key.AccountBy.name, "test@zimbra.com");
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(acct);
mbox.beginTrackingSync();
// message and reply in inbox
int msgId = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
DeliveryOptions dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX).setConversationId(-msgId);
Message msg = mbox.addMessage(null, MailboxTestUtil.generateMessage("Re: test subject"), dopt, null);
int convId = msg.getConversationId();
Set<Integer> expectedDeletes = Sets.newHashSet(convId, msgId, msg.getId());
//initial sync
Element request = new Element.XMLElement(MailConstants.SYNC_REQUEST);
Map<String, Object> context = new HashMap<String, Object>();
context.put(SoapEngine.ZIMBRA_CONTEXT, new ZimbraSoapContext(AuthProvider.getAuthToken(acct), acct.getId(), SoapProtocol.Soap12, SoapProtocol.Soap12));
Element response = new Sync().handle(request, context);
String token = response.getAttribute(MailConstants.A_TOKEN);
Element rootFolder = response.getElement(MailConstants.E_FOLDER);
List<Element> subFolders = rootFolder.listElements(MailConstants.E_FOLDER);
boolean found = true;
for (Element subFolder : subFolders) {
if (subFolder.getAttributeInt(MailConstants.A_ID, 0) == Mailbox.ID_FOLDER_CONVERSATIONS) {
Element conversations = subFolder.getElement(MailConstants.E_CONV);
String ids = conversations.getAttribute(MailConstants.A_IDS);
String[] convIds = ids.split(",");
Assert.assertEquals(1, convIds.length);
Assert.assertEquals(convId + "", convIds[0]);
break;
}
}
Assert.assertTrue("found expected converation", found);
//delete original conv
mbox.delete(null, convId, MailItem.Type.CONVERSATION);
//new conv
msgId = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject 2"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX).setConversationId(-msgId);
convId = mbox.addMessage(null, MailboxTestUtil.generateMessage("Re: test subject 2"), dopt, null).getConversationId();
//delta sync
request = new Element.XMLElement(MailConstants.SYNC_REQUEST);
request.addAttribute(MailConstants.A_TOKEN, token);
response = new Sync().handle(request, context);
Element conv = response.getElement(MailConstants.E_CONV);
Assert.assertEquals(convId, conv.getAttributeInt(MailConstants.A_ID));
Element deleted = response.getElement(MailConstants.E_DELETED);
String ids = deleted.getAttribute(MailConstants.A_IDS);
String[] deletedIds = ids.split(",");
Assert.assertEquals(3, deletedIds.length);
for (String delete : deletedIds) {
Assert.assertTrue("id " + delete + " deleted", expectedDeletes.remove(Integer.valueOf(delete)));
}
Assert.assertTrue("all expected ids deleted", expectedDeletes.isEmpty());
}
use of com.zimbra.cs.mailbox.DeliveryOptions in project zm-mailbox by Zimbra.
the class SearchTest method mute.
@Test
public void mute() throws Exception {
Account acct = Provisioning.getInstance().getAccountById(MockProvisioning.DEFAULT_ACCOUNT_ID);
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(acct);
// setup: add a message
DeliveryOptions dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX).setFlags(Flag.BITMASK_UNREAD | Flag.BITMASK_MUTED);
Message msg = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), dopt, null);
Assert.assertTrue("root unread", msg.isUnread());
Assert.assertTrue("root muted", msg.isTagged(Flag.FlagInfo.MUTED));
// search for the conversation (normal)
Element request = new Element.XMLElement(MailConstants.SEARCH_REQUEST).addAttribute(MailConstants.A_SEARCH_TYPES, "conversation");
request.addAttribute(MailConstants.E_QUERY, "test", Element.Disposition.CONTENT);
Element response = new Search().handle(request, ServiceTestUtil.getRequestContext(acct));
List<Element> hits = response.listElements(MailConstants.E_CONV);
Assert.assertEquals("1 hit", 1, hits.size());
Assert.assertEquals("correct hit", msg.getConversationId(), hits.get(0).getAttributeLong(MailConstants.A_ID));
// search for the conversation (no muted items)
request.addAttribute(MailConstants.A_INCLUDE_TAG_MUTED, false);
response = new Search().handle(request, ServiceTestUtil.getRequestContext(acct));
hits = response.listElements(MailConstants.E_CONV);
Assert.assertTrue("no hits", hits.isEmpty());
}
use of com.zimbra.cs.mailbox.DeliveryOptions in project zm-mailbox by Zimbra.
the class AbstractBlobConsistencyCheckTest method missingBlobs.
@Test
public void missingBlobs() throws Exception {
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(MockProvisioning.DEFAULT_ACCOUNT_ID);
DeliveryOptions dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX);
int msgs = 10;
for (int i = 0; i < msgs; i++) {
mbox.addMessage(null, new ParsedMessage("From: test1-1@sub1.zimbra.com".getBytes(), false), dopt, null);
}
deleteAllBlobs();
BlobConsistencyChecker checker = getChecker();
Results results = checker.check(getVolumeIds(), mbox.getId(), true, false);
Assert.assertEquals(msgs, results.missingBlobs.size());
Assert.assertEquals(0, results.unexpectedBlobs.size());
Assert.assertEquals(0, results.usedBlobs.size());
Assert.assertEquals(0, results.incorrectSize.size());
Assert.assertEquals(0, results.incorrectModContent.size());
}
Aggregations