use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class Notification method notifyIfNecessary.
/**
* If the recipient's account is set up for email notification, sends a notification
* message to the user's notification address.
*/
private void notifyIfNecessary(Account account, Message msg, String rcpt) throws MessagingException, ServiceException {
// Does user have new mail notification turned on
boolean notify = account.getBooleanAttr(Provisioning.A_zimbraPrefNewMailNotificationEnabled, false);
if (!notify) {
return;
}
// Validate notification address
String destination = account.getAttr(Provisioning.A_zimbraPrefNewMailNotificationAddress);
if (destination == null) {
nfailed("destination not set", null, rcpt, msg, null);
return;
}
try {
new JavaMailInternetAddress(destination);
} catch (AddressException ae) {
nfailed("invalid destination", destination, rcpt, msg, ae);
return;
}
// filter rules, we assume it's not interesting.
if (msg.inSpam()) {
nfailed("in spam", destination, rcpt, msg);
return;
}
try {
if (msg.inTrash()) {
nfailed("in trash", destination, rcpt, msg);
return;
}
} catch (ServiceException e) {
nfailed("call to Message.inTrash() failed", destination, rcpt, msg, e);
return;
}
// If precedence is bulk or junk
MimeMessage mm = msg.getMimeMessage();
String[] precedence = mm.getHeader("Precedence");
if (hasPrecedence(precedence, "bulk")) {
nfailed("precedence bulk", destination, rcpt, msg);
return;
}
if (hasPrecedence(precedence, "junk")) {
nfailed("precedence junk", destination, rcpt, msg);
return;
}
// Check for mail loop
String[] autoSubmittedHeaders = mm.getHeader("Auto-Submitted");
if (autoSubmittedHeaders != null) {
for (int i = 0; i < autoSubmittedHeaders.length; i++) {
String headerValue = autoSubmittedHeaders[i].toLowerCase();
if (headerValue.indexOf("notification") != -1) {
nfailed("detected a mail loop", destination, rcpt, msg);
return;
}
}
}
// Send the message
try {
Domain domain = Provisioning.getInstance().getDomain(account);
Session smtpSession = JMSession.getSmtpSession(domain);
// Assemble message components
MimeMessage out = assembleNotificationMessage(account, msg, rcpt, destination, smtpSession);
if (out == null) {
return;
}
String envFrom = "<>";
try {
if (!Provisioning.getInstance().getConfig().getBooleanAttr(Provisioning.A_zimbraAutoSubmittedNullReturnPath, true)) {
envFrom = account.getName();
}
} catch (ServiceException se) {
ZimbraLog.mailbox.warn("error encoutered looking up return path configuration, using null return path instead", se);
}
smtpSession.getProperties().setProperty("mail.smtp.from", envFrom);
Transport.send(out);
MailSender.logMessage(out, out.getAllRecipients(), envFrom, smtpSession.getProperties().getProperty("mail.smtp.host"), String.valueOf(msg.getId()), null, null, "notify");
} catch (MessagingException me) {
nfailed("send failed", destination, rcpt, msg, me);
}
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class Notification method interceptIfNecessary.
/**
* If <tt>zimbraInterceptAddress</tt> is specified, sends a message to that
* address with the given message attached.
*
* @param operation name of the operation being performed (send, add message, save draft, etc.)
* @param folder the folder that the message was filed into, or <tt>null</tt>
*/
void interceptIfNecessary(Mailbox mbox, MimeMessage msg, String operation, Folder folder) throws ServiceException {
// Don't do anything if intercept is turned off.
Account account = mbox.getAccount();
String[] interceptAddresses = account.getMultiAttr(Provisioning.A_zimbraInterceptAddress);
if (interceptAddresses.length == 0) {
return;
}
for (String interceptAddress : interceptAddresses) {
try {
ZimbraLog.mailbox.info("Sending intercept of message %s to %s.", msg.getMessageID(), interceptAddress);
// Fill templates
String folderName = "none";
String folderId = "none";
if (folder != null) {
folderName = folder.getName();
folderId = Integer.toString(folder.getId());
}
Map<String, String> vars = new HashMap<String, String>();
vars.put("ACCOUNT_DOMAIN", getDomain(account.getName()));
vars.put("ACCOUNT_ADDRESS", account.getName());
vars.put("MESSAGE_SUBJECT", Mime.getSubject(msg));
vars.put("OPERATION", operation);
vars.put("FOLDER_NAME", folderName);
vars.put("FOLDER_ID", folderId);
vars.put("NEWLINE", "\r\n");
String from = StringUtil.fillTemplate(account.getAttr(Provisioning.A_zimbraInterceptFrom), vars);
String subject = StringUtil.fillTemplate(account.getAttr(Provisioning.A_zimbraInterceptSubject), vars);
String bodyText = StringUtil.fillTemplate(account.getAttr(Provisioning.A_zimbraInterceptBody), vars);
// Assemble outgoing message
MimeMessage attached = msg;
boolean headersOnly = account.getBooleanAttr(Provisioning.A_zimbraInterceptSendHeadersOnly, false);
if (headersOnly) {
attached = new MimeMessageWithId(msg.getMessageID());
Enumeration e = msg.getAllHeaderLines();
while (e.hasMoreElements()) {
attached.addHeaderLine((String) e.nextElement());
}
attached.setContent("", msg.getContentType());
attached.saveChanges();
}
SMTPMessage out = AccountUtil.getSmtpMessageObj(account);
out.setHeader("Auto-Submitted", "auto-replied (zimbra; intercept)");
InternetAddress address = new JavaMailInternetAddress(from);
out.setFrom(address);
address = new JavaMailInternetAddress(interceptAddress);
out.setRecipient(javax.mail.Message.RecipientType.TO, address);
String charset = getCharset(account, subject);
out.setSubject(subject, charset);
charset = getCharset(account, bodyText);
MimeMultipart multi = new ZMimeMultipart();
// Add message body
MimeBodyPart part = new ZMimeBodyPart();
part.setText(bodyText, charset);
multi.addBodyPart(part);
// Add original message
MimeBodyPart part2 = new ZMimeBodyPart();
part2.setContent(attached, MimeConstants.CT_MESSAGE_RFC822);
multi.addBodyPart(part2);
out.setContent(multi);
String envFrom = "<>";
out.setEnvelopeFrom(envFrom);
out.saveChanges();
Transport.send(out);
// clean up after ourselves...
multi.removeBodyPart(part2);
} catch (MessagingException e) {
ZimbraLog.lmtp.warn("Unable to send intercept message to %s.", interceptAddress, e);
}
}
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class SendInviteReply method handle.
@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Mailbox mbox = getRequestedMailbox(zsc);
Account acct = getRequestedAccount(zsc);
Account authAcct = getAuthenticatedAccount(zsc);
boolean isAdmin = zsc.isUsingAdminPrivileges();
OperationContext octxt = getOperationContext(zsc, context);
boolean onBehalfOf = isOnBehalfOfRequest(zsc);
ItemId iid = new ItemId(request.getAttribute(MailConstants.A_ID), zsc);
int compNum = (int) request.getAttributeLong(MailConstants.A_CAL_COMPONENT_NUM);
String verbStr = request.getAttribute(MailConstants.A_VERB);
Verb verb = CalendarMailSender.parseVerb(verbStr);
boolean isDecline = CalendarMailSender.VERB_DECLINE.equals(verb);
boolean updateOrg = request.getAttributeBool(MailConstants.A_CAL_UPDATE_ORGANIZER, true);
// Get the identity/persona being used in the reply. It is set at the request level, but
// let's also look for it in the <m> child element too, because that is the precedent in
// the SendMsg request. For SendInviteReply we have to insist it at request level because
// <m> is an optional element.
String identityId = request.getAttribute(MailConstants.A_IDENTITY_ID, null);
if (identityId == null) {
Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
if (msgElem != null)
identityId = msgElem.getAttribute(MailConstants.A_IDENTITY_ID, null);
}
Element response = getResponseElement(zsc);
boolean intendedForMe = true;
Invite oldInv = null;
int calItemId;
int inviteMsgId;
CalendarItem calItem = null;
boolean wasInTrash = false;
String stat = null;
// calendar item (id="aaaa-nnnn") --- work in both cases
if (iid.hasSubpart()) {
// directly accepting the calendar item
calItemId = iid.getId();
inviteMsgId = iid.getSubpartId();
calItem = safeGetCalendarItemById(mbox, octxt, iid.getId());
if (calItem == null)
throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
oldInv = calItem.getInvite(inviteMsgId, compNum);
} else {
// accepting the message: go find the calendar item and then the invite
inviteMsgId = iid.getId();
Message msg = mbox.getMessageById(octxt, inviteMsgId);
Message.CalendarItemInfo info = msg.getCalendarItemInfo(compNum);
if (info == null)
throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
String intendedFor = msg.getCalendarIntendedFor();
Account intendedAcct = null;
if (intendedFor != null) {
try {
InternetAddress intendedForAddr = new JavaMailInternetAddress(intendedFor);
intendedAcct = Provisioning.getInstance().get(AccountBy.name, intendedForAddr.getAddress());
} catch (AddressException e) {
throw ServiceException.INVALID_REQUEST("The intended account " + intendedFor + " is invalid", e);
}
if (intendedAcct == null) {
throw ServiceException.INVALID_REQUEST("The intended account " + intendedFor + " was not found", null);
}
// Special case: intended account = me.
if (intendedAcct.equals(mbox.getAccount()))
intendedAcct = null;
else
intendedForMe = false;
}
if (intendedAcct != null) {
// trace logging: let's just indicate we're replying to a remote appointment
ZimbraLog.calendar.info("<SendInviteReply> (remote mbox) id=%s, verb=%s, notifyOrg=%s", new ItemIdFormatter(zsc).formatItemId(iid), verb.toString(), Boolean.toString(updateOrg));
// Replying to a remote appointment
calItem = null;
calItemId = 0;
ZMailbox zmbx = getRemoteZMailbox(octxt, authAcct, intendedAcct);
// Try to add the appointment to remote mailbox.
AddInviteResult addInviteResult = sendAddInvite(zmbx, octxt, msg);
if (addInviteResult == null)
throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
// Forward the reply request.
remoteSendInviteReply(zmbx, request, addInviteResult);
} else {
// Replying to a local appointment
if (info.getInvite() != null) {
calItem = mbox.getCalendarItemByUid(octxt, info.getInvite().getUid());
wasInTrash = calItem != null && calItem.inTrash();
if (calItem != null && !wasInTrash) {
Invite newInv = info.getInvite();
// If appointment exists, check if our invite has been outdated.
Invite curr = calItem.getInvite(newInv.getRecurId());
if (curr != null && !newInv.isSameOrNewerVersion(curr))
throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
}
Invite inv = info.getInvite().newCopy();
Invite.setDefaultAlarm(inv, acct);
inv.setMailItemId(inviteMsgId);
// (TODO: Is it better to delete the existing appointment/instance when declining?)
if (calItem != null || !isDecline) {
// Add the invite. This will either create or update the appointment.
int folder;
boolean untrashing = wasInTrash && !isDecline;
if (calItem == null || untrashing) {
// If appointment/task doesn't exist, create in default folder.
// If it exists but is in Trash and is not a decline, move it out of Trash.
// If it's in trash and we're declining, leave it in trash.
folder = inv.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
} else {
folder = calItem.getFolderId();
}
ParsedMessage pm = new ParsedMessage(msg.getMimeMessage(false), false);
AddInviteData aid = mbox.addInvite(octxt, inv, folder, pm, false, untrashing, true);
if (aid == null)
throw ServiceException.FAILURE("Could not create/update calendar item", null);
calItemId = aid.calItemId;
// Refetch updated item.
calItem = safeGetCalendarItemById(mbox, octxt, aid.calItemId);
if (calItem == null)
throw ServiceException.FAILURE("Could not refetch created/updated calendar item", null);
} else {
calItemId = 0;
}
oldInv = inv;
} else if (info.calItemCreated()) {
// legacy case (before we added Invite info to Message metadata)
calItem = safeGetCalendarItemById(mbox, octxt, info.getCalendarItemId());
if (calItem == null)
throw ServiceException.FAILURE("Missing invite data", null);
// Must be for this mailbox
calItemId = info.getCalendarItemId().getId();
wasInTrash = calItem.inTrash();
oldInv = calItem.getInvite(inviteMsgId, compNum);
} else {
throw ServiceException.FAILURE("Missing invite data", null);
}
}
}
if (intendedForMe) {
if (oldInv == null)
throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
if (calItem != null && (mbox.getEffectivePermissions(octxt, calItemId, MailItem.Type.UNKNOWN) & ACL.RIGHT_ACTION) == 0) {
throw ServiceException.PERM_DENIED("You do not have ACTION rights for CalendarItem " + calItemId);
}
// check if invite organizer requested rsvp or not
if (oldInv.hasRsvp()) {
updateOrg = updateOrg && oldInv.getRsvp();
}
// Don't allow creating/editing a private appointment on behalf of another user,
// unless that other user is a calendar resource.
boolean allowPrivateAccess = calItem != null ? calItem.allowPrivateAccess(authAcct, isAdmin) : true;
boolean isCalendarResource = acct instanceof CalendarResource;
if (!allowPrivateAccess && !oldInv.isPublic() && !isCalendarResource)
throw ServiceException.PERM_DENIED("Cannot reply to a private appointment/task on behalf of another user");
// see if there is a specific Exception being referenced by this reply...
Element exc = request.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID);
ParsedDateTime exceptDt = null;
if (exc != null) {
TimeZoneMap tzmap = oldInv.getTimeZoneMap();
Element tzElem = request.getOptionalElement(MailConstants.E_CAL_TZ);
ICalTimeZone tz = null;
if (tzElem != null) {
tz = CalendarUtils.parseTzElement(tzElem);
tzmap.add(tz);
}
exceptDt = CalendarUtils.parseDateTime(exc, tzmap);
} else if (oldInv.hasRecurId()) {
exceptDt = oldInv.getRecurId().getDt();
}
// trace logging
String calItemIdStr = calItem != null ? Integer.toString(calItem.getId()) : "none";
String folderIdStr = calItem != null ? Integer.toString(calItem.getFolderId()) : "none";
if (exceptDt == null)
ZimbraLog.calendar.info("<SendInviteReply> id=%s, folderId=%s, verb=%s, notifyOrg=%s, subject=\"%s\", UID=%s", calItemIdStr, folderIdStr, verb.toString(), Boolean.toString(updateOrg), oldInv.isPublic() ? oldInv.getName() : "(private)", oldInv.getUid());
else
ZimbraLog.calendar.info("<SendInviteReply> id=%s, folderId=%s, verb=%s, notifyOrg=%s, subject=\"%s\", UID=%s, recurId=%s", calItemIdStr, folderIdStr, verb.toString(), Boolean.toString(updateOrg), oldInv.isPublic() ? oldInv.getName() : "(private)", oldInv.getUid(), exceptDt.getUtcString());
// exception instance first. Then reply to it.
if (calItem != null && oldInv.isRecurrence() && exceptDt != null) {
Invite localException = oldInv.makeInstanceInvite(exceptDt);
long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
localException.setDtStamp(now);
String partStat = verb.getXmlPartStat();
localException.setPartStat(partStat);
ZAttendee at = localException.getMatchingAttendee(acct, identityId);
if (at != null)
at.setPartStat(partStat);
// Carry over the MimeMessage/ParsedMessage to preserve any attachments.
MimeMessage mmInv = calItem.getSubpartMessage(oldInv.getMailItemId());
ParsedMessage pm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
int folder;
boolean untrashing = wasInTrash && !isDecline;
if (untrashing) {
// If it exists but is in Trash and is not a decline, move it out of Trash.
// If it's in trash and we're declining, leave it in trash.
folder = localException.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
} else {
folder = calItem.getFolderId();
}
mbox.addInvite(octxt, localException, folder, pm, true, untrashing, true);
// Refetch the updated calendar item and set oldInv to refetched local exception instance.
calItem = safeGetCalendarItemById(mbox, octxt, calItemId);
if (calItem == null)
throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
oldInv = calItem.getInvite(new RecurId(exceptDt, RecurId.RANGE_NONE));
}
ZAttendee att = oldInv.getMatchingAttendee(mbox.getAccount());
if (att != null) {
stat = att.getPartStat();
}
if (updateOrg && oldInv.hasOrganizer()) {
Locale locale;
Account organizer = oldInv.getOrganizerAccount();
if (organizer != null)
locale = organizer.getLocale();
else
locale = !onBehalfOf ? acct.getLocale() : authAcct.getLocale();
String subject;
if (!allowPrivateAccess && !oldInv.isPublic())
subject = L10nUtil.getMessage(MsgKey.calendarSubjectWithheld, locale);
else
subject = oldInv.getName();
String replySubject = CalendarMailSender.getReplySubject(verb, subject, locale);
CalSendData csd = new CalSendData();
csd.mOrigId = new ItemId(mbox, oldInv.getMailItemId());
csd.mReplyType = MailSender.MSGTYPE_REPLY;
csd.mInvite = CalendarMailSender.replyToInvite(acct, identityId, authAcct, onBehalfOf, allowPrivateAccess, oldInv, verb, replySubject, exceptDt);
ZVCalendar iCal = csd.mInvite.newToICalendar(true);
ParseMimeMessage.MimeMessageData parsedMessageData = new ParseMimeMessage.MimeMessageData();
// did they specify a custom <m> message? If so, then we don't have to build one...
Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
if (msgElem != null) {
String text = ParseMimeMessage.getTextPlainContent(msgElem);
String html = ParseMimeMessage.getTextHtmlContent(msgElem);
iCal.addDescription(text, html);
MimeBodyPart[] mbps = new MimeBodyPart[1];
mbps[0] = CalendarMailSender.makeICalIntoMimePart(iCal);
// the <inv> element is *NOT* allowed -- we always build it manually
// based on the params to the <SendInviteReply> and stick it in the
// mbps (additionalParts) parameter...
csd.mMm = ParseMimeMessage.parseMimeMsgSoap(zsc, octxt, mbox, msgElem, mbps, ParseMimeMessage.NO_INV_ALLOWED_PARSER, parsedMessageData);
} else {
// build a default "Accepted" response
if (!(acct instanceof CalendarResource)) {
csd.mMm = CalendarMailSender.createDefaultReply(acct, identityId, authAcct, identityId, isAdmin, onBehalfOf, calItem, oldInv, null, replySubject, verb, null, iCal);
} else {
// different template for calendar resources
RecurId rid = oldInv.getRecurId();
ParsedDateTime ridDt = rid != null ? rid.getDt() : null;
Invite replyInv = CalendarMailSender.replyToInvite(acct, authAcct, onBehalfOf, allowPrivateAccess, oldInv, verb, replySubject, ridDt);
MimeMessage mmInv = calItem.getSubpartMessage(oldInv.getMailItemId());
csd.mMm = CalendarMailSender.createResourceAutoReply(octxt, identityId, identityId, mbox, verb, false, null, calItem, oldInv, new Invite[] { replyInv }, mmInv, true);
}
}
int apptFolderId;
if (calItem != null)
apptFolderId = calItem.getFolderId();
else
apptFolderId = oldInv.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
MailSendQueue sendQueue = new MailSendQueue();
try {
if (stat != null && IcalXmlStrMap.PARTSTAT_DECLINED.equals(stat) && !CalendarMailSender.VERB_DECLINE.equals(verb)) {
sendCalendarMessage(zsc, octxt, apptFolderId, acct, mbox, csd, response, sendQueue, true);
} else {
sendCalendarMessage(zsc, octxt, apptFolderId, acct, mbox, csd, response, sendQueue);
}
} finally {
sendQueue.send();
}
}
RecurId recurId = null;
if (exceptDt != null) {
recurId = new RecurId(exceptDt, RecurId.RANGE_NONE);
}
ZAttendee me = oldInv.getMatchingAttendee(acct);
String cnStr = null;
String addressStr = acct.getName();
String role = IcalXmlStrMap.ROLE_OPT_PARTICIPANT;
int seqNo = oldInv.getSeqNo();
long dtStamp = oldInv.getDTStamp();
if (me != null) {
if (me.hasCn()) {
cnStr = me.getCn();
}
addressStr = me.getAddress();
if (me.hasRole()) {
role = me.getRole();
}
}
if (calItem != null)
mbox.modifyPartStat(octxt, calItemId, recurId, cnStr, addressStr, null, role, verb.getXmlPartStat(), Boolean.FALSE, seqNo, dtStamp);
}
// move the invite to the Trash if the user wants it
if (deleteInviteOnReply(acct)) {
try {
if (onBehalfOf) {
// HACK: Run the move in the context of the organizer
// mailbox because the authenticated account doesn't
// have rights on Inbox and Trash folders.
octxt = new OperationContext(mbox);
}
mbox.move(octxt, inviteMsgId, MailItem.Type.MESSAGE, Mailbox.ID_FOLDER_TRASH);
} catch (MailServiceException.NoSuchItemException nsie) {
ZimbraLog.calendar.debug("can't move nonexistent invite to Trash: " + inviteMsgId);
}
}
return response;
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class SendVerificationCode method sendVerificationCode.
static void sendVerificationCode(String emailAddr, String code, Mailbox mbox) throws MessagingException, ServiceException {
MimeMessage mm = new Mime.FixedMimeMessage(JMSession.getSmtpSession(mbox.getAccount()));
mm.setRecipient(javax.mail.Message.RecipientType.TO, new JavaMailInternetAddress(emailAddr));
mm.setText(L10nUtil.getMessage(L10nUtil.MsgKey.deviceSendVerificationCodeText, mbox.getAccount().getLocale(), code), MimeConstants.P_CHARSET_UTF8);
mm.saveChanges();
MailSender mailSender = mbox.getMailSender();
mailSender.setSaveToSent(false);
mailSender.sendMimeMessage(null, mbox, mm);
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class CalendarItem method processNewInviteReply.
/**
* @param updatePrevFolders - If set, update the record of previous folders
* @param itemIdGetter - Used in newly created pseudo exceptions
* @return false if the invite being updated is out of date
* @throws ServiceException
*/
protected boolean processNewInviteReply(Invite reply, String sender, boolean updatePrevFolders, Mailbox.ItemIdGetter itemIdGetter) throws ServiceException {
List<ZAttendee> attendees = reply.getAttendees();
String senderAddress = null;
if (sender != null && !sender.isEmpty()) {
try {
JavaMailInternetAddress address = new JavaMailInternetAddress(sender);
senderAddress = address.getAddress();
} catch (AddressException e) {
// ignore invalid sender address.
}
}
if (senderAddress != null && !attendees.isEmpty()) {
AccountAddressMatcher acctMatcher = null;
Account acct = Provisioning.getInstance().get(AccountBy.name, senderAddress);
if (acct != null) {
acctMatcher = new AccountAddressMatcher(acct);
}
Iterator<ZAttendee> iter = attendees.iterator();
while (iter.hasNext()) {
ZAttendee att = iter.next();
// Remove the attendee if not same as the sender.
if (!(att.addressMatches(senderAddress) || (acctMatcher != null && acctMatcher.matches(att.getAddress())))) {
iter.remove();
}
}
}
// trace logging
ZAttendee att1 = !attendees.isEmpty() ? attendees.get(0) : null;
if (att1 != null) {
String ptst = IcalXmlStrMap.sPartStatMap.toIcal(att1.getPartStat());
if (!reply.hasRecurId())
ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s", att1.getAddress(), ptst, mId, getFolderId(), reply.isPublic() ? reply.getName() : "(private)", mUid);
else
ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s, recurId=%s", att1.getAddress(), ptst, mId, getFolderId(), reply.isPublic() ? reply.getName() : "(private)", mUid, reply.getRecurId().getDtZ());
}
// Require private access permission only when we're replying to a private series/instance.
boolean requirePrivateCheck = requirePrivateCheck(reply);
OperationContext octxt = getMailbox().getOperationContext();
Account authAccount = octxt != null ? octxt.getAuthenticatedUser() : null;
boolean asAdmin = octxt != null ? octxt.isUsingAdminPrivileges() : false;
if (!canAccess(ACL.RIGHT_ACTION, authAccount, asAdmin, requirePrivateCheck))
throw ServiceException.PERM_DENIED("you do not have sufficient permissions to change this appointment/task's state");
boolean dirty = false;
// unique ID: UID+RECURRENCE_ID
// See RFC2446: 2.1.5 Message Sequencing
// UID already matches...next check if RecurId matches
// if so, then seqNo is next
// finally use DTStamp
Invite matchingInvite = matchingInvite(reply.getRecurId());
if (matchingInvite != null) {
// up to date with the organizer's event, provided there were no major changes.
if ((matchingInvite.isOrganizer() && (matchingInvite.getLastFullSeqNo() > reply.getSeqNo())) || (!matchingInvite.isOrganizer() && (matchingInvite.getSeqNo() > reply.getSeqNo()))) {
sLog.info("Invite-Reply %s is outdated (Calendar entry has higher SEQUENCE), ignoring!", reply);
return false;
}
// maybeStoreNewReply does some further checks which might invalidate this reply
// so, postpone updating attendee information until after that.
}
// they must be replying to a arbitrary instance)
for (ZAttendee at : attendees) {
if (mReplyList.maybeStoreNewReply(reply, at, this))
dirty = true;
}
if (!dirty) {
sLog.info("Invite-Reply %s is outdated ignoring!", reply);
return false;
}
if (matchingInvite != null) {
matchingInvite.updateMatchingAttendeesFromReply(reply);
updateLocalExceptionsWhichMatchSeriesReply(reply);
} else {
createPseudoExceptionForSingleInstanceReplyIfNecessary(reply, itemIdGetter);
}
if (updatePrevFolders) {
performSetPrevFoldersOperation(octxt);
}
saveMetadata();
return true;
}
Aggregations