Search in sources :

Example 11 with ZOrganizer

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

the class SendMsg method doSendMessage.

public static ItemId doSendMessage(OperationContext oc, Mailbox mbox, MimeMessage mm, List<Upload> uploads, ItemId origMsgId, String replyType, String identityId, String dataSourceId, boolean noSaveToSent, boolean needCalendarSentByFixup, boolean isCalendarForward) throws ServiceException {
    boolean isCalendarMessage = false;
    ItemId id;
    OutlookICalendarFixupMimeVisitor mv = null;
    if (needCalendarSentByFixup || isCalendarForward) {
        mv = new OutlookICalendarFixupMimeVisitor(mbox.getAccount(), mbox).needFixup(needCalendarSentByFixup).setIsCalendarForward(isCalendarForward);
        try {
            mv.accept(mm);
        } catch (MessagingException e) {
            throw ServiceException.PARSE_ERROR("Error while fixing up SendMsg for SENT-BY", e);
        }
        isCalendarMessage = mv.isCalendarMessage();
    }
    MailSender sender;
    if (dataSourceId == null) {
        sender = isCalendarMessage ? CalendarMailSender.getCalendarMailSender(mbox) : mbox.getMailSender();
        if (noSaveToSent) {
            id = sender.sendMimeMessage(oc, mbox, false, mm, uploads, origMsgId, replyType, null, false, processor);
        } else {
            id = sender.sendMimeMessage(oc, mbox, mm, uploads, origMsgId, replyType, identityId, false, processor);
        }
    } else {
        com.zimbra.cs.account.DataSource dataSource = mbox.getAccount().getDataSourceById(dataSourceId);
        if (dataSource == null) {
            throw ServiceException.INVALID_REQUEST("No data source with id " + dataSourceId, null);
        }
        if (!dataSource.isSmtpEnabled()) {
            throw ServiceException.INVALID_REQUEST("Data source SMTP is not enabled", null);
        }
        sender = mbox.getDataSourceMailSender(dataSource, isCalendarMessage);
        id = sender.sendDataSourceMimeMessage(oc, mbox, mm, uploads, origMsgId, replyType);
    }
    // Send Calendar Forward Invitation notification if applicable
    try {
        if (isCalendarMessage && isCalendarForward && mv.getOriginalInvite() != null) {
            ZOrganizer org = mv.getOriginalInvite().getOrganizer();
            if (org != null) {
                String orgAddress = org.getAddress();
                Account orgAccount = Provisioning.getInstance().getAccountByName(orgAddress);
                if (orgAccount != null && !orgAccount.getId().equals(mbox.getAccount().getId())) {
                    MimeMessage notifyMimeMsg = CalendarMailSender.createForwardNotifyMessage(mbox.getAccount(), orgAccount, orgAddress, mm.getAllRecipients(), mv.getOriginalInvite());
                    CalendarMailSender.sendPartial(oc, mbox, notifyMimeMsg, null, null, null, null, false, true);
                }
            }
        }
    } catch (Exception e) {
        ZimbraLog.soap.warn("Ignoring error while sending Calendar Invitation Forward Notification", e);
    }
    return id;
}
Also used : Account(com.zimbra.cs.account.Account) MessagingException(javax.mail.MessagingException) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) CalendarMailSender(com.zimbra.cs.mailbox.calendar.CalendarMailSender) MailSender(com.zimbra.cs.mailbox.MailSender) ItemId(com.zimbra.cs.service.util.ItemId) MessagingException(javax.mail.MessagingException) ServiceException(com.zimbra.common.service.ServiceException) IOException(java.io.IOException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) ZMimeMessage(com.zimbra.common.zmime.ZMimeMessage) MimeMessage(javax.mail.internet.MimeMessage)

Example 12 with ZOrganizer

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

the class CalItemSmsReminderTask method getText.

