Search in sources :

Example 1 with ZAttendee

use of com.zimbra.cs.mailbox.calendar.ZAttendee 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 2 with ZAttendee

use of com.zimbra.cs.mailbox.calendar.ZAttendee 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;
}
Also used : Element(com.zimbra.common.soap.Element) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) ArrayList(java.util.ArrayList) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) ReplyInfo(com.zimbra.cs.mailbox.CalendarItem.ReplyInfo)

Example 3 with ZAttendee

use of com.zimbra.cs.mailbox.calendar.ZAttendee 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;
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) Account(com.zimbra.cs.account.Account) Element(com.zimbra.common.soap.Element) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) ItemId(com.zimbra.cs.service.util.ItemId) CalendarItem(com.zimbra.cs.mailbox.CalendarItem) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap) Invite(com.zimbra.cs.mailbox.calendar.Invite) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone)

Example 4 with ZAttendee

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

the class CalendarRequest method addRemoveAttendeesInExceptions.

protected static void addRemoveAttendeesInExceptions(OperationContext octxt, Mailbox mbox, CalendarItem calItem, List<ZAttendee> toAdd, List<ZAttendee> toRemove, boolean ignorePastExceptions) throws ServiceException {
    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());
        long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
        boolean first = true;
        Invite[] invites = calItem.getInvites();
        for (Invite inv : invites) {
            // Ignore exceptions in the past.
            if (ignorePastExceptions && inv.hasRecurId() && !inviteIsAfterTime(inv, now)) {
                continue;
            }
            // Make a copy of the invite and add/remove attendees.
            boolean modified = false;
            Invite modify = inv.newCopy();
            modify.setMailItemId(inv.getMailItemId());
            List<ZAttendee> existingAts = modify.getAttendees();
            // Survivors are added.
            List<ZAttendee> addList = new ArrayList<ZAttendee>(toAdd);
            for (Iterator<ZAttendee> iter = existingAts.iterator(); iter.hasNext(); ) {
                ZAttendee existingAt = iter.next();
                String existingAtEmail = existingAt.getAddress();
                if (existingAtEmail != null) {
                    // Check if the attendee being added is already in the invite.
                    for (Iterator<ZAttendee> iterAdd = addList.iterator(); iterAdd.hasNext(); ) {
                        ZAttendee at = iterAdd.next();
                        if (existingAtEmail.equalsIgnoreCase(at.getAddress())) {
                            iterAdd.remove();
                        }
                    }
                    // Check if existing attendee matches an attendee being removed.
                    for (ZAttendee at : toRemove) {
                        if (existingAtEmail.equalsIgnoreCase(at.getAddress())) {
                            iter.remove();
                            modified = true;
                            break;
                        }
                    }
                }
            }
            // Duplicates have been eliminated from addList.  Add survivors to the invite.
            if (!addList.isEmpty()) {
                for (ZAttendee at : addList) {
                    modify.addAttendee(at);
                }
                modified = true;
            }
            // Save the modified invite.
            if (modified) {
                // REQUEST so that the recipient can handle it properly.
                if (!modify.isCancel())
                    modify.setMethod(ICalTok.REQUEST.toString());
                // DTSTAMP - Rev it.
                modify.setDtStamp(now);
                // Save the modified invite, using the existing MimeMessage for the exception.
                MimeMessage mmInv = calItem.getSubpartMessage(modify.getMailItemId());
                ParsedMessage pm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
                mbox.addInvite(octxt, modify, calItem.getFolderId(), pm, true, false, first);
                first = false;
                // Refresh calItem after update in mbox.addInvite.
                calItem = mbox.getCalendarItemById(octxt, calItem.getId());
            }
        }
    } finally {
        mbox.lock.release();
    }
}
Also used : MimeMessage(javax.mail.internet.MimeMessage) FixedMimeMessage(com.zimbra.cs.mime.Mime.FixedMimeMessage) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) ArrayList(java.util.ArrayList) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 5 with ZAttendee

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

the class CalendarUtils method parseInviteForCounter.

