use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class GetICal method handle.
/* (non-Javadoc)
* @see com.zimbra.soap.DocumentHandler#handle(org.dom4j.Element, java.util.Map)
*/
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Account authAccount = getAuthenticatedAccount(zsc);
Mailbox mbx = getRequestedMailbox(zsc);
OperationContext octxt = getOperationContext(zsc, context);
String iidStr = request.getAttribute(MailConstants.A_ID, null);
long rangeStart = request.getAttributeLong(MailConstants.A_CAL_START_TIME, -1);
long rangeEnd = request.getAttributeLong(MailConstants.A_CAL_END_TIME, -1);
// int compNum = (int)request.getAttributeLong(MailService.A_CAL_COMPONENT_NUM);
int compNum = 0;
Browser browser = HttpUtil.guessBrowser(zsc.getUserAgent());
boolean useOutlookCompatMode = Browser.IE.equals(browser);
try {
try {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
OutputStreamWriter wout = new OutputStreamWriter(buf);
ZVCalendar cal = null;
ItemId iid = iidStr != null ? new ItemId(iidStr, zsc) : null;
if (iid != null) {
CalendarItem calItem = mbx.getCalendarItemById(octxt, iid.getId());
if (calItem == null) {
throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
}
Invite inv = calItem.getInvite(iid.getSubpartId(), compNum);
if (inv == null) {
throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
}
boolean allowPrivateAccess = calItem.allowPrivateAccess(authAccount, zsc.isUsingAdminPrivileges());
cal = inv.newToICalendar(useOutlookCompatMode, allowPrivateAccess);
cal.toICalendar(wout);
} else {
mbx.writeICalendarForRange(wout, octxt, rangeStart, rangeEnd, Mailbox.ID_FOLDER_CALENDAR, useOutlookCompatMode, false, false);
}
wout.flush();
Element response = getResponseElement(zsc);
Element icalElt = response.addElement(MailConstants.E_CAL_ICAL);
if (iid != null)
icalElt.addAttribute(MailConstants.A_ID, new ItemIdFormatter(zsc).formatItemId(iid));
icalElt.addText(buf.toString());
return response;
} catch (MailServiceException.NoSuchItemException e) {
throw ServiceException.FAILURE("Error could get default invite for Invite: " + iidStr + "-" + compNum, e);
} catch (IOException e) {
throw ServiceException.FAILURE("IO Exception while outputing Calendar for Invite: " + iidStr + "-" + compNum, e);
}
} catch (MailServiceException.NoSuchItemException e) {
throw ServiceException.FAILURE("No Such Invite Message: " + iidStr, e);
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Mailbox method setCalendarItem.
/**
* @param exceptions can be NULL
* @return calendar item ID
*/
public CalendarItem setCalendarItem(OperationContext octxt, int folderId, int flags, String[] tags, SetCalendarItemData defaultInv, SetCalendarItemData[] exceptions, List<ReplyInfo> replies, long nextAlarm) throws ServiceException {
flags = (flags & ~Flag.FLAGS_SYSTEM);
SetCalendarItem redoRecorder = new SetCalendarItem(getId(), attachmentsIndexingEnabled(), flags, tags);
boolean success = false;
try {
beginTransaction("setCalendarItem", octxt, redoRecorder);
Tag.NormalizedTags ntags = new Tag.NormalizedTags(this, tags);
// Make a single list containing default and exceptions.
int scidLen = (defaultInv != null ? 1 : 0) + (exceptions != null ? exceptions.length : 0);
List<SetCalendarItemData> scidList = new ArrayList<SetCalendarItemData>(scidLen);
if (defaultInv != null) {
defaultInv.invite.setLastFullSeqNo(defaultInv.invite.getSeqNo());
scidList.add(defaultInv);
}
if (exceptions != null) {
for (SetCalendarItemData scid : exceptions) {
scid.invite.setLastFullSeqNo(scid.invite.getSeqNo());
scidList.add(scid);
}
}
CalendarItem calItem = null;
// old invId information.
if (!scidList.isEmpty()) {
calItem = getCalendarItemByUid(octxt, scidList.get(0).invite.getUid());
for (SetCalendarItemData scid : scidList) {
int idBeingSet = scid.invite.getMailItemId();
if (idBeingSet <= 0) {
if (calItem != null) {
Invite currInv = calItem.getInvite(scid.invite.getRecurId());
if (currInv != null) {
scid.invite.setInviteId(currInv.getMailItemId());
// Carry over local-only setting.
boolean currLO = currInv.isLocalOnly();
boolean newLO = scid.invite.isLocalOnly();
scid.invite.setLocalOnly(currLO && newLO);
} else {
scid.invite.setInviteId(getNextItemId(Mailbox.ID_AUTO_INCREMENT));
}
} else {
scid.invite.setInviteId(getNextItemId(Mailbox.ID_AUTO_INCREMENT));
}
}
}
// if new version doesn't specify it. (bug 41002)
if (calItem != null && calItem.getFolderId() != Mailbox.ID_FOLDER_TRASH) {
Invite currSeries = calItem.getDefaultInviteOrNull();
for (SetCalendarItemData scid : scidList) {
if (!scid.invite.hasFreeBusy()) {
Invite currInv = calItem.getInvite(scid.invite.getRecurId());
if (currInv == null) {
// Inherit from series as fallback.
currInv = currSeries;
}
if (currInv != null && currInv.hasFreeBusy()) {
if (scid.invite.isTransparent()) {
// New invite is transparent. New intended f/b must be free.
scid.invite.setFreeBusy(IcalXmlStrMap.FBTYPE_FREE);
} else {
// New invite is opaque.
if (IcalXmlStrMap.FBTYPE_FREE.equals(currInv.getFreeBusy())) {
// An opaque invite cannot have intended f/b value of free. Make it busy.
scid.invite.setFreeBusy(IcalXmlStrMap.FBTYPE_BUSY);
} else {
// Current intended f/b has a non-free value, so keep it. It's better
// to preserve tentative or OOO value than to unconditionally change to busy.
scid.invite.setFreeBusy(currInv.getFreeBusy());
}
}
}
}
}
}
}
// trace logging
if (!scidList.isEmpty()) {
Invite invLog = scidList.get(0).invite;
String idStr = calItem != null ? Integer.toString(calItem.getId()) : "(new)";
ZimbraLog.calendar.info("setCalendarItem: id=%s, folderId=%d, subject=\"%s\", UID=%s", idStr, folderId, (invLog != null && invLog.isPublic() ? invLog.getName() : "(private)"), invLog.getUid());
}
redoRecorder.setData(defaultInv, exceptions, replies, nextAlarm);
AlarmData oldAlarmData = null;
for (SetCalendarItemData scid : scidList) {
if (scid.message == null) {
// the MimeMessage is fake, so we don't need to index it
scid.invite.setDontIndexMimeMessage(true);
String desc = scid.invite.getDescription();
if (desc != null && desc.length() > Invite.getMaxDescInMeta()) {
MimeMessage mm = CalendarMailSender.createCalendarMessage(scid.invite);
scid.message = new ParsedMessage(mm, octxt == null ? System.currentTimeMillis() : octxt.getTimestamp(), true);
}
}
}
if (scidList.size() > 0) {
SetCalendarItemData scid = scidList.get(0);
if (calItem == null) {
// ONLY create an calendar item if this is a REQUEST method...otherwise don't.
String method = scid.invite.getMethod();
if ("REQUEST".equals(method) || "PUBLISH".equals(method)) {
try {
calItem = createCalendarItem(folderId, flags, ntags, scid.invite.getUid(), scid.message, scid.invite, null);
} catch (MailServiceException e) {
if (e.getCode() == MailServiceException.ALREADY_EXISTS) {
//bug 49106 - did not find the appointment above in getCalendarItemByUid(), but the mail_item exists
ZimbraLog.calendar.error("failed to create calendar item; already exists. cause: " + (scidList.isEmpty() ? "no items in uid list." : "uid not found in appointment: " + scidList.get(0).invite.getUid() + " or bad mail_item type"));
}
throw e;
}
} else {
// for now, just ignore this Invitation
return null;
}
} else {
calItem.snapshotRevision();
// Preserve alarm time before any modification is made to the item.
if (calItem.getAlarmData() != null) {
oldAlarmData = (AlarmData) calItem.getAlarmData().clone();
}
calItem.setTags(flags, ntags);
calItem.processNewInvite(scid.message, scid.invite, folderId, nextAlarm, false, true);
}
redoRecorder.setCalendarItemAttrs(calItem.getId(), calItem.getFolderId());
}
if (scidList.size() > 1) {
// remove the first one. it is already processed
scidList.remove(0);
// exceptions
calItem.processNewInviteExceptions(scidList, folderId, nextAlarm, false, false);
}
// Recompute alarm time after processing all Invites.
if (nextAlarm == CalendarItem.NEXT_ALARM_KEEP_CURRENT) {
nextAlarm = oldAlarmData != null ? oldAlarmData.getNextAt() : CalendarItem.NEXT_ALARM_FROM_NOW;
}
calItem.updateNextAlarm(nextAlarm, oldAlarmData, false);
// List with one or more replies means replacing existing replies.
if (replies != null) {
calItem.setReplies(replies);
}
index.add(calItem);
success = true;
return calItem;
} finally {
endTransaction(success);
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarRequest method notifyCalendarItem.
// Notify attendees following an update to the series of a recurring appointment. Only the
// added attendees are notified if notifyAllAttendees is false. If it is true all attendees
// for each invite are notified. (Some invites may have more attendees than others.)
protected static void notifyCalendarItem(ZimbraSoapContext zsc, OperationContext octxt, Account acct, Mailbox mbox, CalendarItem calItem, boolean notifyAllAttendees, List<ZAttendee> addedAttendees, boolean ignorePastExceptions, MailSendQueue sendQueue) throws ServiceException {
boolean onBehalfOf = isOnBehalfOfRequest(zsc);
Account authAcct = getAuthenticatedAccount(zsc);
boolean hidePrivate = !calItem.isPublic() && !calItem.allowPrivateAccess(authAcct, zsc.isUsingAdminPrivileges());
Address from = AccountUtil.getFriendlyEmailAddress(acct);
Address sender = null;
if (onBehalfOf)
sender = AccountUtil.getFriendlyEmailAddress(authAcct);
List<Address> addedRcpts = CalendarMailSender.toListFromAttendees(addedAttendees);
long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
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());
Invite[] invites = calItem.getInvites();
// Get exception instances. These will be included in the series update email.
List<Invite> exceptions = new ArrayList<Invite>();
for (Invite inv : invites) {
if (inv.hasRecurId()) {
exceptions.add(inv);
}
}
// Send the update invites.
boolean didExceptions = false;
for (Invite inv : invites) {
if (ignorePastExceptions && inv.hasRecurId() && !inviteIsAfterTime(inv, now)) {
continue;
}
// Make the new iCalendar part to send.
ZVCalendar cal = inv.newToICalendar(!hidePrivate);
// For series invite, append the exception instances.
if (inv.isRecurrence() && !didExceptions) {
// Find the VEVENT/VTODO for the series.
ZComponent seriesComp = null;
for (Iterator<ZComponent> compIter = cal.getComponentIterator(); compIter.hasNext(); ) {
ZComponent comp = compIter.next();
ICalTok compName = comp.getTok();
if (ICalTok.VEVENT.equals(compName) || ICalTok.VTODO.equals(compName)) {
if (comp.getProperty(ICalTok.RRULE) != null) {
seriesComp = comp;
break;
}
}
}
for (Invite except : exceptions) {
if (except.isCancel() && seriesComp != null) {
// Cancels are added as EXDATEs in the series VEVENT/VTODO.
RecurId rid = except.getRecurId();
if (rid != null && rid.getDt() != null) {
ZProperty exdate = rid.getDt().toProperty(ICalTok.EXDATE, false);
seriesComp.addProperty(exdate);
}
} else {
// Exception instances are added as additional VEVENTs/VTODOs.
ZComponent exceptComp = except.newToVComponent(false, !hidePrivate);
cal.addComponent(exceptComp);
}
}
didExceptions = true;
}
// Compose email using the existing MimeMessage as template and send it.
MimeMessage mmInv = calItem.getSubpartMessage(inv.getMailItemId());
List<Address> rcpts;
if (notifyAllAttendees) {
rcpts = CalendarMailSender.toListFromAttendees(inv.getAttendees());
} else {
rcpts = addedRcpts;
}
if (rcpts != null && !rcpts.isEmpty()) {
MimeMessage mmModify = CalendarMailSender.createCalendarMessage(authAcct, from, sender, rcpts, mmInv, inv, cal, true);
CalSendData csd = new CalSendData();
csd.mMm = mmModify;
csd.mOrigId = new ItemId(mbox, inv.getMailItemId());
MailSendQueueEntry entry = new MailSendQueueEntry(octxt, mbox, csd, null);
sendQueue.add(entry);
}
}
} finally {
mbox.lock.release();
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class CalendarUtils method parseInviteForDeclineCounter.
static ParseMimeMessage.InviteParserResult parseInviteForDeclineCounter(Account account, MailItem.Type type, Element inviteElem) throws ServiceException {
TimeZoneMap tzMap = new TimeZoneMap(Util.getAccountTimeZone(account));
Invite inv = new Invite(ICalTok.DECLINECOUNTER.toString(), tzMap, false);
CalendarUtils.parseInviteElementCommon(account, type, inviteElem, inv, true, true);
// UID
String uid = inv.getUid();
if (uid == null || uid.length() == 0)
throw ServiceException.INVALID_REQUEST("Missing uid in a decline counter invite", null);
// ORGANIZER
if (!inv.hasOrganizer())
throw ServiceException.INVALID_REQUEST("Missing organizer in a decline counter invite", null);
// DTSTAMP
if (inv.getDTStamp() == 0) {
//zdsync
inv.setDtStamp(new Date().getTime());
}
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;
}
use of com.zimbra.cs.mailbox.calendar.Invite 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;
}
Aggregations