private String getText(CalendarItem calItem, Invite invite, Locale locale, TimeZone tz) throws ServiceException {
    DateFormat dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
    dateTimeFormat.setTimeZone(tz);
    DateFormat onlyDateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    onlyDateFormat.setTimeZone(tz);
    String formattedStart;
    String formattedEnd;
    if (calItem.getType() == MailItem.Type.APPOINTMENT) {
        Date start = new Date(new Long(getProperty(NEXT_INST_START_PROP_NAME)));
        formattedStart = dateTimeFormat.format(start);
        Date end = invite.getEffectiveDuration().addToDate(start);
        formattedEnd = dateTimeFormat.format(end);
    } else {
        // start date and due date is optional for tasks
        formattedStart = invite.getStartTime() == null ? "" : onlyDateFormat.format(invite.getStartTime().getDate());
        formattedEnd = invite.getEndTime() == null ? "" : onlyDateFormat.format(invite.getEndTime().getDate());
    }
    String location = invite.getLocation();
    if (StringUtil.isNullOrEmpty(location))
        location = L10nUtil.getMessage(L10nUtil.MsgKey.noLocation);
    String organizer = null;
    ZOrganizer zOrganizer = invite.getOrganizer();
    if (zOrganizer != null)
        organizer = zOrganizer.hasCn() ? zOrganizer.getCn() : zOrganizer.getAddress();
    if (organizer == null)
        organizer = "";
    String folder = calItem.getMailbox().getFolderById(null, calItem.getFolderId()).getName();
    return L10nUtil.getMessage(calItem.getType() == MailItem.Type.APPOINTMENT ? L10nUtil.MsgKey.apptReminderSmsText : L10nUtil.MsgKey.taskReminderSmsText, locale, calItem.getSubject(), formattedStart, formattedEnd, location, organizer, folder);
}
Also used : DateFormat(java.text.DateFormat) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) Date(java.util.Date)

Example 13 with ZOrganizer

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

the class GetMsgTest method testHandle.

@Test
public void testHandle() throws Exception {
    Account acct1 = Provisioning.getInstance().get(Key.AccountBy.name, "test@zimbra.com");
    Account acct2 = Provisioning.getInstance().get(Key.AccountBy.name, "test2@zimbra.com");
    Mailbox mbox1 = MailboxManager.getInstance().getMailboxByAccount(acct1);
    Folder calendarFolder = mbox1.getCalendarFolders(null, SortBy.NONE).get(0);
    String fragment = "Some message";
    ZVCalendar calendar = new ZVCalendar();
    calendar.addDescription(desc, null);
    ZComponent comp = new ZComponent("VEVENT");
    calendar.addComponent(comp);
    Invite invite = MailboxTestUtil.generateInvite(acct1, fragment, calendar);
    ICalTimeZone ical = invite.getTimeZoneMap().getLocalTimeZone();
    long utc = 5 * 60 * 60 * 1000;
    ParsedDateTime s = ParsedDateTime.fromUTCTime(System.currentTimeMillis() + utc, ical);
    ParsedDateTime e = ParsedDateTime.fromUTCTime(System.currentTimeMillis() + (30 * 60 * 1000) + utc, ical);
    invite.setDtStart(s);
    invite.setDtEnd(e);
    invite.setPriority("5");
    invite.setClassProp("PRI");
    invite.setOrganizer(new ZOrganizer("test@zimbra.com", null));
    invite.setUid(UUID.randomUUID().toString());
    invite.setMethod("REQUEST");
    invite.setName("Testing");
    invite.setFreeBusy("B");
    invite.setIsOrganizer(true);
    invite.setItemType(MailItem.Type.APPOINTMENT);
    invite.setUid(UUID.randomUUID().toString());
    AddInviteData inviteData = mbox1.addInvite(null, invite, calendarFolder.getId());
    calendarFolder = mbox1.getCalendarFolders(null, SortBy.NONE).get(0);
    Element request = new Element.XMLElement("GetCalendarItem");
    Element action = request.addElement(MailConstants.E_MSG);
    action.addAttribute(MailConstants.A_ID, acct1.getId() + ":" + inviteData.calItemId + "-" + inviteData.invId);
    action.addAttribute(MailConstants.A_WANT_HTML, "1");
    action.addAttribute(MailConstants.A_NEED_EXP, "1");
    Element response = new GetMsg().handle(request, ServiceTestUtil.getRequestContext(acct1));
    Element organizer = response.getElement("m").getElement("inv").getElement("comp").getElement("or");
    String organizerString = organizer.prettyPrint();
    assertTrue(organizerString.contains("a=\"test@zimbra.com\" url=\"test@zimbra.com\""));
    mbox1.grantAccess(null, 10, acct2.getId(), ACL.GRANTEE_USER, ACL.RIGHT_READ, null);
    request = new Element.XMLElement("CreateMountPoint");
    Element link = request.addElement("link");
    link.addAttribute("f", "#");
    link.addAttribute("reminder", 0);
    link.addAttribute("name", "sharedcal");
    link.addAttribute("path", "/Calendar");
    link.addAttribute("owner", "test@zimbra.com");
    link.addAttribute("l", 10);
    link.addAttribute("view", "appoinment");
    response = new CreateMountpoint().handle(request, ServiceTestUtil.getRequestContext(acct2));
    String mptId = response.getElement("link").getAttribute("id");
    request = new Element.XMLElement("GetMsgRequest");
    action = request.addElement(MailConstants.E_MSG);
    action.addAttribute(MailConstants.A_ID, acct1.getId() + ":" + inviteData.calItemId + "-" + mptId);
    action.addAttribute(MailConstants.A_WANT_HTML, "1");
    action.addAttribute(MailConstants.A_NEED_EXP, "1");
    response = new GetMsg().handle(request, ServiceTestUtil.getRequestContext(acct2, acct1));
    organizerString = response.getElement("m").prettyPrint();
    assertTrue(!organizerString.contains("a=\"test@zimbra.com\" url=\"test@zimbra.com\""));
    request = new Element.XMLElement("FolderAction");
    action = request.addElement("action");
    action.addAttribute("id", mptId);
    action.addAttribute("op", "delete");
    response = new FolderAction().handle(request, ServiceTestUtil.getRequestContext(acct2));
    mbox1.revokeAccess(null, 10, acct2.getId());
}
Also used : Account(com.zimbra.cs.account.Account) AddInviteData(com.zimbra.cs.mailbox.Mailbox.AddInviteData) Element(com.zimbra.common.soap.Element) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) Folder(com.zimbra.cs.mailbox.Folder) ZComponent(com.zimbra.common.calendar.ZCalendar.ZComponent) ZVCalendar(com.zimbra.common.calendar.ZCalendar.ZVCalendar) Mailbox(com.zimbra.cs.mailbox.Mailbox) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone) Test(org.junit.Test)

