use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Mailbox method importFeedInternal.
private void importFeedInternal(OperationContext octxt, Folder folder, boolean subscription, FeedManager.SubscriptionData<?> sdata) throws ServiceException {
assert (lock.isWriteLockedByCurrentThread());
// If syncing a folder with calendar items, remember the current items. After applying the new
// appointments/tasks, we need to remove ones that were not updated because they are apparently
// deleted from the source feed.
boolean isCalendar = folder.getDefaultView() == MailItem.Type.APPOINTMENT || folder.getDefaultView() == MailItem.Type.TASK;
Set<Integer> toRemove = new HashSet<Integer>();
if (subscription && isCalendar) {
for (int i : listItemIds(octxt, MailItem.Type.UNKNOWN, folder.getId())) {
toRemove.add(i);
}
}
// if there's nothing to add, we can short-circuit here
List<?> items = sdata.getItems();
if (items.isEmpty()) {
if (subscription && isCalendar) {
// quicker than deleting appointments one at a time
emptyFolder(octxt, folder.getId(), false);
}
updateRssDataSource(folder);
return;
}
// disable modification conflict checks, as we've already wiped the folder and we may hit an appoinment >1 times
OperationContext octxtNoConflicts = null;
if (octxt != null) {
octxtNoConflicts = new OperationContext(octxt).unsetChangeConstraint();
} else {
octxtNoConflicts = new OperationContext(getAccountId()).unsetChangeConstraint();
}
// add the newly-fetched items to the folder
Set<String> calUidsSeen = new HashSet<String>();
int numSkipped = 0;
for (Object obj : items) {
try {
if (obj instanceof Invite) {
Invite inv = (Invite) obj;
String uid = inv.getUid();
if (uid == null) {
uid = UUIDUtil.generateUUID();
inv.setUid(uid);
}
// Create the event in accepted state. (bug 41639)
inv.setPartStat(IcalXmlStrMap.PARTSTAT_ACCEPTED);
inv.setRsvp(false);
boolean addRevision;
if (!calUidsSeen.contains(uid)) {
addRevision = true;
calUidsSeen.add(uid);
} else {
addRevision = false;
}
try {
boolean importIt;
CalendarItem calItem = getCalendarItemByUid(octxtNoConflicts, uid);
if (calItem == null) {
// New appointment. Import it.
importIt = true;
} else {
toRemove.remove(calItem.getId());
Folder curFolder = calItem.getFolder();
boolean sameFolder = curFolder.getId() == folder.getId();
boolean inTrashOrSpam = !sameFolder && (curFolder.inTrash() || curFolder.inSpam());
if (inTrashOrSpam) {
// If appointment is under trash/spam, delete it now to allow the downloaded invite to
// be imported cleanly. Appointment in trash/spam is effectively non-existent, because
// it will eventually get purged.
delete(octxtNoConflicts, calItem.getId(), MailItem.Type.UNKNOWN);
importIt = true;
} else {
Invite[] oldInvites = calItem.getInvites();
if ((oldInvites == null) || (oldInvites.length == 0)) {
// Something is seriously wrong with the existing calendar item. Delete it so
// new invite will import cleanly
delete(octxtNoConflicts, calItem.getId(), MailItem.Type.UNKNOWN);
importIt = true;
} else {
// Don't import if item is in a different folder. It might be a regular appointment, and
// should not be overwritten by a feed version. (bug 14306)
// Import only if downloaded version is newer.
boolean changed;
Invite curInv = calItem.getInvite(inv.getRecurId());
if (curInv == null) {
// We have an appointment with the same UID, but don't have an invite
// with the same RECURRENCE-ID. Treat it as a changed item.
changed = true;
} else {
if (inv.getSeqNo() > curInv.getSeqNo()) {
changed = true;
} else if (inv.getSeqNo() == curInv.getSeqNo()) {
// Compare LAST-MODIFIED rather than DTSTAMP. (bug 55735)
changed = inv.getLastModified() > curInv.getLastModified();
} else {
changed = false;
}
}
importIt = sameFolder && changed;
if (!importIt && ZimbraLog.calendar.isDebugEnabled()) {
if (sameFolder) {
ZimbraLog.calendar.debug("Skip importing UID=%s. Already present & not newer. SEQUENCE/LAST-MODIFIED old=%s,%s new=%s,%s", uid, curInv.getSeqNo(), curInv.getLastModified(), inv.getSeqNo(), inv.getLastModified());
} else {
ZimbraLog.calendar.debug("Skip importing UID=%s. Already in different folder id=%d", uid, curFolder.getId());
}
}
}
}
}
if (importIt) {
addInvite(octxtNoConflicts, inv, folder.getId(), true, addRevision);
} else {
numSkipped++;
}
} catch (ServiceException e) {
ZimbraLog.calendar.warn("Skipping bad iCalendar object UID=%s during import into folder id=%d", inv.getUid(), folder.getId(), e);
}
} else if (obj instanceof ParsedMessage) {
DeliveryOptions dopt = new DeliveryOptions().setFolderId(folder).setNoICal(true).setFlags(Flag.BITMASK_UNREAD);
addMessage(octxtNoConflicts, (ParsedMessage) obj, dopt, null);
}
} catch (IOException e) {
throw ServiceException.FAILURE("IOException", e);
}
}
if (numSkipped > 0) {
ZimbraLog.calendar.warn("Skipped importing %d iCalendar objects with clashing UIDs", numSkipped);
}
// Delete calendar items that have been deleted in the source feed.
for (int i : toRemove) {
delete(octxtNoConflicts, i, MailItem.Type.UNKNOWN);
}
// update the subscription to avoid downloading items twice
long lastModDate = sdata.getLastModifiedDate();
if (subscription && lastModDate > 0) {
try {
setSubscriptionData(octxt, folder.getId(), lastModDate, sdata.getMostRecentGuid());
} catch (Exception e) {
ZimbraLog.mailbox.warn("could not update feed metadata", e);
}
}
updateRssDataSource(folder);
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Mailbox method writeICalendarForCalendarItems.
public void writeICalendarForCalendarItems(Writer writer, OperationContext octxt, Collection<CalendarItem> calItems, Folder f, boolean useOutlookCompatMode, boolean ignoreErrors, boolean needAppleICalHacks, boolean trimCalItemsList, boolean escapeHtmlTags, boolean includeAttaches) throws ServiceException {
lock.lock();
try {
writer.write("BEGIN:VCALENDAR\r\n");
if (f != null) {
writer.write("X-WR-CALNAME:");
writer.write(f.getName());
writer.write("\r\n");
writer.write("X-WR-CALID:");
writer.write(new ItemId(f).toString());
writer.write("\r\n");
}
ZProperty prop;
prop = new ZProperty(ICalTok.PRODID, ZCalendar.sZimbraProdID);
prop.toICalendar(writer, needAppleICalHacks);
prop = new ZProperty(ICalTok.VERSION, ZCalendar.sIcalVersion);
prop.toICalendar(writer, needAppleICalHacks);
prop = new ZProperty(ICalTok.METHOD, ICalTok.PUBLISH.toString());
prop.toICalendar(writer, needAppleICalHacks);
// timezones
ICalTimeZone localTz = Util.getAccountTimeZone(getAccount());
TimeZoneMap tzmap = new TimeZoneMap(localTz);
for (CalendarItem calItem : calItems) {
tzmap.add(calItem.getTimeZoneMap());
}
// iterate the tzmap and add all the VTimeZone's
for (Iterator<ICalTimeZone> iter = tzmap.tzIterator(); iter.hasNext(); ) {
ICalTimeZone tz = iter.next();
tz.newToVTimeZone().toICalendar(writer, needAppleICalHacks);
}
// help keep memory consumption low
tzmap = null;
// build all the event components and add them to the Calendar
for (Iterator<CalendarItem> iter = calItems.iterator(); iter.hasNext(); ) {
CalendarItem calItem = iter.next();
boolean allowPrivateAccess = calItem.isPublic() || calItem.allowPrivateAccess(octxt.getAuthenticatedUser(), octxt.isUsingAdminPrivileges());
if (trimCalItemsList) {
// help keep memory consumption low
iter.remove();
}
Invite[] invites = calItem.getInvites();
if (invites != null && invites.length > 0) {
boolean appleICalExdateHack = LC.calendar_apple_ical_compatible_canceled_instances.booleanValue();
ZComponent[] comps = null;
try {
comps = Invite.toVComponents(invites, allowPrivateAccess, useOutlookCompatMode, appleICalExdateHack, includeAttaches);
} catch (ServiceException e) {
if (ignoreErrors) {
ZimbraLog.calendar.warn("Error retrieving iCalendar data for item %s: %s", calItem.getId(), e.getMessage(), e);
} else {
throw e;
}
}
if (comps != null) {
for (ZComponent comp : comps) {
comp.toICalendar(writer, needAppleICalHacks, escapeHtmlTags);
}
}
}
}
writer.write("END:VCALENDAR\r\n");
} catch (IOException e) {
throw ServiceException.FAILURE("Error writing iCalendar", e);
} finally {
lock.release();
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Mailbox method processICalReplies.
private void processICalReplies(OperationContext octxt, ZVCalendar cal, String sender) throws ServiceException {
// Reply from Outlook will usually have PRODID set to the following:
//
// Outlook2007+ZCO: PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN
// Outlook2010+ZCO: PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
// Outlook20xx+Exchange: PRODID:Microsoft Exchange Server 2007
// (if Exchange is Exchange 2007; Exchange 2010 probably works similarly)
//
// Lowest common denominator is "Microsoft" substring.
String prodId = cal.getPropVal(ICalTok.PRODID, null);
boolean fromOutlook = prodId != null && prodId.toLowerCase().contains("microsoft");
AccountAddressMatcher acctMatcher = new AccountAddressMatcher(getAccount());
List<Invite> components = Invite.createFromCalendar(getAccount(), null, cal, false);
for (Invite inv : components) {
String orgAddress;
if (inv.hasOrganizer()) {
ZOrganizer org = inv.getOrganizer();
orgAddress = org.getAddress();
} else {
ZimbraLog.calendar.warn("No ORGANIZER found in REPLY. Assuming current mailbox.");
orgAddress = getAccount().getName();
}
if (acctMatcher.matches(orgAddress)) {
// RECURRENCE-ID.
if (fromOutlook && !inv.isAllDayEvent() && inv.hasRecurId()) {
RecurId rid = inv.getRecurId();
if (rid.getDt() != null && rid.getDt().hasZeroTime()) {
CalendarItem calItem = getCalendarItemByUid(octxt, inv.getUid());
if (calItem != null) {
Invite seriesInv = calItem.getDefaultInviteOrNull();
if (seriesInv != null) {
ParsedDateTime seriesDtStart = seriesInv.getStartTime();
if (seriesDtStart != null) {
ParsedDateTime fixedDt = seriesDtStart.cloneWithNewDate(rid.getDt());
RecurId fixedRid = new RecurId(fixedDt, rid.getRange());
ZimbraLog.calendar.debug("Fixed up invalid RECURRENCE-ID with zero time; before=[%s], after=[%s]", rid, fixedRid);
inv.setRecurId(fixedRid);
}
}
}
}
}
processICalReply(octxt, inv, sender);
} else {
Account orgAccount = inv.getOrganizerAccount();
// Unknown organizer
if (orgAccount == null) {
ZimbraLog.calendar.warn("Unknown organizer " + orgAddress + " in REPLY");
continue;
}
if (Provisioning.onLocalServer(orgAccount)) {
// Run in the context of organizer's mailbox.
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(orgAccount);
OperationContext orgOctxt = new OperationContext(mbox);
mbox.processICalReply(orgOctxt, inv, sender);
} else {
// Organizer's mailbox is on a remote server.
String uri = AccountUtil.getSoapUri(orgAccount);
if (uri == null) {
ZimbraLog.calendar.warn("Unable to determine URI for organizer account %s", orgAddress);
continue;
}
try {
// TODO: Get the iCalendar data from the
// MIME part since we already have it.
String ical;
StringWriter sr = null;
try {
sr = new StringWriter();
inv.setMethod(ICalTok.REPLY.toString());
inv.newToICalendar(true).toICalendar(sr);
ical = sr.toString();
} finally {
if (sr != null) {
sr.close();
}
}
Options options = new Options();
AuthToken authToken = AuthToken.getCsrfUnsecuredAuthToken(getAuthToken(octxt));
options.setAuthToken(authToken.toZAuthToken());
options.setTargetAccount(orgAccount.getName());
options.setTargetAccountBy(AccountBy.name);
options.setUri(uri);
options.setNoSession(true);
ZMailbox zmbox = ZMailbox.getMailbox(options);
zmbox.iCalReply(ical, sender);
} catch (IOException e) {
throw ServiceException.FAILURE("Error while posting REPLY to organizer mailbox host", e);
}
}
}
}
}
use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.
the class Task method processPartStat.
@Override
protected String processPartStat(Invite invite, MimeMessage mmInv, boolean forCreate, String defaultPartStat) throws ServiceException {
Mailbox mbox = getMailbox();
OperationContext octxt = mbox.getOperationContext();
CreateCalendarItemPlayer player = octxt != null ? (CreateCalendarItemPlayer) octxt.getPlayer() : null;
String partStat = defaultPartStat;
if (player != null) {
String p = player.getCalendarItemPartStat();
if (p != null)
partStat = p;
}
CreateCalendarItemRecorder recorder = (CreateCalendarItemRecorder) mbox.getRedoRecorder();
recorder.setCalendarItemPartStat(partStat);
Account account = getMailbox().getAccount();
invite.updateMyPartStat(account, partStat);
if (forCreate) {
Invite defaultInvite = getDefaultInviteOrNull();
if (defaultInvite != null && !defaultInvite.equals(invite) && !partStat.equals(defaultInvite.getPartStat())) {
defaultInvite.updateMyPartStat(account, partStat);
saveMetadata();
}
}
return partStat;
}
use of com.zimbra.cs.mailbox.calendar.Invite 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();
}
}
Aggregations