use of com.zimbra.cs.mailbox.calendar.ZAttendee in project zm-mailbox by Zimbra.
the class CalendarItem method processNewInviteReply.
boolean processNewInviteReply(Invite reply, String sender) 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);
}
saveMetadata();
return true;
}
use of com.zimbra.cs.mailbox.calendar.ZAttendee in project zm-mailbox by Zimbra.
the class CalendarUtils method parseReplyList.
public static List<ReplyInfo> parseReplyList(Element element, TimeZoneMap tzMap) throws ServiceException {
List<ReplyInfo> list = new ArrayList<ReplyInfo>();
for (Iterator<Element> iter = element.elementIterator(MailConstants.E_CAL_REPLY); iter.hasNext(); ) {
Element riElem = iter.next();
String addr = riElem.getAttribute(MailConstants.A_CAL_ATTENDEE);
ZAttendee at = new ZAttendee(addr);
String sentBy = riElem.getAttribute(MailConstants.A_CAL_SENTBY, null);
if (sentBy != null)
at.setSentBy(sentBy);
String partStat = riElem.getAttribute(MailConstants.A_CAL_PARTSTAT, null);
if (partStat != null)
at.setPartStat(partStat);
int seq = (int) riElem.getAttributeLong(MailConstants.A_SEQ);
long dtStamp = riElem.getAttributeLong(MailConstants.A_DATE);
RecurId recurId = RecurId.fromXml(riElem, tzMap);
ReplyInfo ri = new ReplyInfo(at, seq, dtStamp, recurId);
list.add(ri);
}
return list;
}
use of com.zimbra.cs.mailbox.calendar.ZAttendee in project zm-mailbox by Zimbra.
the class CancelCalendarItem method handle.
@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Account acct = getRequestedAccount(zsc);
Mailbox mbox = getRequestedMailbox(zsc);
OperationContext octxt = getOperationContext(zsc, context);
ItemId iid = new ItemId(request.getAttribute(MailConstants.A_ID), zsc);
if (!iid.hasSubpart())
throw ServiceException.INVALID_REQUEST("missing invId subpart: id should be specified as \"item-inv\"", null);
int compNum = (int) request.getAttributeLong(MailConstants.E_INVITE_COMPONENT);
CalendarItem calItem = mbox.getCalendarItemById(octxt, iid.getId());
if (calItem == null)
throw MailServiceException.NO_SUCH_CALITEM(iid.getId(), " for CancelCalendarItemRequest(" + iid + "," + compNum + ")");
if (calItem.inTrash())
throw ServiceException.INVALID_REQUEST("cannot cancel a calendar item under trash", null);
// We probably don't want to bother with conflict check for a cancel request...
Invite inv = calItem.getInvite(iid.getSubpartId(), compNum);
if (inv == null)
throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
MailSendQueue sendQueue = new MailSendQueue();
try {
Element recurElt = request.getOptionalElement(MailConstants.E_INSTANCE);
if (recurElt != null) {
TimeZoneMap tzmap = inv.getTimeZoneMap();
Element tzElem = request.getOptionalElement(MailConstants.E_CAL_TZ);
ICalTimeZone tz = null;
if (tzElem != null) {
tz = CalendarUtils.parseTzElement(tzElem);
tzmap.add(tz);
}
RecurId recurId = CalendarUtils.parseRecurId(recurElt, tzmap);
// trace logging
ZimbraLog.calendar.info("<CancelCalendarItem> id=%d, folderId=%d, subject=\"%s\", UID=%s, recurId=%s", calItem.getId(), calItem.getFolderId(), inv.isPublic() ? inv.getName() : "(private)", calItem.getUid(), recurId.getDtZ());
Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
cancelInstance(zsc, octxt, msgElem, acct, mbox, calItem, inv, recurId, inv.getAttendees(), sendQueue);
} else {
// if recur is not set, then we're canceling the entire calendar item...
// trace logging
ZimbraLog.calendar.info("<CancelCalendarItem> id=%d, folderId=%d, subject=\"%s\", UID=%s", calItem.getId(), calItem.getFolderId(), inv.isPublic() ? inv.getName() : "(private)", calItem.getUid());
Invite seriesInv = calItem.getDefaultInviteOrNull();
if (seriesInv != null) {
if (seriesInv.getMethod().equals(ICalTok.REQUEST.toString()) || seriesInv.getMethod().equals(ICalTok.PUBLISH.toString())) {
if (seriesInv.isOrganizer()) {
// Send cancel notice to attendees who were invited to exception instances only.
// These attendees need to be notified separately because they aren't included in the series
// cancel notice.
List<ZAttendee> atsSeries = seriesInv.getAttendees();
Invite[] invs = calItem.getInvites();
long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
for (Invite exceptInv : invs) {
if (exceptInv != seriesInv) {
String mthd = exceptInv.getMethod();
if ((mthd.equals(ICalTok.REQUEST.toString()) || mthd.equals(ICalTok.PUBLISH.toString())) && inviteIsAfterTime(exceptInv, now)) {
List<ZAttendee> atsExcept = exceptInv.getAttendees();
// Find exception instance attendees who aren't series attendees.
List<ZAttendee> ats = CalendarUtils.getRemovedAttendees(atsExcept, atsSeries, false, acct);
if (!ats.isEmpty()) {
// notify ats
cancelInstance(zsc, octxt, null, acct, mbox, calItem, exceptInv, exceptInv.getRecurId(), ats, sendQueue);
}
}
}
}
}
// Finally, cancel the series.
Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
cancelInvite(zsc, octxt, msgElem, acct, mbox, calItem, seriesInv, sendQueue);
}
// disable change constraint checking since we've just successfully done a modify
octxt = new OperationContext(octxt).unsetChangeConstraint();
}
}
} finally {
sendQueue.send();
}
Element response = getResponseElement(zsc);
return response;
}
use of com.zimbra.cs.mailbox.calendar.ZAttendee in project zm-mailbox by Zimbra.
the class CalendarRequest method addRemoveAttendeesInExceptions.
protected static void addRemoveAttendeesInExceptions(OperationContext octxt, Mailbox mbox, CalendarItem calItem, List<ZAttendee> toAdd, List<ZAttendee> toRemove, boolean ignorePastExceptions) throws ServiceException {
mbox.lock.lock();
try {
// Refresh the cal item so we see the latest blob, whose path may have been changed
// earlier in the current request.
calItem = mbox.getCalendarItemById(octxt, calItem.getId());
long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
boolean first = true;
Invite[] invites = calItem.getInvites();
for (Invite inv : invites) {
// Ignore exceptions in the past.
if (ignorePastExceptions && inv.hasRecurId() && !inviteIsAfterTime(inv, now)) {
continue;
}
// Make a copy of the invite and add/remove attendees.
boolean modified = false;
Invite modify = inv.newCopy();
modify.setMailItemId(inv.getMailItemId());
List<ZAttendee> existingAts = modify.getAttendees();
// Survivors are added.
List<ZAttendee> addList = new ArrayList<ZAttendee>(toAdd);
for (Iterator<ZAttendee> iter = existingAts.iterator(); iter.hasNext(); ) {
ZAttendee existingAt = iter.next();
String existingAtEmail = existingAt.getAddress();
if (existingAtEmail != null) {
// Check if the attendee being added is already in the invite.
for (Iterator<ZAttendee> iterAdd = addList.iterator(); iterAdd.hasNext(); ) {
ZAttendee at = iterAdd.next();
if (existingAtEmail.equalsIgnoreCase(at.getAddress())) {
iterAdd.remove();
}
}
// Check if existing attendee matches an attendee being removed.
for (ZAttendee at : toRemove) {
if (existingAtEmail.equalsIgnoreCase(at.getAddress())) {
iter.remove();
modified = true;
break;
}
}
}
}
// Duplicates have been eliminated from addList. Add survivors to the invite.
if (!addList.isEmpty()) {
for (ZAttendee at : addList) {
modify.addAttendee(at);
}
modified = true;
}
// Save the modified invite.
if (modified) {
// REQUEST so that the recipient can handle it properly.
if (!modify.isCancel())
modify.setMethod(ICalTok.REQUEST.toString());
// DTSTAMP - Rev it.
modify.setDtStamp(now);
// Save the modified invite, using the existing MimeMessage for the exception.
MimeMessage mmInv = calItem.getSubpartMessage(modify.getMailItemId());
ParsedMessage pm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
mbox.addInvite(octxt, modify, calItem.getFolderId(), pm, true, false, first);
first = false;
// Refresh calItem after update in mbox.addInvite.
calItem = mbox.getCalendarItemById(octxt, calItem.getId());
}
}
} finally {
mbox.lock.release();
}
}
use of com.zimbra.cs.mailbox.calendar.ZAttendee in project zm-mailbox by Zimbra.
the class CalendarUtils method parseInviteForCounter.
static ParseMimeMessage.InviteParserResult parseInviteForCounter(Account account, Invite oldInvite, MailItem.Type type, Element inviteElem) throws ServiceException {
TimeZoneMap tzMap = new TimeZoneMap(Util.getAccountTimeZone(account));
Invite inv = new Invite(ICalTok.COUNTER.toString(), tzMap, false);
CalendarUtils.parseInviteElementCommon(account, type, inviteElem, inv, true, true);
// Get the existing invite to populate X-MS-OLK-ORIGINALSTART and X-MS-OLK-ORIGINALEND
if (oldInvite == null) {
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(account);
CalendarItem calItem = mbox.getCalendarItemByUid(null, inv.getUid());
if (calItem != null)
oldInvite = calItem.getInvite(inv.getRecurId());
}
if (oldInvite != null) {
// Add TZIDs from oldInvite to inv
inv.getTimeZoneMap().add(oldInvite.getTimeZoneMap());
// Add ORIGINALSTART x-prop
ParsedDateTime dt = oldInvite.getStartTime();
if (dt != null) {
ZCalendar.ZProperty prop = new ZCalendar.ZProperty("X-MS-OLK-ORIGINALSTART");
prop.setValue(dt.getDateTimePartString());
if (dt.getTZName() != null)
prop.addParameter(new ZParameter(ICalTok.TZID, dt.getTZName()));
inv.addXProp(prop);
}
// Add ORIGINALEND x-prop
dt = oldInvite.getEffectiveEndTime();
if (dt != null) {
ZCalendar.ZProperty prop = new ZCalendar.ZProperty("X-MS-OLK-ORIGINALEND");
prop.setValue(dt.getDateTimePartString());
if (dt.getTZName() != null)
prop.addParameter(new ZParameter(ICalTok.TZID, dt.getTZName()));
inv.addXProp(prop);
}
// Add LOCATION if not already exist.
if (inv.getLocation() == null || inv.getLocation().isEmpty())
inv.setLocation(oldInvite.getLocation());
}
// UID
String uid = inv.getUid();
if (uid == null || uid.length() == 0)
throw ServiceException.INVALID_REQUEST("Missing uid in a counter invite", null);
// ORGANIZER
if (!inv.hasOrganizer())
throw ServiceException.INVALID_REQUEST("Missing organizer in a counter invite", null);
// DTSTAMP
if (inv.getDTStamp() == 0) {
//zdsync
inv.setDtStamp(new Date().getTime());
}
// DTSTART
if (inv.getStartTime() == null)
throw ServiceException.INVALID_REQUEST("Missing dtstart in a counter invite", null);
// iCalendar object doesn't have an ATTENDEE property. RFC2446 doesn't require one.
if (!inv.hasOtherAttendees()) {
ZAttendee at = new ZAttendee(account.getMail());
at.setPartStat(IcalXmlStrMap.PARTSTAT_TENTATIVE);
inv.addAttendee(at);
}
inv.setLocalOnly(false);
ZVCalendar iCal = inv.newToICalendar(true);
String summaryStr = inv.getName() != null ? inv.getName() : "";
ParseMimeMessage.InviteParserResult toRet = new ParseMimeMessage.InviteParserResult();
toRet.mCal = iCal;
toRet.mUid = inv.getUid();
toRet.mSummary = summaryStr;
toRet.mInvite = inv;
return toRet;
}
Aggregations