Search in sources :

Example 1 with AutoScheduler

use of com.zimbra.cs.dav.caldav.AutoScheduler 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);
        }
    }
}
Also used : RecurId(com.zimbra.cs.mailbox.calendar.RecurId) ReplyInfo(com.zimbra.cs.mailbox.CalendarItem.ReplyInfo) SetCalendarItemData(com.zimbra.cs.mailbox.Mailbox.SetCalendarItemData) HttpServletRequest(javax.servlet.http.HttpServletRequest) CalendarItem(com.zimbra.cs.mailbox.CalendarItem) Mailbox(com.zimbra.cs.mailbox.Mailbox) MimeMessage(javax.mail.internet.MimeMessage) BadOrganizerException(com.zimbra.cs.mailbox.BadOrganizerException) AutoScheduler(com.zimbra.cs.dav.caldav.AutoScheduler) DavException(com.zimbra.cs.dav.DavException) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) MailItem(com.zimbra.cs.mailbox.MailItem) ServiceException(com.zimbra.common.service.ServiceException) AccountAddressMatcher(com.zimbra.cs.util.AccountUtil.AccountAddressMatcher) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Aggregations

ServiceException (com.zimbra.common.service.ServiceException)1 DavException (com.zimbra.cs.dav.DavException)1 AutoScheduler (com.zimbra.cs.dav.caldav.AutoScheduler)1 BadOrganizerException (com.zimbra.cs.mailbox.BadOrganizerException)1 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)1 ReplyInfo (com.zimbra.cs.mailbox.CalendarItem.ReplyInfo)1 MailItem (com.zimbra.cs.mailbox.MailItem)1 Mailbox (com.zimbra.cs.mailbox.Mailbox)1 SetCalendarItemData (com.zimbra.cs.mailbox.Mailbox.SetCalendarItemData)1 Invite (com.zimbra.cs.mailbox.calendar.Invite)1 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)1 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)1 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)1 AccountAddressMatcher (com.zimbra.cs.util.AccountUtil.AccountAddressMatcher)1 MimeMessage (javax.mail.internet.MimeMessage)1 HttpServletRequest (javax.servlet.http.HttpServletRequest)1