use of com.zimbra.cs.mailbox.MailItem.TargetConstraint in project zm-mailbox by Zimbra.
the class Mailbox method emptyFolder.
/**
* @param removeTopLevelFolder - delete folder after it has been emptied
*/
private void emptyFolder(OperationContext octxt, int folderId, boolean removeTopLevelFolder, boolean removeSubfolders, TargetConstraint tcon) throws ServiceException {
try {
if (emptyFolderOpLock.tryLock() || emptyFolderOpLock.tryLock(Provisioning.getInstance().getLocalServer().getEmptyFolderOpTimeout(), TimeUnit.SECONDS)) {
int batchSize = Provisioning.getInstance().getLocalServer().getMailEmptyFolderBatchSize();
ZimbraLog.mailbox.debug("Emptying folder %s, removeTopLevelFolder=%b, removeSubfolders=%b, batchSize=%d", folderId, removeTopLevelFolder, removeSubfolders, batchSize);
List<Integer> folderIds = new ArrayList<Integer>();
if (!removeSubfolders) {
folderIds.add(folderId);
} else {
List<Folder> folders = getFolderById(octxt, folderId).getSubfolderHierarchy();
for (Folder folder : folders) {
folderIds.add(folder.getId());
}
}
// Make sure that the user has the delete permission for all folders in the hierarchy.
for (int id : folderIds) {
if ((getEffectivePermissions(octxt, id, MailItem.Type.FOLDER) & ACL.RIGHT_DELETE) == 0) {
throw ServiceException.PERM_DENIED("not authorized to empty folder " + getFolderById(octxt, id).getPath());
}
}
int lastChangeID = octxt != null && octxt.change != -1 ? octxt.change : getLastChangeID();
// Delete the items in batches. (1000 items by default)
QueryParams params = new QueryParams();
params.setFolderIds(folderIds).setModifiedSequenceBefore(lastChangeID + 1).setRowLimit(batchSize);
boolean firstTime = true;
do {
// Give other threads a chance to use the mailbox between deletion batches.
if (firstTime) {
firstTime = false;
} else {
long sleepMillis = LC.empty_folder_batch_sleep_ms.longValue();
try {
ZimbraLog.mailbox.debug("emptyLargeFolder() sleeping for %dms", sleepMillis);
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
ZimbraLog.mailbox.warn("Sleep was interrupted", e);
}
}
} while (deletedBatchOfItemsInFolder(octxt, params, tcon));
if ((removeTopLevelFolder || removeSubfolders) && (!folderIds.isEmpty())) {
if (!removeTopLevelFolder) {
// 0th position is the folder being emptied
folderIds.remove(0);
}
if (!folderIds.isEmpty()) {
lock.lock();
try {
delete(octxt, ArrayUtil.toIntArray(folderIds), MailItem.Type.FOLDER, tcon, false, /* don't useEmptyForFolders */
null);
} finally {
lock.release();
}
}
}
} else {
ZimbraLog.mailbox.info("Empty large folder operation canceled because previous empty folder operation is in progress");
throw ServiceException.ALREADY_IN_PROGRESS("Empty Folder operation is in progress. Please wait for the operation to complete");
}
} catch (InterruptedException e) {
ZimbraLog.mailbox.warn("Empty folder operation interupted while acquiring emptyFolderOpLock", e);
throw ServiceException.ALREADY_IN_PROGRESS("Empty Folder operation is in progress. Please wait for the operation to complete");
} finally {
if (emptyFolderOpLock.isHeldByCurrentThread()) {
emptyFolderOpLock.unlock();
}
}
}
use of com.zimbra.cs.mailbox.MailItem.TargetConstraint in project zm-mailbox by Zimbra.
the class Mailbox method delete.
/**
* Delete the <tt>MailItem</tt>s with the given ids. If there is no <tt>MailItem</tt> for a given id, that id is
* ignored. If the id maps to an existing <tt>MailItem</tt> of an incompatible type, however, an error is thrown.
*
* @param octxt operation context or {@code null}
* @param itemIds item ids
* @param type item type or {@link MailItem.Type#UNKNOWN}
* @param tcon target constraint or {@code null}
* @param useEmptyForFolders empty folder {@code true} or {@code false}
* @param nonExistingItems object of {@link ArrayList} or {@code null}
*/
private void delete(OperationContext octxt, int[] itemIds, MailItem.Type type, TargetConstraint tcon, boolean useEmptyForFolders, List<Integer> nonExistingItems) throws ServiceException {
DeleteItem redoRecorder = new DeleteItem(mId, itemIds, type, tcon);
List<Integer> folderIds = Lists.newArrayList();
boolean success = false;
try {
beginTransaction("delete", octxt, redoRecorder);
setOperationTargetConstraint(tcon);
for (int id : itemIds) {
if (id == ID_AUTO_INCREMENT) {
continue;
}
MailItem item;
try {
item = getItemById(id, MailItem.Type.UNKNOWN);
} catch (NoSuchItemException nsie) {
if (nonExistingItems != null) {
nonExistingItems.add(id);
}
// trying to delete nonexistent things is A-OK!
continue;
}
// however, trying to delete messages and passing in a folder ID is not OK
if (!MailItem.isAcceptableType(type, item.getType())) {
throw MailItem.noSuchItem(id, type);
} else if (!checkItemChangeID(item) && item instanceof Tag) {
throw MailServiceException.MODIFY_CONFLICT();
}
if (useEmptyForFolders && MailItem.Type.FOLDER.equals(item.getType())) {
// removed later to allow batching delete of contents in there own transactions
folderIds.add(id);
} else {
// delete the item, but don't write the tombstone until we're finished...
item.delete(false);
}
}
// deletes have already been collected, so fetch the tombstones and write once
TypedIdList tombstones = collectPendingTombstones();
if (tombstones != null && !tombstones.isEmpty()) {
DbMailItem.writeTombstones(this, tombstones);
}
success = true;
} finally {
endTransaction(success);
}
for (Integer folderId : folderIds) {
emptyFolder(octxt, folderId, true, /* removeTopLevelFolder */
true, /* removeSubfolders */
tcon);
}
}
use of com.zimbra.cs.mailbox.MailItem.TargetConstraint in project zm-mailbox by Zimbra.
the class Mailbox method moveInternal.
private void moveInternal(OperationContext octxt, int[] itemIds, MailItem.Type type, int targetId, TargetConstraint tcon) throws ServiceException {
MoveItem redoRecorder = new MoveItem(mId, itemIds, type, targetId, tcon);
boolean success = false;
try {
beginTransaction("move", octxt, redoRecorder);
setOperationTargetConstraint(tcon);
Folder target = getFolderById(targetId);
MailItem[] items = getItemById(itemIds, type);
for (MailItem item : items) {
checkItemChangeID(item);
}
int oldUIDNEXT = target.getImapUIDNEXT();
boolean resetUIDNEXT = false;
for (MailItem item : items) {
// train the spam filter if necessary...
trainSpamFilter(octxt, item, target, "move");
// ...do the move...
boolean moved = item.move(target);
// ...and determine whether the move needs to cause an UIDNEXT change
if (moved && !resetUIDNEXT && isTrackingImap() && (item instanceof Conversation || item instanceof Message || item instanceof Contact)) {
resetUIDNEXT = true;
}
}
// if this operation should cause the target folder's UIDNEXT value to change but it hasn't yet, do it here
if (resetUIDNEXT && oldUIDNEXT == target.getImapUIDNEXT()) {
MoveItem redoPlayer = (MoveItem) currentChange().getRedoPlayer();
redoRecorder.setUIDNEXT(getNextItemId(redoPlayer == null ? ID_AUTO_INCREMENT : redoPlayer.getUIDNEXT()));
target.updateUIDNEXT();
}
success = true;
} finally {
endTransaction(success);
}
}
use of com.zimbra.cs.mailbox.MailItem.TargetConstraint in project zm-mailbox by Zimbra.
the class AlterItemTag method redo.
@Override
public void redo() throws Exception {
Mailbox mbox = MailboxManager.getInstance().getMailboxById(getMailboxId());
OperationContext octxt = getOperationContext();
TargetConstraint tcon = null;
if (mConstraint != null) {
try {
tcon = TargetConstraint.parseConstraint(mbox, mConstraint);
} catch (ServiceException e) {
mLog.warn(e);
}
}
if (mTagName == null && mTagId != 0) {
mTagName = TagUtil.tagIdToName(mbox, octxt, mTagId);
}
mbox.alterTag(octxt, mIds, type, mTagName, mTagged, tcon);
}
use of com.zimbra.cs.mailbox.MailItem.TargetConstraint in project zm-mailbox by Zimbra.
the class ItemActionHelper method MOVE.
/**
* Account relative path conversation move.
*/
public static List<ItemActionHelper> MOVE(OperationContext octxt, Mailbox mbox, SoapProtocol responseProto, List<Integer> ids, TargetConstraint tcon, String acctRelativePath) throws ServiceException {
List<ItemActionHelper> returnList = new ArrayList<>();
// First build all external account / data source root folder ids
Set<Integer> dsRootFolderIds = new HashSet<>();
if (!ids.isEmpty()) {
List<DataSource> dataSources = mbox.getAccount().getAllDataSources();
if (dataSources != null) {
for (DataSource ds : dataSources) {
int dsFolderId = ds.getFolderId();
if (dsFolderId != -1) {
dsRootFolderIds.add(dsFolderId);
}
}
}
}
for (int convId : ids) {
Integer rootFolderIdForConv = null;
for (Message msg : mbox.getMessagesByConversation(octxt, convId, SortBy.NONE, -1)) {
int rootFolderIdForThisMsg = AccountUtil.getRootFolderIdForItem(msg, mbox, dsRootFolderIds);
if (rootFolderIdForConv == null) {
rootFolderIdForConv = rootFolderIdForThisMsg;
} else if (rootFolderIdForConv != rootFolderIdForThisMsg) {
// this is conv spanning multiple accounts / data sources
rootFolderIdForConv = null;
break;
}
}
if (rootFolderIdForConv == null) {
continue;
}
Folder rootFolder = mbox.getFolderById(octxt, rootFolderIdForConv);
String rootFolderPath = rootFolder.getPath();
rootFolderPath = "/".equals(rootFolderPath) ? "" : rootFolderPath;
String targetFolderPath = rootFolderPath.concat(acctRelativePath.startsWith("/") ? acctRelativePath : "/" + acctRelativePath);
Folder targetFolder;
try {
targetFolder = mbox.getFolderByPath(octxt, targetFolderPath);
} catch (MailServiceException.NoSuchItemException e) {
targetFolder = mbox.createFolder(octxt, targetFolderPath, new Folder.FolderOptions().setDefaultView(MailItem.Type.MESSAGE));
}
returnList.add(MOVE(octxt, mbox, responseProto, Arrays.asList(convId), MailItem.Type.CONVERSATION, tcon, new ItemId(targetFolder)));
}
return returnList;
}
Aggregations