use of com.zimbra.cs.mailbox.CalendarItem.ReplyInfo in project zm-mailbox by Zimbra.
the class SetCalendarItem method getPrintableData.
@Override
protected String getPrintableData() {
StringBuffer toRet = new StringBuffer();
toRet.append("calItemId=").append(mCalendarItemId);
toRet.append(", calItemPartStat=").append(mCalendarItemPartStat);
toRet.append(", folder=").append(mFolderId);
if (getVersion().atLeast(1, 11)) {
toRet.append(", flags=").append(mFlags);
toRet.append(", tags=").append(TagUtil.encodeTags(mTags));
}
toRet.append("\n");
if (mDefaultInvite != null) {
toRet.append("Default=").append(mDefaultInvite.toString()).append("\n");
}
if (mExceptions != null) {
for (int i = 0; i < mExceptions.length; i++) {
toRet.append("Exception").append(i).append("=").append(mExceptions[i].toString()).append("\n");
}
}
if (mReplies != null) {
int i = 0;
for (ReplyInfo ri : mReplies) {
toRet.append("Reply").append(i).append("=").append(ri.toString()).append("\n");
i++;
}
}
toRet.append("nextAlarm=").append(mNextAlarm).append("\n");
return toRet.toString();
}
use of com.zimbra.cs.mailbox.CalendarItem.ReplyInfo in project zm-mailbox by Zimbra.
the class SetCalendarItem method deserializeData.
@Override
protected void deserializeData(RedoLogInput in) throws IOException {
mFolderId = in.readInt();
if (getVersion().atLeast(1, 0)) {
in.readShort();
}
mCalendarItemId = in.readInt();
if (getVersion().atLeast(1, 1)) {
mCalendarItemPartStat = in.readUTF();
}
if (getVersion().atLeast(1, 2)) {
mAttachmentIndexingEnabled = in.readBoolean();
} else {
mAttachmentIndexingEnabled = false;
}
if (getVersion().atLeast(1, 11)) {
mFlags = in.readInt();
if (getVersion().atLeast(1, 33)) {
mTags = in.readUTFArray();
} else {
mTagBitmask = in.readLong();
}
}
Invite tzmapInv = null;
boolean hasDefaultInvite = true;
if (getVersion().atLeast(1, 17)) {
hasDefaultInvite = in.readBoolean();
}
try {
if (hasDefaultInvite) {
mDefaultInvite = deserializeSetCalendarItemData(in, mAttachmentIndexingEnabled);
tzmapInv = mDefaultInvite.invite;
}
int numExceptions = in.readInt();
if (numExceptions > 0) {
mExceptions = new Mailbox.SetCalendarItemData[numExceptions];
for (int i = 0; i < numExceptions; i++) {
mExceptions[i] = deserializeSetCalendarItemData(in, mAttachmentIndexingEnabled);
if (tzmapInv == null) {
tzmapInv = mExceptions[i].invite;
}
}
}
} catch (MessagingException ex) {
ex.printStackTrace();
throw new IOException("Cannot read serialized entry for SetCalendarItem" + ex.toString());
}
if (getVersion().atLeast(1, 15)) {
int num = in.readInt();
if (num > 10000 && !getVersion().atLeast(1, 24)) {
// exception.
throw new IOException("Replies count > 10000. Looks like a corrupted pre-v1.24 redo entry. Skipping");
}
if (num < 0) {
// no replies list
mReplies = null;
} else {
mReplies = new ArrayList<ReplyInfo>(num);
TimeZoneMap tzMap = tzmapInv.getTimeZoneMap();
for (int i = 0; i < num; i++) {
String data = in.readUTF();
try {
Metadata md = new Metadata(data);
ReplyInfo reply = ReplyInfo.decodeFromMetadata(md, tzMap);
mReplies.add(reply);
} catch (ServiceException e) {
IOException ioe = new IOException("Cannot read serialized entry for ReplyInfo");
ioe.initCause(e);
throw ioe;
}
}
}
}
if (getVersion().atLeast(1, 21)) {
mNextAlarm = in.readLong();
}
}
use of com.zimbra.cs.mailbox.CalendarItem.ReplyInfo 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.CalendarItem.ReplyInfo in project zm-mailbox by Zimbra.
the class CalendarCollection method createItemFromInvites.
/**
* @param name Preferred DAV basename for the new item - including ".ics" if appropriate.
* @param allowUpdate - PUTs are allowed to update a pre-existing item. POSTs to the containing collection are not.
*/
public DavResource createItemFromInvites(DavContext ctxt, Account account, String name, List<Invite> invites, boolean allowUpdate) throws DavException, IOException {
boolean useEtag = allowUpdate;
try {
String user = account.getName();
/*
* Some of the CalDAV clients do not behave very well when it comes to etags.
* chandler doesn't set User-Agent header, doesn't understand If-None-Match or If-Match headers.
* evolution 2.8 always sets If-None-Match although we return etag in REPORT.
* ical correctly understands etag and sets If-Match for existing etags, but does not use If-None-Match
* for new resource creation.
*/
HttpServletRequest req = ctxt.getRequest();
String etag = null;
if (useEtag) {
etag = req.getHeader(DavProtocol.HEADER_IF_MATCH);
useEtag = (etag != null);
}
String baseName = HttpUtil.urlUnescape(name);
boolean acceptableClientChosenBasename = DebugConfig.enableDAVclientCanChooseResourceBaseName && baseName.equals(name);
if (name.endsWith(CalendarObject.CAL_EXTENSION)) {
name = name.substring(0, name.length() - CalendarObject.CAL_EXTENSION.length());
// Unescape the name (It was encoded in DavContext intentionally)
name = HttpUtil.urlUnescape(name);
}
String uid = findEventUid(invites);
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(account);
CalendarItem origCalItem = null;
// Is the basename of the path client assigned rather than following the standard pattern?
Integer itemId = null;
if (acceptableClientChosenBasename) {
itemId = DavNames.get(this.mMailboxId, this.mId, baseName);
}
if (itemId != null) {
try {
MailItem mailItem = mbox.getItemById(ctxt.getOperationContext(), itemId, MailItem.Type.UNKNOWN);
if (mailItem != null && mailItem instanceof CalendarItem) {
origCalItem = (CalendarItem) mailItem;
}
} catch (ServiceException se) {
}
}
if (origCalItem == null) {
if (uid.equals(name)) {
origCalItem = mbox.getCalendarItemByUid(ctxt.getOperationContext(), name);
} else {
/* the basename of the path doesn't fit our preferred naming convention. */
origCalItem = mbox.getCalendarItemByUid(ctxt.getOperationContext(), uid);
String redirectUrl = null;
if (origCalItem != null) {
if (this.mId != origCalItem.getFolderId()) {
// In another folder, ignore
origCalItem = null;
} else {
// The item exists, but doesn't have this name - UID conflict.
if (acceptableClientChosenBasename) {
redirectUrl = hrefForCalendarItem(origCalItem, user, uid);
} else {
redirectUrl = defaultUrlForCalendarItem(user, uid);
}
throw new DavException.UidConflict("An item with the same UID already exists in the calendar", redirectUrl);
}
}
if ((origCalItem == null) && (!DebugConfig.enableDAVclientCanChooseResourceBaseName)) {
redirectUrl = defaultUrlForCalendarItem(user, uid);
}
if (allowUpdate && (redirectUrl != null)) {
/* SC_FOUND - Status code (302) indicating that the resource reside temporarily under a
* different URI. Since the redirection might be altered on occasion, the client should
* continue to use the Request-URI for future requests.(HTTP/1.1) To represent the status code
* (302), it is recommended to use this variable. Used to be called SC_MOVED_TEMPORARILY
*/
// sets status to SC_FOUND
ctxt.getResponse().sendRedirect(redirectUrl);
StringBuilder wrongUrlMsg = new StringBuilder();
wrongUrlMsg.append("wrong url - redirecting to:\n").append(redirectUrl);
throw new DavException(wrongUrlMsg.toString(), HttpServletResponse.SC_FOUND, null);
}
}
}
if (origCalItem == null && useEtag) {
throw new DavException("event not found", HttpServletResponse.SC_NOT_FOUND, null);
}
if (origCalItem != null && !allowUpdate) {
throw new DavException.UidConflict("An item with the same UID already exists in the calendar", hrefForCalendarItem(origCalItem, user, uid));
}
boolean isNewItem = true;
if (useEtag) {
String itemEtag = MailItemResource.getEtag(origCalItem);
if (!itemEtag.equals(etag)) {
throw new DavException(String.format("CalDAV client has stale event: event has different etag (%s) vs %s", itemEtag, etag), HttpServletResponse.SC_PRECONDITION_FAILED);
}
isNewItem = false;
}
// prepare to call Mailbox.setCalendarItem()
int flags = 0;
String[] tags = null;
List<ReplyInfo> replies = null;
Invite[] origInvites = null;
if (origCalItem != null) {
flags = origCalItem.getFlagBitmask();
tags = origCalItem.getTags();
replies = origCalItem.getAllReplies();
origInvites = origCalItem.getInvites();
}
SetCalendarItemData scidDefault = new SetCalendarItemData();
SetCalendarItemData[] scidExceptions = null;
int idxExceptions = 0;
boolean first = true;
for (Invite i : invites) {
// check for valid uid.
if (i.getUid() == null)
i.setUid(uid);
adjustOrganizer(ctxt, i);
// Carry over the MimeMessage/ParsedMessage to preserve any attachments.
// CalDAV clients don't support attachments, and on edit we have to either
// retain existing attachments or drop them. Retaining is better.
ParsedMessage oldPm = null;
if (origCalItem != null) {
Invite oldInv = origCalItem.getInvite(i.getRecurId());
if (oldInv == null && i.hasRecurId()) {
// It's a new exception instance. Inherit from series.
oldInv = origCalItem.getInvite((RecurId) null);
}
if (oldInv != null) {
MimeMessage mmInv = origCalItem.getSubpartMessage(oldInv.getMailItemId());
oldPm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
}
}
if (first) {
scidDefault.invite = i;
scidDefault.message = oldPm;
first = false;
} else {
SetCalendarItemData scid = new SetCalendarItemData();
scid.invite = i;
scid.message = oldPm;
if (scidExceptions == null) {
scidExceptions = new SetCalendarItemData[invites.size() - 1];
}
scidExceptions[idxExceptions++] = scid;
}
// For attendee case, update replies list with matching ATTENDEE from the invite.
if (!i.isOrganizer() && replies != null) {
ZAttendee at = i.getMatchingAttendee(account);
if (at != null) {
AccountAddressMatcher acctMatcher = new AccountAddressMatcher(account);
ReplyInfo newReply = null;
for (Iterator<ReplyInfo> replyIter = replies.iterator(); replyIter.hasNext(); ) {
ReplyInfo reply = replyIter.next();
if (acctMatcher.matches(reply.getAttendee().getAddress())) {
RecurId ridR = reply.getRecurId(), ridI = i.getRecurId();
if ((ridR == null && ridI == null) || (ridR != null && ridR.equals(ridI))) {
// matching RECURRENCE-ID
// No need to compare SEQUENCE and DTSTAMP of existing reply and new invite.
// We're just going to take what the caldav client sent, even if it's older
// than the existing reply.
replyIter.remove();
if (!IcalXmlStrMap.PARTSTAT_NEEDS_ACTION.equalsIgnoreCase(at.getPartStat())) {
newReply = new ReplyInfo(at, i.getSeqNo(), i.getDTStamp(), ridI);
}
break;
}
}
}
if (newReply != null) {
replies.add(newReply);
}
}
}
}
CalendarItem newCalItem = null;
AutoScheduler autoScheduler = AutoScheduler.getAutoScheduler(mbox, this.getCalendarMailbox(ctxt), origInvites, mId, flags, tags, scidDefault, scidExceptions, replies, ctxt);
if (autoScheduler == null) {
newCalItem = mbox.setCalendarItem(ctxt.getOperationContext(), mId, flags, tags, scidDefault, scidExceptions, replies, CalendarItem.NEXT_ALARM_KEEP_CURRENT);
} else {
// Note: This also sets the calendar item
newCalItem = autoScheduler.doSchedulingActions();
}
if (newCalItem == null) {
throw new DavException("cannot create icalendar item - corrupt ICAL?", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
if (!uid.equals(name)) {
if (acceptableClientChosenBasename) {
DavNames.put(DavNames.DavName.create(this.mMailboxId, newCalItem.getFolderId(), baseName), newCalItem.getId());
}
}
return new CalendarObject.LocalCalendarObject(ctxt, newCalItem, isNewItem);
} catch (BadOrganizerException.DiffOrganizerInComponentsException e) {
throw new DavException.NeedSameOrganizerInAllComponents(e.getMessage());
} catch (BadOrganizerException e) {
// FORBIDDEN if we aren't going to be able to cope with the data
throw new DavException(e.getMessage(), HttpServletResponse.SC_FORBIDDEN, e);
} catch (ServiceException e) {
if (e.getCode().equals(ServiceException.FORBIDDEN)) {
throw new DavException(e.getMessage(), HttpServletResponse.SC_FORBIDDEN, e);
} else {
throw new DavException("cannot create icalendar item", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
}
}
use of com.zimbra.cs.mailbox.CalendarItem.ReplyInfo in project zm-mailbox by Zimbra.
the class SetCalendarItem method serializeData.
@Override
protected void serializeData(RedoLogOutput out) throws IOException {
assert (getMailboxId() != 0);
out.writeInt(mFolderId);
if (getVersion().atLeast(1, 0)) {
out.writeShort((short) -1);
}
out.writeInt(mCalendarItemId);
if (getVersion().atLeast(1, 1)) {
out.writeUTF(mCalendarItemPartStat);
}
if (getVersion().atLeast(1, 2)) {
out.writeBoolean(mAttachmentIndexingEnabled);
}
if (getVersion().atLeast(1, 11)) {
out.writeInt(mFlags);
if (getVersion().atLeast(1, 33)) {
out.writeUTFArray(mTags);
} else {
out.writeLong(mTagBitmask);
}
}
boolean hasDefaultInvite = mDefaultInvite != null;
if (getVersion().atLeast(1, 17))
out.writeBoolean(hasDefaultInvite);
if (hasDefaultInvite) {
serializeSetCalendarItemData(out, mDefaultInvite);
}
if (mExceptions == null) {
out.writeInt(0);
} else {
out.writeInt(mExceptions.length);
for (int i = 0; i < mExceptions.length; i++) {
serializeSetCalendarItemData(out, mExceptions[i]);
}
}
if (getVersion().atLeast(1, 15)) {
if (mReplies == null) {
// special case: -1 means there was no replies list. This is distinct from there
// being a non-null list with 0 replies. No list means to leave current replies
// alone; 0-length list means to clear current list.
out.writeInt(-1);
} else {
int num = mReplies.size();
out.writeInt(num);
for (ReplyInfo ri : mReplies) {
out.writeUTF(ri.encodeAsMetadata().toString());
}
}
}
if (getVersion().atLeast(1, 21)) {
out.writeLong(mNextAlarm);
}
}
Aggregations