Example 14 with ZOrganizer

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

the class Message method getInfoForAssociatedCalendarItem.

/**
     * Update {@code status.calItem} and {@code calendarItemInfos}
     */
private void getInfoForAssociatedCalendarItem(Account acct, Invite cur, String method, ParsedMessage pm, boolean applyToCalendar, ProcessInvitesStatus status) throws ServiceException {
    boolean calItemIsNew = false;
    boolean modifiedCalItem = false;
    boolean success = false;
    try {
        InviteChanges invChanges = null;
        // Look for organizer-provided change list.
        ZProperty changesProp = cur.getXProperty(ICalTok.X_ZIMBRA_CHANGES.toString());
        if (changesProp != null) {
            invChanges = new InviteChanges(changesProp.getValue());
            // Don't let the x-prop propagate further. This x-prop is used during transport only. Presence
            // of this x-prop in the appointment object can confuse clients.
            cur.removeXProp(ICalTok.X_ZIMBRA_CHANGES.toString());
        }
        if (!(status.intendedForMe || status.intendedForCalendarIManage)) {
            // Not intended for me. Just save the invite detail in metadata.
            CalendarItemInfo info = new CalendarItemInfo(CalendarItemInfo.CALITEM_ID_NONE, cur.getComponentNum(), cur, invChanges);
            calendarItemInfos.add(info);
            status.updatedMetadata = true;
            success = true;
            return;
        }
        OperationContext octxt = getMailbox().getOperationContext();
        ICalTok methodTok = Invite.lookupMethod(method);
        AccountAddressMatcher acctMatcher = status.getAcctMatcher();
        cur.sanitize(true);
        if (status.intendedForCalendarIManage) {
            Provisioning prov = Provisioning.getInstance();
            Account ownerAcct = prov.get(AccountBy.name, calendarIntendedFor);
            com.zimbra.soap.mail.type.CalendarItemInfo cii = getMailbox().getRemoteCalItemByUID(ownerAcct, cur.getUid(), false, false);
            CalendarItemInfo info;
            if (cii == null) {
                info = new CalendarItemInfo(CalendarItemInfo.CALITEM_ID_NONE, cur.getComponentNum(), cur, invChanges);
                calendarItemInfos.add(info);
            } else {
                int calItemId;
                String owner;
                try {
                    ItemId iid = new ItemId(cii.getId(), (String) null);
                    calItemId = iid.getId();
                    owner = iid.getAccountId();
                } catch (Exception e) {
                    calItemId = CalendarItemInfo.CALITEM_ID_NONE;
                    owner = null;
                }
                info = new CalendarItemInfo(calItemId, owner, cur.getComponentNum(), cur, invChanges);
                calendarItemInfos.add(info);
            }
            status.updatedMetadata = true;
            success = true;
            return;
        }
        status.calItem = mMailbox.getCalendarItemByUid(octxt, cur.getUid());
        if (applyToCalendar && // replies are handled elsewhere (in Mailbox.addMessage())
        !ICalTok.REPLY.equals(methodTok) && !ICalTok.COUNTER.equals(methodTok) && !ICalTok.DECLINECOUNTER.equals(methodTok)) {
            if (status.calItem == null) {
                // Allow PUBLISH method as well depending on the preference.
                if (ICalTok.REQUEST.equals(methodTok) || (ICalTok.PUBLISH.equals(methodTok) && getAccount().getBooleanAttr(Provisioning.A_zimbraPrefCalendarAllowPublishMethodInvite, false))) {
                    if (status.autoAddNew) {
                        if (mMailbox.getAccount().sameAccount(cur.getOrganizerAccount())) {
                            // Bug 100456 ZCO sends out invites before adding a calendar entry.  If server adds
                            // Calendar entry and ZCO sees that before creating its entry, ZCO gets confused.
                            LOG.info("Mailbox %d Msg %d Don't create ORGANIZER calendar item for Invite %s", getMailboxId(), getId(), method);
                        } else {
                            int flags = 0;
                            // int flags = Flag.BITMASK_INDEXING_DEFERRED;
                            // mMailbox.incrementIndexDeferredCount(1);
                            int defaultFolder = cur.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
                            status.calItem = mMailbox.createCalendarItem(defaultFolder, flags, null, cur.getUid(), pm, cur, null);
                            calItemIsNew = true;
                            status.calItemFolderId = status.calItem.getFolderId();
                        }
                    }
                } else {
                    LOG.info("Mailbox %d Message %d SKIPPING Invite %s b/c no CalendarItem could be found", getMailboxId(), getId(), method);
                }
            } else {
                // bug 27887: Ignore when calendar request email somehow made a loop back to the
                // organizer address.  Necessary conditions are:
                //
                // 1) This mailbox is currently organizer.
                // 2) ORGANIZER in the request is this mailbox.
                // 3) User/COS preference doesn't explicitly allow it. (bug 29777)
                //
                // If ORGANIZER in the request is a different user this is an organizer change request,
                // which should be allowed to happen.
                boolean ignore = false;
                Invite defInv = status.calItem.getDefaultInviteOrNull();
                if (defInv != null && defInv.isOrganizer()) {
                    ZOrganizer org = cur.getOrganizer();
                    String orgAddress = org != null ? org.getAddress() : null;
                    if (acctMatcher.matches(orgAddress))
                        ignore = !acct.getBooleanAttr(Provisioning.A_zimbraPrefCalendarAllowCancelEmailToSelf, false);
                }
                if (ignore) {
                    ZimbraLog.calendar.info("Ignoring calendar request emailed from organizer to self, " + "possibly in a mail loop involving mailing lists and/or forwards; " + "calItemId=" + status.calItem.getId() + ", msgId=" + getId());
                } else {
                    // the calendar item in the folder it's currently in.
                    if (status.addRevision)
                        status.calItem.snapshotRevision(false);
                    // If updating (but not canceling) an appointment in trash folder,
                    // use default calendar folder and discard all existing invites.
                    boolean discardExistingInvites = false;
                    int calFolderId = status.calItem.getFolderId();
                    if (!cur.isCancel() && status.calItem.inTrash()) {
                        discardExistingInvites = true;
                        if (status.calItem.getType() == MailItem.Type.TASK) {
                            calFolderId = Mailbox.ID_FOLDER_TASKS;
                        } else {
                            calFolderId = Mailbox.ID_FOLDER_CALENDAR;
                        }
                    }
                    // against the current appointment data.
                    if (invChanges == null && !cur.isCancel()) {
                        Invite prev = status.calItem.getInvite(cur.getRecurId());
                        if (prev == null) {
                            // If incoming invite is a brand-new exception instance, we have to compare
                            // against the series data, but adjusted to the RECURRENCE-ID of the instance.
                            Invite series = status.calItem.getInvite(null);
                            if (series != null)
                                prev = series.makeInstanceInvite(cur.getRecurId().getDt());
                        }
                        if (prev != null)
                            invChanges = new InviteChanges(prev, cur);
                    }
                    modifiedCalItem = status.calItem.processNewInvite(pm, cur, calFolderId, discardExistingInvites);
                    status.calItemFolderId = calFolderId;
                    status.calItem.getFolder().updateHighestMODSEQ();
                }
            }
        }
        int calItemId = status.calItem != null ? status.calItem.getId() : CalendarItemInfo.CALITEM_ID_NONE;
        CalendarItemInfo info = new CalendarItemInfo(calItemId, cur.getComponentNum(), cur, invChanges);
        calendarItemInfos.add(info);
        status.updatedMetadata = true;
        if (status.calItem != null && (calItemIsNew || modifiedCalItem)) {
            mMailbox.index.add(status.calItem);
        }
        success = true;
    } finally {
        if (!success && status.calItem != null) {
            // Error occurred and the calItem in memory may be out of sync with the database.
            // Uncache it here, because the error will be ignored by this method's caller.
            getMailbox().uncache(status.calItem);
        }
    }
}
Also used : Account(com.zimbra.cs.account.Account) InviteChanges(com.zimbra.cs.mailbox.calendar.InviteChanges) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) ItemId(com.zimbra.cs.service.util.ItemId) Provisioning(com.zimbra.cs.account.Provisioning) MessagingException(javax.mail.MessagingException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) ServiceException(com.zimbra.common.service.ServiceException) IOException(java.io.IOException) ICalTok(com.zimbra.common.calendar.ZCalendar.ICalTok) AccountAddressMatcher(com.zimbra.cs.util.AccountUtil.AccountAddressMatcher) ZProperty(com.zimbra.common.calendar.ZCalendar.ZProperty) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 15 with ZOrganizer

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

