use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarItem method requirePrivateCheck.
// If we're adding a private invite, we must make sure the authenticated user has permission to
// access private data. If we're adding a public invite but the appointment currently has
// some private data, private access permission is not needed as long as the instance(s) being
// updated aren't currently private.
private boolean requirePrivateCheck(Invite newInvite) {
if (!newInvite.isPublic()) {
// adding a private invite
return true;
}
if (!isPublic()) {
RecurId rid = newInvite.getRecurId();
// If canceling whole series, requester must have private access permission.
if (rid == null && newInvite.isCancel())
return true;
Invite current = getInvite(rid);
// If no matching recurrence-id was found, look at the current series.
if (current == null && rid != null)
current = getInvite((RecurId) null);
if (current != null && !current.isPublic()) {
// updating a currently private invite to public
return true;
} else {
// no matching rid found, or current is public
return false;
}
} else {
// Both old and new are public.
return false;
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarItem method createPseudoExceptionForSingleInstanceReplyIfNecessary.
/**
* Bug 94018 - Need an exception to represent a reply to a single instance of an exception, otherwise a decline
* to a single instance gets forgotten in some cases where the series partstat is used instead.
* Assumption - already checked that there isn't a matching exception instance already
* Caller is responsible for ensuring changed MetaData is written through to SQL sending notification of change.
*/
private void createPseudoExceptionForSingleInstanceReplyIfNecessary(Invite reply) throws ServiceException {
if ((reply == null) || reply.getRecurId() == null) {
// reply isn't to a single instance
return;
}
Recurrence.RecurrenceRule recurrenceRule = null;
if ((mRecurrence == null) || !(mRecurrence instanceof Recurrence.RecurrenceRule)) {
return;
}
recurrenceRule = (Recurrence.RecurrenceRule) mRecurrence;
Collection<Instance> instancesNear = instancesNear(reply.getRecurId());
if (!instancesNear.isEmpty()) {
/* we need a new exception to handle the difference in attendee status */
for (int i = 0; i < numInvites(); i++) {
Invite cur = getInvite(i);
if (cur.getRecurId() == null) {
try {
ParsedDateTime pdt = ParsedDateTime.parseUtcOnly(reply.getRecurId().getDtZ());
Invite localException = cur.makeInstanceInvite(pdt);
localException.setDtStamp(System.currentTimeMillis());
localException.updateMatchingAttendeesFromReply(reply);
// flag as organizer change
localException.setClassPropSetByMe(true);
mInvites.add(localException);
// create a fake ExceptionRule wrapper around the single-instance
recurrenceRule.addException(new Recurrence.ExceptionRule(reply.getRecurId(), localException.getStartTime(), localException.getEffectiveDuration(), new InviteInfo(localException)));
} catch (ParseException e) {
sLog.debug("Unexpected exception - not updating calendar invite with pseudo exception", e);
}
break;
}
}
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Appointment method makeReplyInvite.
private Invite makeReplyInvite(Account account, Account authAccount, Locale lc, boolean onBehalfOf, boolean allowPrivateAccess, Invite inv, RecurId rid, Verb verb) throws ServiceException {
boolean hidePrivate = !inv.isPublic() && !allowPrivateAccess;
String subject;
if (hidePrivate)
subject = L10nUtil.getMessage(MsgKey.calendarSubjectWithheld, lc);
else
subject = inv.getName();
String replySubject = CalendarMailSender.getReplySubject(verb, subject, lc);
ParsedDateTime ridDt = rid != null ? rid.getDt() : null;
Invite replyInv = CalendarMailSender.replyToInvite(account, authAccount, onBehalfOf, allowPrivateAccess, inv, verb, replySubject, ridDt);
return replyInv;
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarItem method organizerChangeCheck.
/**
* Check to make sure the new invite doesn't change the organizer in a disallowed way.
* @param newInvite
* @return true if organizer change was detected, false if no change
* @throws ServiceException
*/
private boolean organizerChangeCheck(Invite newInvite, boolean denyChange) throws ServiceException {
Invite originalInvite = null;
if (!newInvite.hasRecurId()) {
// New invite is not for an exception.
originalInvite = getDefaultInviteOrNull();
} else {
// New invite is for an exception.
boolean found = false;
RecurId newRid = newInvite.getRecurId();
for (Invite inv : mInvites) {
if (inv.hasRecurId() && newRid.equals(inv.getRecurId())) {
originalInvite = inv;
found = true;
break;
}
}
if (!found) {
// If no invite with matching RECURRENCE-ID was found, use the default invite.
originalInvite = getDefaultInviteOrNull();
}
}
if (originalInvite == null) {
// If no "default" invite was found, use the first one.
if (mInvites.size() > 0)
originalInvite = mInvites.get(0);
if (originalInvite == null) {
// checks in this method.
return false;
}
}
boolean updatingSameComponent = true;
if (newInvite.hasRecurId()) {
if (originalInvite.hasRecurId()) {
updatingSameComponent = newInvite.getRecurId().equals(originalInvite.getRecurId());
} else {
updatingSameComponent = false;
}
}
boolean changed = false;
ZOrganizer originalOrganizer = originalInvite.getOrganizer();
if (!originalInvite.isOrganizer()) {
// This account WAS NOT the organizer. Prevent organizer change.
if (newInvite.hasOrganizer()) {
String newOrgAddr = newInvite.getOrganizer().getAddress();
if (originalOrganizer == null) {
if (denyChange) {
newInvite.isTodo();
if (updatingSameComponent) {
throw BadOrganizerException.ADD_ORGANIZER_NOT_ALLOWED(newOrgAddr, calDesc(newInvite));
} else {
throw BadOrganizerException.ORGANIZER_INTRODUCED_FOR_EXCEPTION(newOrgAddr, calDesc(newInvite));
}
} else {
changed = true;
}
} else {
// Both old and new organizers are set. They must be the same address.
String origOrgAddr = originalOrganizer.getAddress();
if (newOrgAddr == null || !CalendarUtils.belongToSameAccount(origOrgAddr, newOrgAddr)) {
if (denyChange) {
if (updatingSameComponent) {
throw BadOrganizerException.CHANGE_ORGANIZER_NOT_ALLOWED(origOrgAddr, newOrgAddr, calDesc(newInvite));
} else {
throw BadOrganizerException.DIFF_ORGANIZER_IN_COMPONENTS(origOrgAddr, newOrgAddr, calDesc(newInvite));
}
} else {
changed = true;
}
}
}
} else if (originalOrganizer != null) {
// No organizer for new newInvite but there is one in the original
String origOrgAddr = originalOrganizer.getAddress();
if (denyChange) {
if (updatingSameComponent) {
throw BadOrganizerException.DEL_ORGANIZER_NOT_ALLOWED(origOrgAddr, calDesc(newInvite));
} else {
throw BadOrganizerException.MISSING_ORGANIZER_IN_SINGLE_INSTANCE(origOrgAddr, calDesc(newInvite));
}
} else {
changed = true;
}
}
} else {
// Still don't allow changing the organizer field to an arbitrary address.
if (newInvite.hasOrganizer()) {
if (!newInvite.isOrganizer()) {
String newOrgAddr = newInvite.getOrganizer().getAddress();
String origOrgAddr = (originalOrganizer != null) ? originalOrganizer.getAddress() : null;
if (newOrgAddr.equalsIgnoreCase(origOrgAddr)) {
/* Speculative fix for Bug 83261. Had gotten to this point with the same address but
* thought that wasn't the organizer for the new invite even though that organizer
* passed the test for originalInvite. Ideally, should track down why the value was wrong
* but don't have a full repro scenario.
*/
newInvite.setIsOrganizer(true);
}
if (!newInvite.isOrganizer()) {
if (denyChange) {
if (originalOrganizer != null) {
if (updatingSameComponent) {
throw BadOrganizerException.CHANGE_ORGANIZER_NOT_ALLOWED(origOrgAddr, newOrgAddr, calDesc(newInvite));
} else {
throw BadOrganizerException.DIFF_ORGANIZER_IN_COMPONENTS(origOrgAddr, newOrgAddr, calDesc(newInvite));
}
} else {
if (updatingSameComponent) {
throw BadOrganizerException.ADD_ORGANIZER_NOT_ALLOWED(newOrgAddr, calDesc(newInvite));
} else {
throw BadOrganizerException.ORGANIZER_INTRODUCED_FOR_EXCEPTION(newOrgAddr, calDesc(newInvite));
}
}
} else {
changed = true;
}
}
}
}
}
if (changed) {
String origOrg = originalOrganizer != null ? originalOrganizer.getAddress() : null;
ZOrganizer newOrganizer = newInvite.getOrganizer();
String newOrg = newOrganizer != null ? newOrganizer.getAddress() : null;
boolean wasOrganizer = originalInvite.isOrganizer();
boolean isOrganizer = newInvite.isOrganizer();
ZimbraLog.calendar.info("Changed organizer: old=" + origOrg + ", new=" + newOrg + ", wasOrg=" + wasOrganizer + ", isOrg=" + isOrganizer + ", UID=\"" + newInvite.getUid() + "\", invId=" + newInvite.getMailItemId());
}
return changed;
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarItem method create.
static CalendarItem create(int id, Folder folder, int flags, Tag.NormalizedTags ntags, String uid, ParsedMessage pm, Invite firstInvite, long nextAlarm, CustomMetadata custom) throws ServiceException {
firstInvite.sanitize(false);
if (!folder.canAccess(ACL.RIGHT_INSERT)) {
throw ServiceException.PERM_DENIED("you do not have the required rights on the folder");
}
if (!firstInvite.isPublic() && !folder.canAccess(ACL.RIGHT_PRIVATE)) {
throw ServiceException.PERM_DENIED("you do not have permission to create private calendar item in this folder");
}
Mailbox mbox = folder.getMailbox();
if (pm != null && pm.hasAttachments()) {
firstInvite.setHasAttachment(true);
flags |= Flag.BITMASK_ATTACHED;
} else {
firstInvite.setHasAttachment(false);
flags &= ~Flag.BITMASK_ATTACHED;
}
if (firstInvite.isDraft()) {
flags |= Flag.BITMASK_DRAFT;
} else {
flags &= ~Flag.BITMASK_DRAFT;
}
if (firstInvite.isHighPriority()) {
flags |= Flag.BITMASK_HIGH_PRIORITY;
} else {
flags &= ~Flag.BITMASK_HIGH_PRIORITY;
}
if (firstInvite.isLowPriority()) {
flags |= Flag.BITMASK_LOW_PRIORITY;
} else {
flags &= ~Flag.BITMASK_LOW_PRIORITY;
}
MailItem.Type type = firstInvite.isEvent() ? Type.APPOINTMENT : Type.TASK;
String sender = null;
ZOrganizer org = firstInvite.getOrganizer();
if (org != null) {
sender = org.getIndexString();
}
sender = Strings.nullToEmpty(sender);
String subject = Strings.nullToEmpty(firstInvite.getName());
List<Invite> invites = new ArrayList<Invite>();
invites.add(firstInvite);
Recurrence.IRecurrence recur = firstInvite.getRecurrence();
long startTime, endTime;
if (recur != null) {
ParsedDateTime dtStart = recur.getStartTime();
startTime = dtStart != null ? dtStart.getUtcTime() : 0;
ParsedDateTime dtEnd = recur.getEndTime();
endTime = dtEnd != null ? dtEnd.getUtcTime() : 0;
} else {
ParsedDateTime dtStart = firstInvite.getStartTime();
startTime = dtStart != null ? dtStart.getUtcTime() : 0;
ParsedDateTime dtEnd = firstInvite.getEffectiveEndTime();
endTime = dtEnd != null ? dtEnd.getUtcTime() : startTime;
}
Account account = mbox.getAccount();
firstInvite.updateMyPartStat(account, firstInvite.getPartStat());
UnderlyingData data = new UnderlyingData();
data.id = id;
data.type = type.toByte();
data.folderId = folder.getId();
if (!folder.inSpam() || mbox.getAccount().getBooleanAttr(Provisioning.A_zimbraJunkMessagesIndexingEnabled, false)) {
data.indexId = IndexStatus.DEFERRED.id();
}
data.imapId = id;
data.date = mbox.getOperationTimestamp();
data.setFlags(flags & (Flag.FLAGS_CALITEM | Flag.FLAGS_GENERIC));
data.setTags(ntags);
data.setSubject(subject);
data.metadata = encodeMetadata(DEFAULT_COLOR_RGB, 1, 1, custom, uid, startTime, endTime, recur, invites, firstInvite.getTimeZoneMap(), new ReplyList(), null);
data.contentChanged(mbox, false);
if (!firstInvite.hasRecurId()) {
ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s", data.id, pm != null ? pm.getMessageID() : "(none)", folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid());
} else {
ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s, recurId=%s", data.id, pm != null ? pm.getMessageID() : "(none)", folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid(), firstInvite.getRecurId().getDtZ());
}
new DbMailItem(mbox).setSender(sender).create(data);
CalendarItem item = type == Type.APPOINTMENT ? new Appointment(mbox, data) : new Task(mbox, data);
Invite defInvite = item.getDefaultInviteOrNull();
if (defInvite != null) {
Collection<Instance> instances = item.expandInstances(CalendarUtils.MICROSOFT_EPOC_START_MS_SINCE_EPOC, Long.MAX_VALUE, false);
if (instances.isEmpty()) {
ZimbraLog.calendar.info("CalendarItem has effectively zero instances: id=%d, folderId=%d, subject=\"%s\", UID=%s ", data.id, folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid());
item.delete();
throw ServiceException.FORBIDDEN("Recurring series has effectively zero instances");
}
}
// If we're creating an invite during email delivery, always default to NEEDS_ACTION state.
// If not email delivery, we assume the requesting client knows what it's doing and has set the
// correct partstat in the invite.
String defaultPartStat;
if (mbox.getOperationContext() == null) {
// octxt == null implies we're in email delivery. (There needs to be better way to determine this...)
defaultPartStat = IcalXmlStrMap.PARTSTAT_NEEDS_ACTION;
} else {
defaultPartStat = firstInvite.getPartStat();
}
item.processPartStat(firstInvite, pm != null ? pm.getMimeMessage() : null, true, defaultPartStat);
item.finishCreation(null);
folder.updateHighestMODSEQ();
if (pm != null) {
item.createBlob(pm, firstInvite);
}
item.mEndTime = item.recomputeRecurrenceEndTime(item.mEndTime);
if (firstInvite.hasAlarm()) {
item.recomputeNextAlarm(nextAlarm, false, false);
item.saveMetadata();
AlarmData alarmData = item.getAlarmData();
if (alarmData != null) {
long newNextAlarm = alarmData.getNextAtBase();
if (newNextAlarm > 0 && newNextAlarm < item.mStartTime) {
item.mStartTime = newNextAlarm;
}
}
}
DbMailItem.addToCalendarItemTable(item);
Callback cb = getCallback();
if (cb != null) {
cb.created(item);
}
return item;
}
Aggregations