use of com.zimbra.cs.mailbox.Contact in project zm-mailbox by Zimbra.
the class AddressObject method create.
/**
* @param name Preferred DAV basename for the new item - including ".vcf".
* @param allowUpdate - PUTs are allowed to update a pre-existing item. POSTs to the containing collection are not.
*/
public static DavResource create(DavContext ctxt, String name, Collection where, VCard vcard, boolean allowUpdate) throws DavException, IOException {
if (vcard.fields.isEmpty()) {
throw new DavException.InvalidData(DavElements.CardDav.E_VALID_ADDRESS_DATA, String.format("Problem parsing %s data - no fields found.", DavProtocol.VCARD_CONTENT_TYPE));
}
String baseName = HttpUtil.urlUnescape(name);
DavResource res = null;
boolean useEtag = allowUpdate;
StringBuilder pathSB = new StringBuilder(where.getUri());
if (pathSB.charAt(pathSB.length() - 1) != '/') {
pathSB.append('/');
}
pathSB.append(baseName);
String path = pathSB.toString();
try {
if (name.endsWith(AddressObject.VCARD_EXTENSION)) {
name = name.substring(0, name.length() - 4);
name = HttpUtil.urlUnescape(name);
}
Mailbox mbox = where.getMailbox(ctxt);
vcard.fields.put(ContactConstants.A_vCardURL, name);
String uid = vcard.uid;
Account ownerAccount = Provisioning.getInstance().getAccountById(where.mOwnerId);
Contact c = null;
// check for existing contact
if (uid != null) {
vcard.fields.put(ContactConstants.A_vCardUID, uid);
AddressObject ao = getAddressObjectByUID(ctxt, uid, ctxt.getTargetMailbox().getAccount(), ctxt.getTargetMailbox().getFolderById(ctxt.getOperationContext(), where.mId), baseName);
if (ao != null) {
if (!allowUpdate) {
throw new DavException.CardDavUidConflict(// "An item with the same UID already exists in the address book", ao.getUri());
"An item with the same UID already exists in the address book", ao.getHref());
}
if (path.equals(ao.getUri())) {
res = ao;
} else {
throw new DavException.CardDavUidConflict(// "An item with the same UID already exists in the address book", ao.getUri());
"An item with the same UID already exists in the address book", ao.getHref());
}
} else {
res = UrlNamespace.getResourceAt(ctxt, ctxt.getUser(), path);
}
}
if (res == null) {
// Convert Apple contact group to Zimbra contact group.
constructContactGroupFromAppleXProps(ctxt, ownerAccount, vcard, null, where.getId());
c = mbox.createContact(ctxt.getOperationContext(), vcard.asParsedContact(), where.mId, null);
res = new AddressObject(ctxt, c);
res.mNewlyCreated = true;
} else {
String etag = null;
if (useEtag) {
etag = ctxt.getRequest().getHeader(DavProtocol.HEADER_IF_MATCH);
useEtag = (etag != null);
}
String itemEtag = res.getEtag();
if (useEtag && (etag != null) && !etag.equals(itemEtag)) {
throw new DavException("item etag does not match", HttpServletResponse.SC_PRECONDITION_FAILED);
}
String ifnonematch = ctxt.getRequest().getHeader(DavProtocol.HEADER_IF_NONE_MATCH);
if ((ifnonematch != null) && ifnonematch.equals("*")) {
throw new DavException("item already exists", HttpServletResponse.SC_PRECONDITION_FAILED);
}
MailItemResource mir = (MailItemResource) res;
constructContactGroupFromAppleXProps(ctxt, ownerAccount, vcard, (Contact) mir.getMailItem(ctxt), where.getId());
vcard.merge((Contact) mir.getMailItem(ctxt));
mbox.modifyContact(ctxt.getOperationContext(), mir.getId(), vcard.asParsedContact());
res = UrlNamespace.getResourceAt(ctxt, ctxt.getUser(), path);
}
} catch (ServiceException e) {
ZimbraLog.dav.info("Problem parsing VCARD", e);
throw new DavException.InvalidData(DavElements.CardDav.E_VALID_ADDRESS_DATA, String.format("cannot parse vcard - %s", e.getMessage()), e);
}
if (res == null) {
throw new DavException.InvalidData(DavElements.CardDav.E_VALID_ADDRESS_DATA, "cannot parse vcard");
}
return res;
}
use of com.zimbra.cs.mailbox.Contact in project zm-mailbox by Zimbra.
the class AddressObject method populateContactGroupAppleXProps.
private static void populateContactGroupAppleXProps(DavContext ctxt, Contact contact) {
if (contact.isContactGroup() == false) {
return;
}
ContactGroup contactGroup = null;
try {
contactGroup = ContactGroup.init(contact.get(ContactConstants.A_groupMember));
} catch (ServiceException e) {
ZimbraLog.dav.warn("can't get group members for Contact %d", contact.getId(), e);
}
ListMultimap<String, VCardParamsAndValue> xprops = contact.getUnknownVCardProps();
xprops.put(XABSKIND, new VCardParamsAndValue("group"));
if (contactGroup != null) {
try {
for (Member member : contactGroup.getMembers()) {
if (member.getType().equals(Member.Type.CONTACT_REF)) {
ItemId itemId = new ItemId(member.getValue(), contact.getAccount().getId());
if (itemId.belongsTo(contact.getAccount())) {
// make sure member belongs to the same collection as the group.
Contact c = getContactByUID(ctxt, itemId.toString(), contact.getAccount(), contact.getFolderId());
if (c != null) {
xprops.put(XABSMEMBER, new VCardParamsAndValue("urn:uuid:" + VCard.getUid(c)));
}
}
}
}
} catch (ServiceException e) {
ZimbraLog.dav.warn("can't create group members xprops for Contact %d", contact.getId(), e);
}
}
contact.setUnknownVCardProps(xprops);
}
use of com.zimbra.cs.mailbox.Contact in project zm-mailbox by Zimbra.
the class AddressObject method getMatchingHit.
/**
* There could be multiple contacts with the same UID from different collections.
* Due to an old CardDAV bug, it is also possible there may be more than one contact with the same UID in
* the same collection. Using "preferredBaseName" ensures that CardDAV clients can still do an
* update in that case.
*
* @param zqr
* @param folderId
* @param preferredBaseName If more than one item matches, prefer one matching this name - can be null
* Ignored if folderId < 0
* @return
* @throws ServiceException
*/
private static Contact getMatchingHit(ZimbraQueryResults zqr, int folderId, String preferredBaseName) throws ServiceException {
Contact item = null;
Contact firstMatchingItem = null;
while (zqr.hasNext()) {
ZimbraHit hit = zqr.getNext();
if (hit instanceof ContactHit) {
item = ((ContactHit) hit).getContact();
if (folderId < 0) {
break;
}
if (item.getFolderId() == folderId) {
if (firstMatchingItem == null) {
firstMatchingItem = item;
}
if (preferredBaseName != null) {
String contactBaseName = VCard.getUrl(item) + AddressObject.VCARD_EXTENSION;
if (!preferredBaseName.equals(contactBaseName)) {
item = null;
continue;
}
}
break;
}
item = null;
}
}
return item != null ? item : firstMatchingItem;
}
use of com.zimbra.cs.mailbox.Contact in project zm-mailbox by Zimbra.
the class ItemActionHelper method executeRemote.
private ItemActionResult executeRemote() throws ServiceException, IOException, HttpException {
Account target = Provisioning.getInstance().get(Key.AccountBy.id, mIidFolder.getAccountId());
AuthToken at = getAuthToken();
String pxyAuthToken = Provisioning.onLocalServer(target) ? null : at.getProxyAuthToken();
ZAuthToken zat = null;
if (pxyAuthToken == null) {
zat = at.toZAuthToken();
zat.resetProxyAuthToken();
} else {
zat = new ZAuthToken(pxyAuthToken);
}
ZMailbox.Options zoptions = new ZMailbox.Options(zat, AccountUtil.getSoapUri(target));
zoptions.setNoSession(true);
zoptions.setTargetAccount(target.getId());
zoptions.setTargetAccountBy(Key.AccountBy.id);
ZMailbox zmbx = ZMailbox.getMailbox(zoptions);
zmbx.setName(target.getName());
/* need this when logging in using another user's auth */
// check for mountpoints before going any further...
ZFolder zfolder = zmbx.getFolderById(mIidFolder.toString(mAuthenticatedAccount));
if (zfolder instanceof ZMountpoint) {
ItemId iidTarget = new ItemId(((ZMountpoint) zfolder).getCanonicalRemoteId(), mAuthenticatedAccount.getId());
if (!mIidFolder.equals(iidTarget)) {
mIidFolder = iidTarget;
if (++mHopCount > com.zimbra.soap.ZimbraSoapContext.MAX_HOP_COUNT)
throw MailServiceException.TOO_MANY_HOPS(mIidRequestedFolder);
schedule();
return ItemActionResult.create(mOperation);
}
}
boolean deleteOriginal = mOperation != Op.COPY;
String folderStr = mIidFolder.toString();
List<String> createdIds = new ArrayList<String>(itemIds.length);
List<String> nonExistentIds = new ArrayList<String>();
boolean toSpam = mIidFolder.getId() == Mailbox.ID_FOLDER_SPAM;
boolean toMailbox = !toSpam && mIidFolder.getId() != Mailbox.ID_FOLDER_TRASH;
for (MailItem item : mMailbox.getItemById(mOpCtxt, itemIds, type)) {
if (item == null) {
continue;
}
List<Message> msgs = null;
if (item instanceof Conversation) {
msgs = mMailbox.getMessagesByConversation(mOpCtxt, item.getId(), SortBy.DATE_ASC, -1);
}
if (deleteOriginal) {
if (msgs != null) {
// determine which of the conversation's component messages are actually able to be moved
boolean permDenied = false;
for (Iterator<Message> it = msgs.iterator(); it.hasNext(); ) {
Message msg = it.next();
if (!TargetConstraint.checkItem(mTargetConstraint, msg)) {
it.remove();
} else if (!canDelete(msg)) {
it.remove();
permDenied = true;
}
}
// stop here if no messages would be moved...
if (msgs.isEmpty()) {
if (permDenied) {
throw ServiceException.PERM_DENIED("cannot delete any messages in " + item.getType() + " " + item.getId());
}
// all messages were excluded by the TargetConstraint, so there's no failure...
continue;
}
} else {
if (!canDelete(item)) {
throw ServiceException.PERM_DENIED("cannot delete existing copy of " + item.getType() + " " + item.getId());
}
}
}
boolean fromSpam = item.inSpam();
if ((fromSpam && toMailbox) || (!fromSpam && toSpam)) {
try {
Folder dest = mMailbox.getFolderById(mOpCtxt, mIidFolder.getId());
SpamReport report = new SpamReport(toSpam, "remote " + mOperation, dest.getPath());
Folder source = mMailbox.getFolderById(mOpCtxt, item.getFolderId());
report.setSourceFolderPath(source.getPath());
report.setDestAccountName(target.getName());
SpamHandler.getInstance().handle(mOpCtxt, mMailbox, item.getId(), item.getType(), report);
} catch (OutOfMemoryError e) {
Zimbra.halt("out of memory", e);
} catch (Throwable t) {
ZimbraLog.mailop.info("could not train spam filter: " + new ItemId(item).toString(), t);
}
}
// since we can't apply tags to a remote object, hardwiring "tags" to null below...
String flags = (mOperation == Op.UPDATE && mFlags != null ? mFlags : item.getFlagString());
String name = ((mOperation == Op.RENAME || mOperation == Op.UPDATE) && mName != null ? mName : item.getName());
String createdId = null;
InputStream in = null;
switch(item.getType()) {
case CONTACT:
Contact ct = (Contact) item;
Map<String, ZMailbox.ZAttachmentInfo> attachments = new HashMap<String, ZMailbox.ZAttachmentInfo>();
for (Contact.Attachment att : ct.getAttachments()) {
String attachmentId = zmbx.uploadAttachment(att.getFilename(), att.getContent(), att.getContentType(), 0);
ZMailbox.ZAttachmentInfo info = new ZMailbox.ZAttachmentInfo().setAttachmentId(attachmentId);
attachments.put(att.getName(), info);
}
Map<String, String> fields = ct.getFields();
Map<String, String> members = new HashMap<String, String>();
for (String key : fields.keySet()) {
if (ContactConstants.A_groupMember.equals(key)) {
String memberEncoded = fields.get(key);
ContactGroup group = ContactGroup.init(memberEncoded);
for (Member m : group.getMembers()) {
members.put(m.getValue(), m.getType().getSoapEncoded());
}
break;
}
}
fields.remove(ContactConstants.A_groupMember);
ZContact contact = zmbx.createContact(folderStr, null, fields, attachments, members);
createdId = contact.getId();
createdIds.add(createdId);
break;
case MESSAGE:
try {
in = StoreManager.getInstance().getContent(item.getBlob());
createdId = zmbx.addMessage(folderStr, flags, (String) null, item.getDate(), in, item.getSize(), true);
} finally {
ByteUtil.closeStream(in);
}
createdIds.add(createdId);
break;
case VIRTUAL_CONVERSATION:
case CONVERSATION:
for (Message msg : msgs) {
flags = (mOperation == Op.UPDATE && mFlags != null ? mFlags : msg.getFlagString());
try {
in = StoreManager.getInstance().getContent(msg.getBlob());
createdId = zmbx.addMessage(folderStr, flags, (String) null, msg.getDate(), in, msg.getSize(), true);
} finally {
ByteUtil.closeStream(in);
}
createdIds.add(createdId);
}
break;
case DOCUMENT:
Document doc = (Document) item;
SoapHttpTransport transport = new SoapHttpTransport(zoptions.getUri());
try {
in = StoreManager.getInstance().getContent(doc.getBlob());
String uploadId = zmbx.uploadContentAsStream(name, in, doc.getContentType(), doc.getSize(), 4000, true);
// instead of using convenience method from ZMailbox
// we need to hand marshall the request and set the
// response protocol explicitly to what was requested
// from the client.
Element req = new XMLElement(MailConstants.SAVE_DOCUMENT_REQUEST);
Element edoc = req.addUniqueElement(MailConstants.E_DOC);
edoc.addAttribute(MailConstants.A_NAME, name);
edoc.addAttribute(MailConstants.A_FOLDER, folderStr);
edoc.addAttribute(MailConstants.A_FLAGS, flags);
Element upload = edoc.addNonUniqueElement(MailConstants.E_UPLOAD);
upload.addAttribute(MailConstants.A_ID, uploadId);
transport.setResponseProtocol(mResponseProtocol);
transport.setAuthToken(zat);
Element response = transport.invoke(req);
createdId = response.getElement(MailConstants.E_DOC).getAttribute(MailConstants.A_ID);
} finally {
ByteUtil.closeStream(in);
transport.shutdown();
}
createdIds.add(createdId);
break;
case APPOINTMENT:
case TASK:
CalendarItem cal = (CalendarItem) item;
// private calendar item may not be moved by non-owner unless permission was granted
if (!cal.isPublic()) {
boolean asAdmin = mOpCtxt != null ? mOpCtxt.isUsingAdminPrivileges() : false;
if (!cal.allowPrivateAccess(mAuthenticatedAccount, asAdmin))
throw ServiceException.PERM_DENIED("you do not have permission to move/copy a private calendar item from the current folder/mailbox");
}
// Move the item to remote mailbox using SetAppointmentRequest/SetTaskRequest.
QName qname = (item.getType() == MailItem.Type.TASK ? MailConstants.SET_TASK_REQUEST : MailConstants.SET_APPOINTMENT_REQUEST);
Element request = new Element.XMLElement(qname).addAttribute(MailConstants.A_FOLDER, folderStr).addAttribute(MailConstants.A_FLAGS, flags);
ToXML.encodeAlarmTimes(request, cal);
Invite invDefault = cal.getDefaultInviteOrNull();
// Takeover as organizer if we're doing a MOVE and source mailbox is the organizer.
// Don't takeover in a COPY operation.
boolean takeoverAsOrganizer = false;
boolean blockMove = false;
if (Op.MOVE.equals(mOperation)) {
Invite inv = invDefault;
if (inv == null) {
// no default invite; let's use the first invite
Invite[] invs = cal.getInvites();
if (invs != null && invs.length > 0)
inv = invs[0];
}
takeoverAsOrganizer = inv != null && inv.isOrganizer();
blockMove = takeoverAsOrganizer && inv.hasOtherAttendees();
}
if (blockMove) {
throw MailServiceException.INVALID_REQUEST("This operation requires change of organizer and it is not permitted", null);
}
if (invDefault != null) {
addCalendarPart(request.addUniqueElement(MailConstants.A_DEFAULT), cal, invDefault, zmbx, target, takeoverAsOrganizer);
}
for (Invite inv : cal.getInvites()) {
if (inv == null || inv == invDefault)
continue;
String elem = inv.isCancel() ? MailConstants.E_CAL_CANCEL : MailConstants.E_CAL_EXCEPT;
addCalendarPart(request.addNonUniqueElement(elem), cal, inv, zmbx, target, takeoverAsOrganizer);
}
ToXML.encodeCalendarReplies(request, cal);
createdId = zmbx.invoke(request).getAttribute(MailConstants.A_CAL_ID);
createdIds.add(createdId);
break;
default:
throw MailServiceException.CANNOT_COPY(item.getId());
}
try {
if (deleteOriginal && !mIdFormatter.formatItemId(item).equals(createdId)) {
List<Integer> nonExistentItems = new ArrayList<Integer>();
if (msgs == null) {
mMailbox.delete(mOpCtxt, new int[] { item.getId() }, item.getType(), null, nonExistentItems);
} else {
for (Message msg : msgs) {
mMailbox.delete(mOpCtxt, new int[] { msg.getId() }, msg.getType(), null, nonExistentItems);
}
}
for (Integer id : nonExistentItems) {
nonExistentIds.add(id.toString());
}
}
} catch (ServiceException e) {
if (e.getCode() != ServiceException.PERM_DENIED)
throw e;
// something funky happened permissions-wise between the getEffectivePermissions check and here...
ZimbraLog.misc.info("could not delete original item " + item.getId() + "; treating operation as a copy instead");
}
}
ItemActionResult result = ItemActionResult.create(mOperation);
if (Op.HARD_DELETE.equals(mOperation)) {
((DeleteActionResult) result).setNonExistentIds(nonExistentIds);
} else if (Op.COPY.equals(mOperation)) {
((CopyActionResult) result).setCreatedIds(createdIds);
}
for (int itemId : itemIds) {
result.appendSuccessId(Integer.toString(itemId));
}
return result;
}
use of com.zimbra.cs.mailbox.Contact in project zm-mailbox by Zimbra.
the class ModifyContact 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);
boolean replace = request.getAttributeBool(MailConstants.A_REPLACE, false);
boolean verbose = request.getAttributeBool(MailConstants.A_VERBOSE, true);
boolean wantImapUid = request.getAttributeBool(MailConstants.A_WANT_IMAP_UID, true);
boolean wantModSeq = request.getAttributeBool(MailConstants.A_WANT_MODIFIED_SEQUENCE, false);
Element cn = request.getElement(MailConstants.E_CONTACT);
ItemId iid = new ItemId(cn.getAttribute(MailConstants.A_ID), zsc);
String tagsAttr = cn.getAttribute(MailConstants.A_TAG_NAMES, null);
Contact contact = mbox.getContactById(octxt, iid.getId());
ParsedContact pc;
if (replace) {
Pair<Map<String, Object>, List<Attachment>> cdata = CreateContact.parseContact(cn, zsc, octxt, contact);
pc = new ParsedContact(cdata.getFirst(), cdata.getSecond());
} else {
pc = CreateContact.parseContactMergeMode(cn, zsc, octxt, contact);
}
if (CreateContact.needToMigrateDlist(zsc)) {
CreateContact.migrateFromDlist(pc);
}
mbox.modifyContact(octxt, iid.getId(), pc);
if (tagsAttr != null) {
String[] tags = TagUtil.decodeTags(tagsAttr);
if (tags != null) {
mbox.setTags(octxt, iid.getId(), MailItem.Type.CONTACT, MailItem.FLAG_UNCHANGED, tags);
}
}
Contact con = mbox.getContactById(octxt, iid.getId());
return makeResponse(zsc, octxt, con, verbose, wantImapUid, wantModSeq);
}
Aggregations