static ParseMimeMessage.InviteParserResult parseInviteForCounter(Account account, Invite oldInvite, MailItem.Type type, Element inviteElem) throws ServiceException {
    TimeZoneMap tzMap = new TimeZoneMap(Util.getAccountTimeZone(account));
    Invite inv = new Invite(ICalTok.COUNTER.toString(), tzMap, false);
    CalendarUtils.parseInviteElementCommon(account, type, inviteElem, inv, true, true);
    // Get the existing invite to populate X-MS-OLK-ORIGINALSTART and X-MS-OLK-ORIGINALEND
    if (oldInvite == null) {
        Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(account);
        CalendarItem calItem = mbox.getCalendarItemByUid(null, inv.getUid());
        if (calItem != null)
            oldInvite = calItem.getInvite(inv.getRecurId());
    }
    if (oldInvite != null) {
        // Add TZIDs from oldInvite to inv
        inv.getTimeZoneMap().add(oldInvite.getTimeZoneMap());
        // Add ORIGINALSTART x-prop
        ParsedDateTime dt = oldInvite.getStartTime();
        if (dt != null) {
            ZCalendar.ZProperty prop = new ZCalendar.ZProperty("X-MS-OLK-ORIGINALSTART");
            prop.setValue(dt.getDateTimePartString());
            if (dt.getTZName() != null)
                prop.addParameter(new ZParameter(ICalTok.TZID, dt.getTZName()));
            inv.addXProp(prop);
        }
        // Add ORIGINALEND x-prop
        dt = oldInvite.getEffectiveEndTime();
        if (dt != null) {
            ZCalendar.ZProperty prop = new ZCalendar.ZProperty("X-MS-OLK-ORIGINALEND");
            prop.setValue(dt.getDateTimePartString());
            if (dt.getTZName() != null)
                prop.addParameter(new ZParameter(ICalTok.TZID, dt.getTZName()));
            inv.addXProp(prop);
        }
        // Add LOCATION if not already exist.
        if (inv.getLocation() == null || inv.getLocation().isEmpty())
            inv.setLocation(oldInvite.getLocation());
    }
    // UID
    String uid = inv.getUid();
    if (uid == null || uid.length() == 0)
        throw ServiceException.INVALID_REQUEST("Missing uid in a counter invite", null);
    // ORGANIZER
    if (!inv.hasOrganizer())
        throw ServiceException.INVALID_REQUEST("Missing organizer in a counter invite", null);
    // DTSTAMP
    if (inv.getDTStamp() == 0) {
        //zdsync
        inv.setDtStamp(new Date().getTime());
    }
    // DTSTART
    if (inv.getStartTime() == null)
        throw ServiceException.INVALID_REQUEST("Missing dtstart in a counter invite", null);
    // iCalendar object doesn't have an ATTENDEE property.  RFC2446 doesn't require one.
    if (!inv.hasOtherAttendees()) {
        ZAttendee at = new ZAttendee(account.getMail());
        at.setPartStat(IcalXmlStrMap.PARTSTAT_TENTATIVE);
        inv.addAttendee(at);
    }
    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;
}
Also used : ZProperty(com.zimbra.common.calendar.ZCalendar.ZProperty) ZParameter(com.zimbra.common.calendar.ZCalendar.ZParameter) Date(java.util.Date) CalendarItem(com.zimbra.cs.mailbox.CalendarItem) ZCalendar(com.zimbra.common.calendar.ZCalendar) ZVCalendar(com.zimbra.common.calendar.ZCalendar.ZVCalendar) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap) ZProperty(com.zimbra.common.calendar.ZCalendar.ZProperty) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Aggregations

ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)29 Account (com.zimbra.cs.account.Account)16 Invite (com.zimbra.cs.mailbox.calendar.Invite)15 Mailbox (com.zimbra.cs.mailbox.Mailbox)10 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)9 ArrayList (java.util.ArrayList)9 ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)8 ZOrganizer (com.zimbra.cs.mailbox.calendar.ZOrganizer)7 Element (com.zimbra.common.soap.Element)6 MimeMessage (javax.mail.internet.MimeMessage)6 ParsedDuration (com.zimbra.common.calendar.ParsedDuration)5 Provisioning (com.zimbra.cs.account.Provisioning)5 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)5 AccountAddressMatcher (com.zimbra.cs.util.AccountUtil.AccountAddressMatcher)5 ICalTimeZone (com.zimbra.common.calendar.ICalTimeZone)4 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)4 ZProperty (com.zimbra.common.calendar.ZCalendar.ZProperty)4 ServiceException (com.zimbra.common.service.ServiceException)4 CalendarResource (com.zimbra.cs.account.CalendarResource)4 Alarm (com.zimbra.cs.mailbox.calendar.Alarm)4