use of com.zimbra.cs.mailbox.Mountpoint in project zm-mailbox by Zimbra.
the class MailDocumentHandler method getProxyTarget.
protected static ItemId getProxyTarget(ZimbraSoapContext zsc, OperationContext octxt, ItemId iid, boolean checkMountpoint) throws ServiceException {
if (zsc == null || iid == null) {
return null;
}
Account acct = getRequestedAccount(zsc);
if (!iid.belongsTo(acct)) {
return iid;
}
if (!checkMountpoint || !Provisioning.onLocalServer(acct)) {
return null;
}
Mailbox mbox = getRequestedMailbox(zsc);
MailItem item = mbox.getItemById(octxt, iid.getId(), MailItem.Type.UNKNOWN);
if (!(item instanceof Mountpoint)) {
return null;
}
return ((Mountpoint) item).getTarget();
}
use of com.zimbra.cs.mailbox.Mountpoint in project zm-mailbox by Zimbra.
the class ToXML method encodeFolder.
public static Element encodeFolder(Element parent, ItemIdFormatter ifmt, OperationContext octxt, Folder folder, int fields, boolean exposeAclAccessKey) throws ServiceException {
if (folder instanceof SearchFolder) {
return encodeSearchFolder(parent, ifmt, (SearchFolder) folder, fields);
} else if (folder instanceof Mountpoint) {
return encodeMountpoint(parent, ifmt, octxt, (Mountpoint) folder, fields);
}
Element elem = parent.addNonUniqueElement(MailConstants.E_FOLDER);
encodeFolderCommon(elem, ifmt, folder, fields);
if (needToOutput(fields, Change.SIZE)) {
int deleted = folder.getDeletedCount();
elem.addAttribute(MailConstants.A_NUM, folder.getItemCount() - deleted);
if (deleted > 0) {
elem.addAttribute(MailConstants.A_IMAP_NUM, folder.getItemCount());
}
elem.addAttribute(MailConstants.A_SIZE, folder.getTotalSize());
elem.addAttribute(MailConstants.A_IMAP_MODSEQ, folder.getImapMODSEQ());
elem.addAttribute(MailConstants.A_IMAP_UIDNEXT, folder.getImapUIDNEXT());
}
if (needToOutput(fields, Change.URL)) {
String url = folder.getUrl();
if (!url.isEmpty() || fields != NOTIFY_FIELDS) {
// Note: in this case, a url on a folder object
// is not a url to the folder, but the url to another item that's
// external of the mail system. In most cases this is a 'synced' folder
// that is either RSS or a remote calendar object
elem.addAttribute(MailConstants.A_URL, HttpUtil.sanitizeURL(url));
}
}
Mailbox mbox = folder.getMailbox();
boolean remote = octxt != null && octxt.isDelegatedRequest(mbox);
boolean canAdminister = !remote;
boolean canDelete = canAdminister;
if (remote) {
// return effective permissions only for remote folders
String perms = encodeEffectivePermissions(folder, octxt);
elem.addAttribute(MailConstants.A_RIGHTS, perms);
canAdminister = perms != null && perms.indexOf(ACL.ABBR_ADMIN) != -1;
// Need to know retention policy if grantees can delete from a folder so clients can warn
// them when they try to delete something within the retention period
canDelete = canAdminister || (perms != null && perms.indexOf(ACL.ABBR_DELETE) != -1);
}
if (canAdminister) {
// return full ACLs for folders we have admin rights on
if (needToOutput(fields, Change.ACL)) {
if (fields != NOTIFY_FIELDS || folder.isTagged(Flag.FlagInfo.NO_INHERIT)) {
encodeACL(octxt, elem, folder.getEffectiveACL(), exposeAclAccessKey);
}
}
}
if (canDelete) {
if (needToOutput(fields, Change.RETENTION_POLICY)) {
RetentionPolicy rp = folder.getRetentionPolicy();
if (fields != NOTIFY_FIELDS || rp.isSet()) {
// Only output retention policy if it's being modified, or if we're returning all
// folder data and policy is set.
encodeRetentionPolicy(elem, RetentionPolicyManager.getInstance().getCompleteRetentionPolicy(folder.getAccount(), rp));
}
}
}
return elem;
}
use of com.zimbra.cs.mailbox.Mountpoint in project zm-mailbox by Zimbra.
the class UserServletUtil method resolveItem.
/*
* Parses the pathInfo, then returns MailItem corresponding to the resource in pathInfo.
*
* If the formatter does not require authentication, e.g. IfbFormatter,
* then the path resolution is skipped and returns null. That's because
* IfbFormatter does not internally use the resource identified in the URL.
* It gets the ifb information directly from the Mailbox.
*
* If the formatter declares that the authentication is not required, it's
* the formatter's responsibility to make sure the MailItems returned to
* the clients has gone through the access checks.
*
*/
public static MailItem resolveItem(UserServletContext context, boolean checkExtension) throws ServiceException {
if (context.formatter != null && !context.formatter.requiresAuth() && !FormatType.FREE_BUSY.equals(context.formatter.getType())) {
return null;
}
if (context.formatter != null && context.formatter.getType() == FormatType.MOBILE_CONFIG) {
return null;
}
Mailbox mbox = context.targetMailbox;
// special-case the fetch-by-IMAP-id option
if (context.imapId > 0) {
// fetch the folder from the path
Folder folder = mbox.getFolderByPath(context.opContext, context.itemPath);
// and then fetch the item from the "imap_id" query parameter
return mbox.getItemByImapId(context.opContext, context.imapId, folder.getId());
}
if (context.itemId != null) {
OperationContext opContext = null;
if (context.fromDumpster && context.isUsingAdminPrivileges()) {
// should have the admin rights to search dumpster
// use the authToken passed by invoker to generate a new one
opContext = new OperationContext(context.authToken);
} else {
// use the opContext generated from account
opContext = context.opContext;
}
context.target = mbox.getItemById(opContext, context.itemId.getId(), MailItem.Type.UNKNOWN, context.fromDumpster);
context.itemPath = context.target.getPath();
if (context.target instanceof Mountpoint || context.extraPath == null || context.extraPath.equals(""))
return context.target;
if (context.itemPath == null)
throw MailServiceException.NO_SUCH_ITEM("?id=" + context.itemId + "&name=" + context.extraPath);
context.target = null;
context.itemId = null;
}
if (context.extraPath != null && !context.extraPath.equals("")) {
context.itemPath = (context.itemPath + '/' + context.extraPath).replaceAll("//+", "/");
context.extraPath = null;
}
if (FormatType.FREE_BUSY.equals(context.format) || FormatType.IFB.equals(context.format)) {
try {
// Do the get as mailbox owner to circumvent ACL system.
context.target = mbox.getItemByPath(null, context.itemPath);
} catch (ServiceException e) {
if (!(e instanceof NoSuchItemException))
throw e;
}
} else {
// first, try the full requested path
ServiceException failure = null;
try {
context.target = mbox.getItemByPath(context.opContext, context.itemPath);
} catch (ServiceException e) {
if (!(e instanceof NoSuchItemException) && !e.getCode().equals(ServiceException.PERM_DENIED))
throw e;
failure = e;
}
if (context.target == null) {
// then we need to proxy the request to the sharer's mailbox.
try {
// to search for the mountpoint we use admin rights on the user's mailbox.
// this is done so that MailItems in the mountpoint can be resolved
// according to the ACL stored in the owner's Folder. when a mountpoint
// is found, then the request is proxied to the owner's mailbox host,
// and the current requestor's credential is used to validate against
// the ACL.
Pair<Folder, String> match = mbox.getFolderByPathLongestMatch(null, Mailbox.ID_FOLDER_USER_ROOT, context.itemPath);
Folder reachable = match.getFirst();
if (reachable instanceof Mountpoint) {
context.target = reachable;
context.itemPath = reachable.getPath();
context.extraPath = match.getSecond();
}
} catch (ServiceException e) {
}
}
if (context.target == null) {
// if they asked for something like "calendar.csv" (where "calendar" was the folder name), try again minus the extension
int dot = context.itemPath.lastIndexOf('.'), slash = context.itemPath.lastIndexOf('/');
if (checkExtension && context.format == null && dot != -1 && dot > slash) {
/* if path == /foo/bar/baz.html, then
* format -> html
* path -> /foo/bar/baz */
String unsuffixedPath = context.itemPath.substring(0, dot);
try {
context.format = FormatType.fromString(context.itemPath.substring(dot + 1));
if (context.format != null) {
context.formatter = FormatterFactory.mFormatters.get(context.format);
}
if (context.formatter != null && !context.formatter.requiresAuth()) {
// Do the get as mailbox owner to circumvent ACL system.
context.target = mbox.getItemByPath(null, unsuffixedPath);
} else {
context.target = mbox.getItemByPath(context.opContext, unsuffixedPath);
}
context.itemPath = unsuffixedPath;
} catch (ServiceException e) {
}
}
}
if (context.target == null)
throw failure;
}
return context.target;
}
use of com.zimbra.cs.mailbox.Mountpoint in project zm-mailbox by Zimbra.
the class ItemId method groupFoldersByAccount.
// groups folders by account id
// value is list of folder id integers
// mountpoints in local account are fully resolved to real folder in target account
public static Map<String, List<Integer>> groupFoldersByAccount(OperationContext octxt, Mailbox mbox, List<ItemId> folderIids) throws ServiceException {
Map<String, List<Integer>> foldersMap = new HashMap<String, List<Integer>>();
for (ItemId iidFolder : folderIids) {
String targetAccountId = iidFolder.getAccountId();
int folderId = iidFolder.getId();
try {
if (mbox.getAccountId().equals(targetAccountId)) {
boolean isMountpoint = true;
int hopCount = 0;
// resolve local mountpoint to a real folder; deal with possible mountpoint chain
while (isMountpoint && hopCount < ZimbraSoapContext.MAX_HOP_COUNT) {
Folder folder = mbox.getFolderById(octxt, folderId);
isMountpoint = folder instanceof Mountpoint;
if (isMountpoint) {
Mountpoint mp = (Mountpoint) folder;
folderId = mp.getRemoteId();
if (!mp.isLocal()) {
// done resolving if pointing to a different account
targetAccountId = mp.getOwnerId();
Account targetAcct = Provisioning.getInstance().get(Key.AccountBy.id, targetAccountId);
if (targetAcct == null) {
throw MailServiceException.NO_SUCH_MOUNTPOINT(mp.getId(), mp.getOwnerId(), mp.getRemoteId(), AccountServiceException.NO_SUCH_ACCOUNT(targetAccountId));
}
break;
}
hopCount++;
}
}
if (hopCount >= ZimbraSoapContext.MAX_HOP_COUNT)
throw MailServiceException.TOO_MANY_HOPS(iidFolder);
}
List<Integer> folderList = foldersMap.get(targetAccountId);
if (folderList == null) {
folderList = new ArrayList<Integer>();
foldersMap.put(targetAccountId, folderList);
}
folderList.add(folderId);
} catch (ServiceException e) {
String ecode = e.getCode();
ItemIdFormatter ifmt = new ItemIdFormatter(targetAccountId, targetAccountId, false);
if (ecode.equals(ServiceException.PERM_DENIED)) {
// share permission was revoked
ZimbraLog.calendar.warn("Ignorable permission error %s", ifmt.formatItemId(folderId), e);
} else if (ecode.equals(MailServiceException.NO_SUCH_FOLDER)) {
// shared calendar folder was deleted by the owner
ZimbraLog.calendar.warn("Ignoring deleted folder %s", ifmt.formatItemId(folderId));
} else {
throw e;
}
}
}
return foldersMap;
}
use of com.zimbra.cs.mailbox.Mountpoint in project zm-mailbox by Zimbra.
the class SoapSession method putQueuedNotifications.
/**
* Write a single instance of the PendingLocalModifications structure into the
* passed-in <ctxt> block.
*/
protected void putQueuedNotifications(Mailbox mbox, QueuedNotifications ntfn, Element parent, ZimbraSoapContext zsc) {
// create the base "notify" block: <notify seq="6"/>
Element eNotify = parent.addNonUniqueElement(ZimbraNamespace.E_NOTIFY);
if (ntfn.getSequence() > 0) {
eNotify.addAttribute(HeaderConstants.A_SEQNO, ntfn.getSequence());
}
OperationContext octxt = null;
try {
octxt = DocumentHandler.getOperationContext(zsc, this);
} catch (ServiceException e) {
ZimbraLog.session.warn("error fetching operation context for: " + zsc.getAuthtokenAccountId(), e);
return;
}
boolean debug = ZimbraLog.session.isDebugEnabled();
PendingLocalModifications pms = ntfn.mMailboxChanges;
RemoteNotifications rns = ntfn.mRemoteChanges;
Element eDeleted = eNotify.addUniqueElement(ZimbraNamespace.E_DELETED);
StringBuilder deletedIds = new StringBuilder();
if (pms != null && pms.deleted != null && pms.deleted.size() > 0) {
for (ModificationKey mkey : pms.deleted.keySet()) {
addDeletedNotification(mkey, deletedIds);
}
}
if (rns != null && rns.deleted != null) {
deletedIds.append(deletedIds.length() == 0 ? "" : ",").append(rns.deleted);
}
boolean hasLocalCreates = pms != null && pms.created != null && !pms.created.isEmpty();
boolean hasRemoteCreates = rns != null && rns.created != null && !rns.created.isEmpty();
boolean hasLocalModifies = pms != null && pms.modified != null && !pms.modified.isEmpty();
boolean hasRemoteModifies = rns != null && rns.modified != null && !rns.modified.isEmpty();
if (SoapTransport.NotificationFormat.valueOf(zsc.getNotificationFormat()) == SoapTransport.NotificationFormat.IMAP) {
try {
AccountWithModifications info = new AccountWithModifications(zsc.getAuthtokenAccountId(), mbox.getLastChangeID());
Map<Integer, PendingFolderModifications> folderMods = PendingModifications.encodeIMAPFolderModifications(pms);
info.setPendingFolderModifications(folderMods.values());
eNotify.addUniqueElement(JaxbUtil.jaxbToElement(info, eNotify.getFactory()));
} catch (ContainerException | ServiceException e) {
ZimbraLog.session.error("Failed to encode IMAP notifications for a SOAP session ", e);
}
}
if (hasLocalCreates || hasRemoteCreates) {
Element eCreated = eNotify.addUniqueElement(ZimbraNamespace.E_CREATED);
if (hasLocalCreates) {
for (BaseItemInfo item : pms.created.values()) {
if (item instanceof MailItem) {
MailItem mi = (MailItem) item;
ItemIdFormatter ifmt = new ItemIdFormatter(mAuthenticatedAccountId, mi.getMailbox(), false);
try {
Element elem = ToXML.encodeItem(eCreated, ifmt, octxt, mi, ToXML.NOTIFY_FIELDS);
// special-case notifications for new mountpoints in the authenticated user's mailbox
if (item instanceof Mountpoint && mbox == mi.getMailbox()) {
Map<ItemId, Pair<Boolean, Element>> mountpoints = new HashMap<ItemId, Pair<Boolean, Element>>(2);
expandLocalMountpoint(octxt, (Mountpoint) mi, eCreated.getFactory(), mountpoints);
expandRemoteMountpoints(octxt, zsc, mountpoints);
transferMountpointContents(elem, octxt, mountpoints);
}
} catch (ServiceException e) {
ZimbraLog.session.warn("error encoding item " + mi.getId(), e);
return;
}
}
}
// sanity-check the returned element
if (!eCreated.hasChildren() && debug) {
ZimbraLog.session.debug("no serialied creates for item set: %s", pms.created.keySet());
}
}
if (hasRemoteCreates) {
if (debug) {
ZimbraLog.session.debug("adding %d proxied creates", rns.created.size());
}
for (Element elt : rns.created) {
if (encodingMatches(parent, elt)) {
eCreated.addElement(elt.clone().detach());
} else {
ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
}
}
}
}
ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
if (hasLocalModifies || hasRemoteModifies) {
Element eModified = eNotify.addUniqueElement(ZimbraNamespace.E_MODIFIED);
if (hasLocalModifies) {
for (Change chg : pms.modified.values()) {
if (chg.why != 0 && chg.what instanceof MailItem) {
MailItem item = (MailItem) chg.what;
try {
Element elt = ToXML.encodeItem(eModified, ifmt, octxt, item, chg.why);
if (elt == null) {
ModificationKey mkey = new PendingLocalModifications.ModificationKey(item);
addDeletedNotification(mkey, deletedIds);
if (debug) {
ZimbraLog.session.debug("marking nonserialized item as a delete: %s", mkey);
}
}
} catch (ServiceException e) {
ZimbraLog.session.warn("error encoding item " + item.getId(), e);
return;
}
} else if (chg.why != 0 && chg.what instanceof Mailbox) {
ToXML.encodeMailbox(eModified, octxt, (Mailbox) chg.what, chg.why);
}
}
// sanity-check the returned element
if (!eModified.hasChildren() && debug) {
ZimbraLog.session.debug("no serialied modifies for item set: %s", pms.modified.keySet());
}
}
if (hasRemoteModifies) {
if (debug) {
ZimbraLog.session.debug("adding %d proxied modifies", rns.modified.size());
}
for (Element elt : rns.modified) {
if (encodingMatches(parent, elt)) {
eModified.addElement(elt.clone().detach());
} else {
ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
}
}
}
}
if (rns != null && rns.activities != null && !rns.activities.isEmpty()) {
for (Element elt : rns.activities) {
if (encodingMatches(parent, elt)) {
eNotify.addElement(elt.clone().detach());
} else {
ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
}
}
}
putExtraNotifications(ntfn, eNotify, ifmt);
if (deletedIds == null || deletedIds.length() == 0) {
eDeleted.detach();
} else {
eDeleted.addAttribute(A_ID, deletedIds.toString());
}
}
Aggregations