Search in sources :

Example 1 with Invite

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;
            }
        }
    }
}
Also used : IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Recurrence(com.zimbra.cs.mailbox.calendar.Recurrence) InviteInfo(com.zimbra.cs.mailbox.calendar.InviteInfo) RecurrenceRule(com.zimbra.cs.mailbox.calendar.Recurrence.RecurrenceRule) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) ParseException(java.text.ParseException) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 2 with Invite

use of com.zimbra.cs.mailbox.calendar.Invite 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;
}
Also used : Account(com.zimbra.cs.account.Account) AccountAddressMatcher(com.zimbra.cs.util.AccountUtil.AccountAddressMatcher) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) AddressException(javax.mail.internet.AddressException) JavaMailInternetAddress(com.zimbra.common.mime.shim.JavaMailInternetAddress) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 3 with Invite

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();
    }
}
Also used : IOException(java.io.IOException) ItemId(com.zimbra.cs.service.util.ItemId) SetCalendarItem(com.zimbra.cs.redolog.op.SetCalendarItem) ZComponent(com.zimbra.common.calendar.ZCalendar.ZComponent) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) ZProperty(com.zimbra.common.calendar.ZCalendar.ZProperty) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone) CreateInvite(com.zimbra.cs.redolog.op.CreateInvite) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 4 with Invite

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;
}
Also used : Account(com.zimbra.cs.account.Account) CreateCalendarItemRecorder(com.zimbra.cs.redolog.op.CreateCalendarItemRecorder) Invite(com.zimbra.cs.mailbox.calendar.Invite) CreateCalendarItemPlayer(com.zimbra.cs.redolog.op.CreateCalendarItemPlayer)

Example 5 with Invite

use of com.zimbra.cs.mailbox.calendar.Invite in project zm-mailbox by Zimbra.

the class CalendarItem method decodeMetadata.

@Override
void decodeMetadata(Metadata meta) throws ServiceException {
    super.decodeMetadata(meta);
    mUid = Invite.fixupIfOutlookUid(meta.get(Metadata.FN_UID, null));
    mInvites = new ArrayList<Invite>();
    ICalTimeZone accountTZ = Util.getAccountTimeZone(getMailbox().getAccount());
    if (meta.containsKey(Metadata.FN_TZMAP)) {
        try {
            Set<String> tzids = new HashSet<String>();
            mTzMap = Util.decodeFromMetadata(meta.getMap(Metadata.FN_TZMAP), accountTZ);
            // appointment/task start and end
            mStartTime = meta.getLong(Metadata.FN_CALITEM_START, 0);
            mEndTime = meta.getLong(Metadata.FN_CALITEM_END, 0);
            // invite ID's
            long numComp = meta.getLong(Metadata.FN_NUM_COMPONENTS);
            for (int i = 0; i < numComp; i++) {
                Metadata md = meta.getMap(Metadata.FN_INV + i);
                Invite inv = Invite.decodeMetadata(getMailboxId(), md, this, accountTZ);
                mInvites.add(inv);
                tzids.addAll(inv.getReferencedTZIDs());
                mTzMap.add(inv.getTimeZoneMap());
            }
            Metadata metaRecur = meta.getMap(FN_CALITEM_RECURRENCE, true);
            if (metaRecur != null) {
                mRecurrence = Recurrence.decodeMetadata(metaRecur, mTzMap);
                if (mRecurrence != null) {
                    tzids.addAll(Recurrence.getReferencedTZIDs(mRecurrence));
                }
            }
            if (meta.containsKey(Metadata.FN_REPLY_LIST)) {
                mReplyList = ReplyList.decodeFromMetadata(meta.getMap(Metadata.FN_REPLY_LIST), mTzMap);
                // Get all TZIDs referenced by replies.
                for (ReplyInfo ri : mReplyList.mReplies) {
                    if (ri.mRecurId != null) {
                        ParsedDateTime dt = ri.mRecurId.getDt();
                        if (dt != null && dt.hasTime()) {
                            ICalTimeZone tz = dt.getTimeZone();
                            if (tz != null)
                                tzids.add(tz.getID());
                        }
                    }
                }
            } else {
                mReplyList = new ReplyList();
            }
            Metadata metaAlarmData = meta.getMap(Metadata.FN_ALARM_DATA, true);
            if (metaAlarmData != null)
                mAlarmData = AlarmData.decodeMetadata(metaAlarmData);
            // Reduce tzmap to minimal set of TZIDs referenced by invites, recurrence, and replies.
            mTzMap.reduceTo(tzids);
        } catch (ServiceException se) {
            if (ServiceException.INVALID_REQUEST.equals(se.getCode()) && this.getChangeDate() < new GregorianCalendar(2006, 0, 1).getTimeInMillis()) {
                // could have been metadata version 3, 4 or 5.
                // All of those versions have FN_TZMAP, but different format for other fields
                // these are edge cases that should only appear in dev/df/cf
                mStartTime = 0;
                mEndTime = 0;
            } else {
                throw se;
            }
        }
    } else {
        // version 2 or earlier
        mStartTime = 0;
        mEndTime = 0;
    }
}
Also used : GregorianCalendar(java.util.GregorianCalendar) ServiceException(com.zimbra.common.service.ServiceException) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone) HashSet(java.util.HashSet)

Aggregations

Invite (com.zimbra.cs.mailbox.calendar.Invite)105 Account (com.zimbra.cs.account.Account)31 Element (com.zimbra.common.soap.Element)26 ServiceException (com.zimbra.common.service.ServiceException)23 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)23 Mailbox (com.zimbra.cs.mailbox.Mailbox)23 MimeMessage (javax.mail.internet.MimeMessage)23 ZVCalendar (com.zimbra.common.calendar.ZCalendar.ZVCalendar)22 ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)21 ItemId (com.zimbra.cs.service.util.ItemId)21 ArrayList (java.util.ArrayList)20 IOException (java.io.IOException)19 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)16 ZOrganizer (com.zimbra.cs.mailbox.calendar.ZOrganizer)16 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)15 OperationContext (com.zimbra.cs.mailbox.OperationContext)14 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)14 MessagingException (javax.mail.MessagingException)12 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)11 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)11