use of com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException in project zm-mailbox by Zimbra.
the class Mailbox method alterTag.
public void alterTag(OperationContext octxt, int[] itemIds, MailItem.Type type, String tagName, boolean addTag, TargetConstraint tcon) throws ServiceException {
AlterItemTag redoRecorder = new AlterItemTag(mId, itemIds, type, tagName, addTag, tcon);
boolean success = false;
try {
beginTransaction("alterTag", octxt, redoRecorder);
setOperationTargetConstraint(tcon);
Tag tag;
try {
tag = getTagByName(tagName);
} catch (NoSuchItemException nsie) {
if (tagName.startsWith(Tag.FLAG_NAME_PREFIX)) {
throw nsie;
}
Tag.NormalizedTags ntags = new NormalizedTags(this, new String[] { tagName }, addTag);
if (ntags.getTags().length == 0) {
success = true;
return;
}
tag = getTagByName(ntags.getTags()[0]);
}
alterTag(itemIds, type, tag, addTag);
success = true;
} finally {
endTransaction(success);
}
}
use of com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException in project zm-mailbox by Zimbra.
the class Mailbox method addMessageInternal.
private Message addMessageInternal(OperationContext octxt, ParsedMessage pm, int folderId, boolean noICal, int flags, String[] tags, int conversationId, String rcptEmail, Message.DraftInfo dinfo, CustomMetadata customData, DeliveryContext dctxt, StagedBlob staged) throws IOException, ServiceException {
assert lock.isWriteLockedByCurrentThread();
if (pm == null) {
throw ServiceException.INVALID_REQUEST("null ParsedMessage when adding message to mailbox " + mId, null);
}
if (Math.abs(conversationId) <= HIGHEST_SYSTEM_ID) {
conversationId = ID_AUTO_INCREMENT;
}
CreateMessage redoPlayer = (octxt == null ? null : (CreateMessage) octxt.getPlayer());
boolean needRedo = needRedo(octxt, redoPlayer);
boolean isRedo = redoPlayer != null;
Blob blob = dctxt.getIncomingBlob();
if (blob == null) {
throw ServiceException.FAILURE("Incoming blob not found.", null);
}
// make sure we're parsing headers using the target account's charset
pm.setDefaultCharset(getAccount().getPrefMailDefaultCharset());
// quick check to make sure we don't deliver 5 copies of the same message
String msgidHeader = pm.getMessageID();
boolean isSent = ((flags & Flag.BITMASK_FROM_ME) != 0);
if (!isRedo && msgidHeader != null && !isSent && mSentMessageIDs.containsKey(msgidHeader)) {
Integer sentMsgID = mSentMessageIDs.get(msgidHeader);
if (conversationId == ID_AUTO_INCREMENT) {
conversationId = getConversationIdFromReferent(pm.getMimeMessage(), sentMsgID.intValue());
ZimbraLog.mailbox.debug("duplicate detected but not deduped (%s); will try to slot into conversation %d", msgidHeader, conversationId);
}
}
// caller can't set system flags other than \Draft, \Sent and \Post
flags &= ~Flag.FLAGS_SYSTEM | Flag.BITMASK_DRAFT | Flag.BITMASK_FROM_ME | Flag.BITMASK_POST;
// caller can't specify non-message flags
flags &= Flag.FLAGS_GENERIC | Flag.FLAGS_MESSAGE;
String digest;
int msgSize;
try {
digest = blob.getDigest();
msgSize = (int) blob.getRawSize();
} catch (IOException e) {
throw ServiceException.FAILURE("Unable to get message properties.", e);
}
CreateMessage redoRecorder = new CreateMessage(mId, rcptEmail, pm.getReceivedDate(), dctxt.getShared(), digest, msgSize, folderId, noICal, flags, tags, customData);
StoreIncomingBlob storeRedoRecorder = null;
// strip out unread flag for internal storage (don't do this before redoRecorder initialization)
boolean unread = (flags & Flag.BITMASK_UNREAD) > 0;
flags &= ~Flag.BITMASK_UNREAD;
// "having attachments" is currently tracked via flags
if (pm.hasAttachments()) {
flags |= Flag.BITMASK_ATTACHED;
} else {
flags &= ~Flag.BITMASK_ATTACHED;
}
// priority is calculated from headers
flags &= ~(Flag.BITMASK_HIGH_PRIORITY | Flag.BITMASK_LOW_PRIORITY);
flags |= pm.getPriorityBitmask();
boolean isSpam = folderId == ID_FOLDER_SPAM;
boolean isDraft = (flags & Flag.BITMASK_DRAFT) != 0;
// draft replies get slotted in the same conversation as their parent, if possible
if (isDraft && !isRedo && conversationId == ID_AUTO_INCREMENT && dinfo != null && !Strings.isNullOrEmpty(dinfo.origId)) {
try {
ItemId iid = new ItemId(dinfo.origId, getAccountId());
if (iid.getId() > 0 && iid.belongsTo(this)) {
conversationId = getMessageById(octxt, iid.getId()).getConversationId();
}
} catch (ServiceException e) {
}
}
Message msg = null;
boolean success = false;
CustomMetadata.CustomMetadataList extended = MetadataCallback.preDelivery(pm);
if (customData != null) {
if (extended == null) {
extended = customData.asList();
} else {
extended.addSection(customData);
}
}
Threader threader = pm.getThreader(this);
String subject = pm.getNormalizedSubject();
try {
beginTransaction("addMessage", octxt, redoRecorder);
if (isRedo) {
rcptEmail = redoPlayer.getRcptEmail();
}
Tag.NormalizedTags ntags = new Tag.NormalizedTags(this, tags);
Folder folder = getFolderById(folderId);
// step 0: preemptively check for quota issues (actual update is done in Message.create)
if (!getAccount().isMailAllowReceiveButNotSendWhenOverQuota()) {
checkSizeChange(getSize() + staged.getSize());
}
// step 1: get an ID assigned for the new message
int messageId = getNextItemId(!isRedo ? ID_AUTO_INCREMENT : redoPlayer.getMessageId());
List<Conversation> mergeConvs = null;
if (isRedo) {
conversationId = redoPlayer.getConvId();
// fetch the conversations that were merged in as a result of the original delivery...
List<Integer> mergeConvIds = redoPlayer.getMergedConvIds();
mergeConvs = new ArrayList<Conversation>(mergeConvIds.size());
for (int mergeId : mergeConvIds) {
try {
mergeConvs.add(getConversationById(mergeId));
} catch (NoSuchItemException nsie) {
ZimbraLog.mailbox.debug("could not find merge conversation %d", mergeId);
}
}
}
// step 2: figure out where the message belongs
Conversation conv = null;
if (threader.isEnabled()) {
boolean isReply = pm.isReply();
if (conversationId != ID_AUTO_INCREMENT) {
try {
// fetch the requested conversation
// (we'll ensure that it's receiving new mail after the new message is added to it)
conv = getConversationById(conversationId);
ZimbraLog.mailbox.debug("fetched explicitly-specified conversation %d", conv.getId());
} catch (NoSuchItemException nsie) {
if (!isRedo) {
ZimbraLog.mailbox.debug("could not find explicitly-specified conversation %d", conversationId);
conversationId = ID_AUTO_INCREMENT;
}
}
} else if (!isRedo && !isSpam && (isReply || (!isSent && !subject.isEmpty()))) {
List<Conversation> matches = threader.lookupConversation();
if (matches != null && !matches.isEmpty()) {
// file the message into the largest conversation, then later merge any other matching convs
Collections.sort(matches, new MailItem.SortSizeDescending());
conv = matches.remove(0);
mergeConvs = matches;
}
}
}
if (conv != null && conv.isTagged(Flag.FlagInfo.MUTED)) {
// adding a message to a muted conversation marks it muted and read
unread = false;
flags |= Flag.BITMASK_MUTED;
}
// step 3: create the message and update the cache
// and if the message is also an invite, deal with the calendar item
Conversation convTarget = conv instanceof VirtualConversation ? null : conv;
if (convTarget != null) {
ZimbraLog.mailbox.debug(" placing message in existing conversation %d", convTarget.getId());
}
CalendarPartInfo cpi = pm.getCalendarPartInfo();
ZVCalendar iCal = null;
if (cpi != null && CalendarItem.isAcceptableInvite(getAccount(), cpi)) {
iCal = cpi.cal;
}
msg = Message.create(messageId, folder, convTarget, pm, staged, unread, flags, ntags, dinfo, noICal, iCal, extended);
redoRecorder.setMessageId(msg.getId());
// step 4: create a conversation for the message, if necessary
if (threader.isEnabled() && convTarget == null) {
if (conv == null && conversationId == ID_AUTO_INCREMENT) {
conv = VirtualConversation.create(this, msg);
ZimbraLog.mailbox.debug("placed message %d in vconv %d", msg.getId(), conv.getId());
redoRecorder.setConvFirstMsgId(-1);
} else {
Message[] contents = null;
VirtualConversation vconv = null;
if (!isRedo) {
vconv = (VirtualConversation) conv;
contents = (vconv == null ? new Message[] { msg } : new Message[] { vconv.getMessage(), msg });
} else {
// Executing redo.
int convFirstMsgId = redoPlayer.getConvFirstMsgId();
Message convFirstMsg = null;
// If there was a virtual conversation, then...
if (convFirstMsgId > 0) {
try {
convFirstMsg = getMessageById(octxt, redoPlayer.getConvFirstMsgId());
} catch (MailServiceException e) {
if (!MailServiceException.NO_SUCH_MSG.equals(e.getCode())) {
throw e;
}
// The first message of conversation may have been deleted
// by user between the time of original operation and redo.
// Handle the case by skipping the updating of its
// conversation ID.
}
// if it is still a standalone message.
if (convFirstMsg != null && convFirstMsg.getConversationId() < 0) {
contents = new Message[] { convFirstMsg, msg };
vconv = new VirtualConversation(this, convFirstMsg);
}
}
if (contents == null) {
contents = new Message[] { msg };
}
}
redoRecorder.setConvFirstMsgId(vconv != null ? vconv.getMessageId() : -1);
conv = createConversation(conversationId, contents);
if (vconv != null) {
ZimbraLog.mailbox.debug("removed vconv %d", vconv.getId());
vconv.removeChild(vconv.getMessage());
}
// associate the first message's reference hashes with the new conversation
if (contents.length == 2) {
threader.changeThreadingTargets(contents[0], conv);
}
}
} else {
// conversation feature turned off
redoRecorder.setConvFirstMsgId(-1);
}
redoRecorder.setConvId(conv != null && !(conv instanceof VirtualConversation) ? conv.getId() : -1);
// if we're threading by references, associate the new message's reference hashes with its conversation
if (!isSpam && !isDraft) {
threader.recordAddedMessage(conv);
}
if (conv != null && mergeConvs != null) {
redoRecorder.setMergedConversations(mergeConvs);
for (Conversation smaller : mergeConvs) {
ZimbraLog.mailbox.info("merging conversation %d for references threading", smaller.getId());
// try {
conv.merge(smaller);
// } catch (ServiceException e) {
// if (!e.getCode().equals(MailServiceException.NO_SUCH_MSG)) {
// throw e;
// }
// }
}
}
// conversations may have shifted, so the threader's cached state is now questionable
threader.reset();
// step 5: write the redolog entries
if (dctxt.getShared()) {
if (dctxt.isFirst() && needRedo) {
// Log entry in redolog for blob save. Blob bytes are logged in the StoreIncoming entry.
// Subsequent CreateMessage ops will reference this blob.
storeRedoRecorder = new StoreIncomingBlob(digest, msgSize, dctxt.getMailboxIdList());
storeRedoRecorder.start(getOperationTimestampMillis());
storeRedoRecorder.setBlobBodyInfo(blob.getFile());
storeRedoRecorder.log();
}
// Link to the file created by StoreIncomingBlob.
redoRecorder.setMessageLinkInfo(blob.getPath());
} else {
// Store the blob data inside the CreateMessage op.
redoRecorder.setMessageBodyInfo(blob.getFile());
}
// step 6: link to existing blob
MailboxBlob mblob = StoreManager.getInstance().link(staged, this, messageId, getOperationChangeID());
markOtherItemDirty(mblob);
// when we created the Message, we used the staged locator/size/digest;
// make sure that data actually matches the final blob in the store
msg.updateBlobData(mblob);
if (dctxt.getMailboxBlob() == null) {
// Set mailbox blob for in case we want to add the message to the
// message cache after delivery.
dctxt.setMailboxBlob(mblob);
}
// step 7: queue new message for indexing
index.add(msg);
success = true;
// step 8: send lawful intercept message
try {
Notification.getInstance().interceptIfNecessary(this, pm.getMimeMessage(), "add message", folder);
} catch (ServiceException e) {
ZimbraLog.mailbox.error("unable to send legal intercept message", e);
}
} finally {
if (storeRedoRecorder != null) {
if (success) {
storeRedoRecorder.commit();
} else {
storeRedoRecorder.abort();
}
}
endTransaction(success);
if (success) {
// Everything worked. Update the blob field in ParsedMessage
// so the next recipient in the multi-recipient case will link
// to this blob as opposed to saving its own copy.
dctxt.setFirst(false);
}
}
// step 8: remember the Message-ID header so that we can avoid receiving duplicates
if (isSent && !isRedo && msgidHeader != null) {
mSentMessageIDs.put(msgidHeader, msg.getId());
}
return msg;
}
use of com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException in project zm-mailbox by Zimbra.
the class Mailbox method getItemById.
MailItem getItemById(int id, MailItem.Type type, boolean fromDumpster) throws ServiceException {
if (fromDumpster) {
MailItem item = null;
try {
item = MailItem.getById(this, id, type, true);
if (item != null && !isVisibleInDumpster(item)) {
item = null;
}
} catch (NoSuchItemException e) {
}
// fault.
if (item == null) {
throw MailItem.noSuchItem(id, type);
}
return item;
}
// try the cache first
MailItem item = getCachedItem(Integer.valueOf(id), type);
if (item != null) {
return item;
}
// the tag and folder caches contain ALL tags and folders, so cache miss == doesn't exist
if (isCachedType(type)) {
throw MailItem.noSuchItem(id, type);
}
boolean virtualConv = false;
boolean cachedMsg = true;
boolean sameId = true;
if (id <= -FIRST_USER_ID) {
virtualConv = true;
ZimbraLog.mailbox.debug("getting virtual conversation");
// special-case virtual conversations
if (type != MailItem.Type.CONVERSATION && type != MailItem.Type.UNKNOWN) {
throw MailItem.noSuchItem(id, type);
}
Message msg = getCachedMessage(Integer.valueOf(-id));
if (msg == null) {
ZimbraLog.mailbox.debug("message not cached");
cachedMsg = false;
msg = getMessageById(-id);
}
int convId = msg.getConversationId();
if (msg.getConversationId() != id) {
ZimbraLog.mailbox.debug("message(%d) conv id(%d) != id(%d), getting parent", msg.getId(), msg.getConversationId(), id);
sameId = false;
item = msg.getParent();
if (item == null) {
ZimbraLog.mailbox.warn("got null parent for message id [%d] conv id [%d] (before condition [%d]) != id [%d]. equality? [%s:%s] in dumpster? [%s]", msg.getId(), msg.getConversationId(), convId, id, convId == id, msg.getConversationId() == id, msg.inDumpster());
}
} else {
ZimbraLog.mailbox.debug("returning normal virtual conv");
item = new VirtualConversation(this, msg);
}
} else {
// cache miss, so fetch from the database
item = MailItem.getById(this, id, type);
}
if (item == null) {
ZimbraLog.mailbox.warn("item is null for id [%d] in mailbox [%d]. Virtual conv? [%s] cachedMsg? [%s] sameId? [%s]", id, this.mId, virtualConv, cachedMsg, sameId);
throw MailItem.noSuchItem(id, type);
}
return item;
}
use of com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException in project zm-mailbox by Zimbra.
the class CreateDataSource method validateFolderId.
/**
* Confirms that the folder attribute specifies a valid folder id and is not
* within the subtree of another datasource
*/
static void validateFolderId(Account account, Mailbox mbox, Element eDataSource, DataSourceType dsType) throws ServiceException {
int folderId = eDataSource.getAttributeInt(MailConstants.A_FOLDER);
String id = eDataSource.getAttribute(MailConstants.A_ID, null);
try {
mbox.getFolderById(null, folderId);
} catch (NoSuchItemException e) {
throw ServiceException.INVALID_REQUEST("Invalid folder id: " + folderId, null);
}
for (DataSource ds : account.getAllDataSources()) {
if (id != null && ds.getId().equals(id))
continue;
try {
for (Folder fldr : mbox.getFolderById(null, ds.getFolderId()).getSubfolderHierarchy()) {
if (fldr.getId() == folderId)
if ((DataSourceType.pop3.equals(dsType)) && (DataSourceType.pop3.equals(ds.getType()))) {
// Allows unified inbox to work for more than one Pop3 datasource
} else {
throw ServiceException.INVALID_REQUEST("Folder location conflict: " + fldr.getPath(), null);
}
}
} catch (NoSuchItemException ignored) {
}
}
}
use of com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException in project zm-mailbox by Zimbra.
the class CalDavDataImport method applyRemoteItem.
private MailItem applyRemoteItem(RemoteItem remoteItem, Folder where) throws ServiceException, IOException {
if (!(remoteItem instanceof RemoteCalendarItem)) {
ZimbraLog.datasource.warn("applyRemoteItem: not a calendar item: %s", remoteItem);
return null;
}
RemoteCalendarItem item = (RemoteCalendarItem) remoteItem;
DataSource ds = getDataSource();
DataSourceItem dsItem = DbDataSource.getReverseMapping(ds, item.href);
OperationContext octxt = new OperationContext(mbox);
MailItem mi = null;
boolean isStale = false;
boolean isCreate = false;
if (dsItem.md == null && item.status != Status.deleted) {
dsItem.md = new Metadata();
dsItem.md.put(METADATA_KEY_TYPE, METADATA_TYPE_APPOINTMENT);
}
if (dsItem.itemId == 0) {
isStale = true;
isCreate = true;
} else {
String etag = dsItem.md.get(METADATA_KEY_ETAG, null);
try {
mi = mbox.getItemById(octxt, dsItem.itemId, MailItem.Type.UNKNOWN);
} catch (MailServiceException.NoSuchItemException se) {
ZimbraLog.datasource.warn("applyRemoteItem: calendar item not found: ", remoteItem);
}
if (item.etag == null) {
ZimbraLog.datasource.warn("No Etag returned for item %s", item.href);
isStale = true;
} else if (etag == null) {
ZimbraLog.datasource.warn("Empty etag for item %d", dsItem.itemId);
isStale = true;
} else {
isStale = !item.etag.equals(etag);
}
if (mi == null)
isStale = true;
}
if (item.status == Status.deleted) {
ZimbraLog.datasource.debug("Deleting appointment %s", item.href);
try {
mi = mbox.getItemById(octxt, item.itemId, MailItem.Type.UNKNOWN);
} catch (NoSuchItemException se) {
mi = null;
}
try {
mbox.delete(octxt, item.itemId, MailItem.Type.UNKNOWN);
} catch (ServiceException se) {
ZimbraLog.datasource.warn("Error deleting remotely deleted item %d (%s)", item.itemId, dsItem.remoteId);
}
} else if (isStale) {
ZimbraLog.datasource.debug("Updating stale appointment %s", item.href);
ZCalendar.ZVCalendar vcalendar;
SetCalendarItemData main = new SetCalendarItemData();
SetCalendarItemData[] exceptions = null;
CalDavClient client = null;
try {
client = getClient();
} catch (DavException e) {
throw ServiceException.FAILURE("error creating CalDAV client", e);
}
Appointment appt = client.getCalendarData(new Appointment(item.href, item.etag));
if (appt.data == null) {
ZimbraLog.datasource.warn("No appointment found at " + item.href);
return null;
}
dsItem.md.put(METADATA_KEY_ETAG, appt.etag);
try {
vcalendar = ZCalendar.ZCalendarBuilder.build(appt.data);
List<Invite> invites = Invite.createFromCalendar(mbox.getAccount(), null, vcalendar, true);
if (invites.size() > 1)
exceptions = new SetCalendarItemData[invites.size() - 1];
int pos = 0;
boolean first = true;
for (Invite i : invites) {
if (first) {
main.invite = i;
first = false;
} else {
SetCalendarItemData scid = new SetCalendarItemData();
scid.invite = i;
exceptions[pos++] = scid;
}
}
} catch (Exception e) {
ZimbraLog.datasource.warn("Error parsing appointment ", e);
return null;
}
mi = mbox.setCalendarItem(octxt, where.getId(), 0, null, main, exceptions, null, CalendarItem.NEXT_ALARM_KEEP_CURRENT);
dsItem.itemId = mi.getId();
dsItem.folderId = mi.getFolderId();
if (isCreate) {
DbDataSource.addMapping(ds, dsItem);
} else {
DbDataSource.updateMapping(ds, dsItem);
}
} else {
ZimbraLog.datasource.debug("Appointment up to date %s", item.href);
try {
mi = mbox.getItemById(octxt, dsItem.itemId, MailItem.Type.UNKNOWN);
} catch (NoSuchItemException se) {
// item not found. delete the mapping so it can be downloaded again if needed.
ArrayList<Integer> deletedIds = new ArrayList<Integer>();
deletedIds.add(dsItem.itemId);
DbDataSource.deleteMappings(ds, deletedIds);
}
}
return mi;
}
Aggregations