the class Message method processInvitesAfterCreate.

/**
     * This has to be done as a separate step, after the MailItem has been added, because of foreign key constraints on
     * the CalendarItems table.
     */
private void processInvitesAfterCreate(String method, int folderId, boolean applyToCalendar, ParsedMessage pm, List<Invite> invites) throws ServiceException {
    if (pm == null) {
        throw ServiceException.INVALID_REQUEST("null ParsedMessage while processing invite in message " + mId, null);
    }
    Account acct = getAccount();
    AccountAddressMatcher acctMatcher = new AccountAddressMatcher(acct);
    OperationContext octxt = getMailbox().getOperationContext();
    ProcessInvitesStatus status = new ProcessInvitesStatus(acct, pm);
    status.initAutoAddNew(octxt);
    boolean isOrganizerMethod = Invite.isOrganizerMethod(method);
    if (isOrganizerMethod && !invites.isEmpty() && status.intendedForMe) {
        // Check if the sender is allowed to invite this user.  Only do this for invite-type methods,
        // namely REQUEST/PUBLISH/CANCEL/ADD/DECLINECOUNTER.  REPLY/REFRESH/COUNTER don't undergo
        // the check because they are not organizer-to-attendee methods.
        String senderEmail;
        Account senderAcct = null;
        boolean onBehalfOf = false;
        boolean canInvite;
        AccessManager accessMgr = AccessManager.getInstance();
        if (octxt != null && octxt.getAuthenticatedUser() != null) {
            onBehalfOf = octxt.isDelegatedRequest(getMailbox());
            senderAcct = octxt.getAuthenticatedUser();
            senderEmail = senderAcct.getName();
            canInvite = accessMgr.canDo(senderAcct, acct, User.R_invite, octxt.isUsingAdminPrivileges());
        } else {
            senderEmail = pm.getSenderEmail(false);
            if (senderEmail != null) {
                senderAcct = Provisioning.getInstance().get(AccountBy.name, senderEmail);
            }
            canInvite = accessMgr.canDo(senderEmail, acct, User.R_invite, false);
        }
        if (!canInvite) {
            Invite invite = invites.get(0);
            CalendarMailSender.handleInviteAutoDeclinedNotification(octxt, getMailbox(), acct, senderEmail, senderAcct, onBehalfOf, applyToCalendar, getId(), invite);
            String inviteSender = senderEmail != null ? senderEmail : "unknown sender";
            ZimbraLog.calendar.info("Calendar invite from %s to %s is not allowed", inviteSender, acct.getName());
            // Turn off auto-add.  We still have to run through the code below to save the invite's
            // data in message's metadata.
            status.autoAddNew = false;
        }
    }
    // Override CLASS property if preference says to mark everything as private.
    PrefCalendarApptVisibility prefClass = acct.getPrefCalendarApptVisibility();
    boolean forcePrivateClass = prefClass != null && !prefClass.equals(PrefCalendarApptVisibility.public_);
    // Ignore alarms set by organizer.
    boolean allowOrganizerAlarm = DebugConfig.calendarAllowOrganizerSpecifiedAlarms;
    if (calendarItemInfos == null) {
        calendarItemInfos = new ArrayList<CalendarItemInfo>();
    }
    // properties.
    if (invites.size() > 1) {
        boolean hasSeries = false;
        ZOrganizer seriesOrganizer = null;
        boolean seriesIsOrganizer = false;
        List<ZAttendee> seriesAttendees = null;
        ParsedDateTime seriesDtStart = null;
        // Get organizer and attendees from series VEVENT.
        for (Invite inv : invites) {
            if (!inv.hasRecurId()) {
                hasSeries = true;
                seriesOrganizer = inv.getOrganizer();
                seriesIsOrganizer = inv.isOrganizer();
                seriesAttendees = inv.getAttendees();
                seriesDtStart = inv.getStartTime();
                break;
            }
        }
        if (hasSeries) {
            for (Invite inv : invites) {
                RecurId rid = inv.getRecurId();
                if (rid != null) {
                    if (seriesOrganizer != null && !inv.hasOrganizer()) {
                        inv.setOrganizer(seriesOrganizer);
                        inv.setIsOrganizer(seriesIsOrganizer);
                        // exception instance to have no attendee.
                        if (!inv.hasOtherAttendees() && seriesAttendees != null) {
                            for (ZAttendee at : seriesAttendees) {
                                inv.addAttendee(at);
                            }
                        }
                    }
                    if (!inv.isAllDayEvent() && seriesDtStart != null) {
                        // Exchange can send invalid RECURRENCE-ID with HHMMSS set to 000000.  Detect it and fix it up
                        // by copying the time from series DTSTART.
                        ParsedDateTime ridDt = rid.getDt();
                        if (ridDt != null && ridDt.hasZeroTime() && !seriesDtStart.hasZeroTime() && ridDt.sameTimeZone(seriesDtStart)) {
                            ParsedDateTime fixedDt = seriesDtStart.cloneWithNewDate(ridDt);
                            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);
                        }
                    }
                    // Exception instance invites shouldn't point to the same MIME part in the appointment blob
                    // as the series invite.  If they do, we will lose the series attachment when a new exception
                    // instance update is received.
                    inv.setMailItemId(0);
                }
            }
        }
    }
    // used to check if any invite is non-public
    boolean publicInvites = true;
    status.calItemFolderId = invites.size() > 0 && invites.get(0).isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
    CalendarItem firstCalItem = null;
    Set<String> calUidsSeen = new HashSet<String>();
    for (Invite cur : invites) {
        if (!cur.isPublic()) {
            publicInvites = false;
        }
        // it's the correct organizer.
        if (!cur.hasOrganizer() && cur.hasOtherAttendees()) {
            String fromEmail = pm.getSenderEmail(true);
            if (fromEmail != null) {
                boolean dangerousSender = false;
                // Is sender == recipient?  If so, clear attendees.
                if (status.intendedForAddress != null) {
                    if (status.intendedForAddress.equalsIgnoreCase(fromEmail)) {
                        ZimbraLog.calendar.info("Got malformed invite without organizer.  Clearing attendees to prevent inadvertent cancels.");
                        cur.clearAttendees();
                        dangerousSender = true;
                    }
                } else if (acctMatcher.matches(fromEmail)) {
                    ZimbraLog.calendar.info("Got malformed invite without organizer.  Clearing attendees to prevent inadvertent cancels.");
                    cur.clearAttendees();
                    dangerousSender = true;
                }
                if (!dangerousSender) {
                    if (isOrganizerMethod = Invite.isOrganizerMethod(method)) {
                        // For organizer-originated methods, use email sender as default organizer.
                        ZOrganizer org = new ZOrganizer(fromEmail, null);
                        String senderEmail = pm.getSenderEmail(false);
                        if (senderEmail != null && !senderEmail.equalsIgnoreCase(fromEmail))
                            org.setSentBy(senderEmail);
                        cur.setOrganizer(org);
                        ZimbraLog.calendar.info("Got malformed invite that lists attendees without specifying an organizer.  " + "Defaulting organizer to: " + org.toString());
                    } else {
                        // For attendee-originated methods, look up organizer from appointment on calendar.
                        // If appointment is not found, fall back to the intended-for address, then finally to self.
                        ZOrganizer org = null;
                        CalendarItem ci = mMailbox.getCalendarItemByUid(octxt, cur.getUid());
                        if (ci != null) {
                            Invite inv = ci.getInvite(cur.getRecurId());
                            if (inv == null) {
                                inv = ci.getDefaultInviteOrNull();
                            }
                            if (inv != null) {
                                org = inv.getOrganizer();
                            }
                        }
                        if (org == null) {
                            if (status.intendedForAddress != null) {
                                org = new ZOrganizer(status.intendedForAddress, null);
                            } else {
                                org = new ZOrganizer(acct.getName(), null);
                            }
                        }
                        cur.setOrganizer(org);
                        cur.setIsOrganizer(status.intendedForMe);
                        ZimbraLog.calendar.info("Got malformed reply missing organizer.  Defaulting to " + org.toString());
                    }
                }
            }
        }
        cur.setLocalOnly(false);
        status.initAddRevisionSetting(cur.getUid(), calUidsSeen);
        // other than BUSY.  And don't allow transparent meetings.  This will prevent double booking in the future.
        if (cur.isEvent() && (acct instanceof CalendarResource)) {
            cur.setFreeBusy(IcalXmlStrMap.FBTYPE_BUSY);
            cur.setTransparency(IcalXmlStrMap.TRANSP_OPAQUE);
        }
        if (forcePrivateClass) {
            cur.setClassProp(IcalXmlStrMap.CLASS_PRIVATE);
            cur.setClassPropSetByMe(true);
        }
        ICalTok methodTok = Invite.lookupMethod(method);
        // Discard alarms set by organizer.  Add a new one based on attendee's preferences.
        if (!allowOrganizerAlarm) {
            // only for non-cancel/non-declinecounter VEVENTs
            if (cur.isEvent() && isOrganizerMethod && !cur.isCancel() && !ICalTok.DECLINECOUNTER.equals(methodTok))
                Invite.setDefaultAlarm(cur, acct);
        }
        getInfoForAssociatedCalendarItem(acct, cur, method, pm, applyToCalendar, status);
        if (firstCalItem == null) {
            firstCalItem = status.calItem;
        }
    }
    if (status.updatedMetadata) {
        saveMetadata();
    }
    // Don't forward from a system account. (e.g. archiving, galsync, ham/spam)
    if (applyToCalendar && !status.isForwardedInvite && status.intendedForMe && folderId != Mailbox.ID_FOLDER_SENT && !invites.isEmpty() && !acct.isIsSystemResource()) {
        // Don't do the forwarding during redo playback.
        RedoableOp redoPlayer = octxt != null ? octxt.getPlayer() : null;
        RedoLogProvider redoProvider = RedoLogProvider.getInstance();
        boolean needToForward = redoProvider.isMaster() && (redoPlayer == null || redoProvider.getRedoLogManager().getInCrashRecovery());
        if (needToForward) {
            String[] forwardTo = null;
            if (isOrganizerMethod) {
                forwardTo = acct.getPrefCalendarForwardInvitesTo();
            } else {
                // not the users listed in the zimbraPrefCalendarForwardInvitesTo preference.
                if (firstCalItem != null) {
                    Invite invCalItem = firstCalItem.getInvite(invites.get(0).getRecurId());
                    if (invCalItem == null)
                        invCalItem = firstCalItem.getDefaultInviteOrNull();
                    if (invCalItem != null && invCalItem.isOrganizer()) {
                        ZOrganizer org = invCalItem.getOrganizer();
                        if (org.hasSentBy()) {
                            forwardTo = new String[] { org.getSentBy() };
                        }
                    }
                }
            }
            Account senderAcct = null;
            String senderEmail = pm.getSenderEmail(false);
            if (senderEmail != null)
                senderAcct = Provisioning.getInstance().get(AccountBy.name, senderEmail);
            if (forwardTo != null && forwardTo.length > 0) {
                // recipients to receive unfiltered message
                List<String> rcptsUnfiltered = new ArrayList<String>();
                // recipients to receive message filtered to remove private data
                List<String> rcptsFiltered = new ArrayList<String>();
                Folder calFolder = null;
                try {
                    calFolder = getMailbox().getFolderById(status.calItemFolderId);
                } catch (NoSuchItemException e) {
                    ZimbraLog.mailbox.warn("No such calendar folder (" + status.calItemFolderId + ") during invite auto-forwarding");
                }
                for (String fwd : forwardTo) {
                    if (fwd != null) {
                        fwd = fwd.trim();
                    }
                    if (StringUtil.isNullOrEmpty(fwd)) {
                        continue;
                    }
                    // Prevent forwarding to self.
                    if (acctMatcher.matches(fwd))
                        continue;
                    // Don't forward back to the sender.  It's redundant and confusing.
                    Account rcptAcct = Provisioning.getInstance().get(AccountBy.name, fwd);
                    boolean rcptIsSender = false;
                    if (rcptAcct != null) {
                        if (senderAcct != null) {
                            rcptIsSender = rcptAcct.getId().equalsIgnoreCase(senderAcct.getId());
                        } else {
                            rcptIsSender = AccountUtil.addressMatchesAccount(rcptAcct, senderEmail);
                        }
                    } else {
                        if (senderAcct != null) {
                            rcptIsSender = AccountUtil.addressMatchesAccount(senderAcct, fwd);
                        } else {
                            rcptIsSender = fwd.equalsIgnoreCase(senderEmail);
                        }
                    }
                    if (rcptIsSender) {
                        ZimbraLog.calendar.info("Not auto-forwarding to " + fwd + " because it is the sender of this message");
                        continue;
                    }
                    if (publicInvites) {
                        rcptsUnfiltered.add(fwd);
                    } else {
                        boolean allowed = false;
                        if (calFolder != null && rcptAcct != null) {
                            allowed = calFolder.canAccess(ACL.RIGHT_PRIVATE, rcptAcct, false);
                        }
                        if (allowed) {
                            rcptsUnfiltered.add(fwd);
                        } else if (acct instanceof CalendarResource) {
                            // Forward filtered invite from calendar resource accounts only.  Don't forward filtered
                            // invite from regular user account because the forwardee won't be able to accept/decline
                            // due to permission error.
                            rcptsFiltered.add(fwd);
                        }
                    }
                }
                if (!rcptsUnfiltered.isEmpty() || !rcptsFiltered.isEmpty()) {
                    MimeMessage mmOrig = pm.getMimeMessage();
                    if (mmOrig != null) {
                        String origSender = pm.getSenderEmail(false);
                        String forwarder = AccountUtil.getCanonicalAddress(acct);
                        if (!rcptsUnfiltered.isEmpty()) {
                            MimeMessage mm = CalendarMailSender.createForwardedInviteMessage(mmOrig, origSender, forwarder, rcptsUnfiltered.toArray(new String[0]));
                            if (mm != null) {
                                ItemId origMsgId = new ItemId(getMailbox(), getId());
                                CalendarMailSender.sendInviteAutoForwardMessage(octxt, getMailbox(), origMsgId, mm);
                            }
                        }
                        if (!rcptsFiltered.isEmpty()) {
                            MimeMessage mm = CalendarMailSender.createForwardedPrivateInviteMessage(acct, acct.getLocale(), method, invites, origSender, forwarder, rcptsFiltered.toArray(new String[0]));
                            if (mm != null) {
                                ItemId origMsgId = new ItemId(getMailbox(), getId());
                                CalendarMailSender.sendInviteAutoForwardMessage(octxt, getMailbox(), origMsgId, mm);
                            }
                        }
                    }
                }
            }
        }
    }
}
Also used : AccessManager(com.zimbra.cs.account.AccessManager) Account(com.zimbra.cs.account.Account) ArrayList(java.util.ArrayList) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) ItemId(com.zimbra.cs.service.util.ItemId) ICalTok(com.zimbra.common.calendar.ZCalendar.ICalTok) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) RedoLogProvider(com.zimbra.cs.redolog.RedoLogProvider) ZMimeMessage(com.zimbra.common.zmime.ZMimeMessage) MimeMessage(javax.mail.internet.MimeMessage) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) CalendarResource(com.zimbra.cs.account.CalendarResource) HashSet(java.util.HashSet) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) PrefCalendarApptVisibility(com.zimbra.common.account.ZAttrProvisioning.PrefCalendarApptVisibility) AccountAddressMatcher(com.zimbra.cs.util.AccountUtil.AccountAddressMatcher) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Aggregations

ZOrganizer (com.zimbra.cs.mailbox.calendar.ZOrganizer)26 Invite (com.zimbra.cs.mailbox.calendar.Invite)16 Account (com.zimbra.cs.account.Account)15 ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)10 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)8 ZProperty (com.zimbra.common.calendar.ZCalendar.ZProperty)7 Element (com.zimbra.common.soap.Element)7 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)7 IOException (java.io.IOException)6 ParsedDuration (com.zimbra.common.calendar.ParsedDuration)5 AccountAddressMatcher (com.zimbra.cs.util.AccountUtil.AccountAddressMatcher)5 Date (java.util.Date)5 Geo (com.zimbra.common.calendar.Geo)4 ServiceException (com.zimbra.common.service.ServiceException)4 Mailbox (com.zimbra.cs.mailbox.Mailbox)4 IRecurrence (com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence)4 ItemId (com.zimbra.cs.service.util.ItemId)4 ArrayList (java.util.ArrayList)4 MimeMessage (javax.mail.internet.MimeMessage)4 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)3