Search in sources :

Example 1 with RedoLogProvider

use of com.zimbra.cs.redolog.RedoLogProvider in project zm-mailbox by Zimbra.

the class Zimbra method startup.

/**
     * Initialize the various subsystems at server/CLI startup time.
     * @param forMailboxd true if this is the mailboxd process; false for CLI processes
     * @throws ServiceException
     */
private static synchronized void startup(boolean forMailboxd) throws ServiceException {
    if (sInited)
        return;
    sIsMailboxd = forMailboxd;
    if (sIsMailboxd) {
        FirstServlet.waitForInitialization();
    }
    Provisioning prov = Provisioning.getInstance();
    Server server = prov.getLocalServer();
    alwaysOnClusterId = server.getAlwaysOnClusterId();
    setSystemProperties();
    validateJavaOptions();
    logVersionAndSysInfo();
    SoapTransport.setDefaultUserAgent("ZCS", BuildInfo.VERSION);
    checkForClasses();
    ZimbraApplication app = ZimbraApplication.getInstance();
    DbPool.startup();
    app.initializeZimbraDb(forMailboxd);
    if (!Versions.checkVersions()) {
        Zimbra.halt("Data version mismatch.  Reinitialize or upgrade the backend data store.");
    }
    DbPool.loadSettings();
    String tzFilePath = LC.timezone_file.value();
    try {
        File tzFile = new File(tzFilePath);
        WellKnownTimeZones.loadFromFile(tzFile);
    } catch (Throwable t) {
        Zimbra.halt("Unable to load timezones from " + tzFilePath, t);
    }
    if (prov instanceof LdapProv) {
        ((LdapProv) prov).waitForLdapServer();
        if (forMailboxd) {
            AttributeManager.loadLdapSchemaExtensionAttrs((LdapProv) prov);
        }
    }
    if (server.isMailSSLClientCertOCSPEnabled()) {
        // Activate OCSP
        Security.setProperty("ocsp.enable", "true");
        // Activate CRLDP
        System.setProperty("com.sun.security.enableCRLDP", "true");
    } else {
        // Disable OCSP
        Security.setProperty("ocsp.enable", "false");
        // Disable CRLDP
        System.setProperty("com.sun.security.enableCRLDP", "false");
    }
    try {
        RightManager.getInstance();
    } catch (ServiceException e) {
        Util.halt("cannot initialize RightManager", e);
    }
    ZimbraHttpConnectionManager.startReaperThread();
    EphemeralStore.registerFactory("ldap", LdapEphemeralStore.Factory.class.getName());
    ExtensionUtil.initAll();
    try {
        StoreManager.getInstance().startup();
    } catch (IOException e) {
        throw ServiceException.FAILURE("Unable to initialize StoreManager.", e);
    }
    MailboxManager.getInstance();
    app.startup();
    if (app.supports(MemcachedConnector.class.getName())) {
        MemcachedConnector.startup();
    }
    if (app.supports(EhcacheManager.class.getName())) {
        EhcacheManager.getInstance().startup();
    }
    // ZimletUtil.loadZimlets();
    MailboxIndex.startup();
    RedoLogProvider redoLog = RedoLogProvider.getInstance();
    if (sIsMailboxd) {
        redoLog.startup();
    } else {
        redoLog.initRedoLogManager();
    }
    System.setProperty("ical4j.unfolding.relaxed", "true");
    MailboxManager.getInstance().startup();
    app.initialize(sIsMailboxd);
    if (sIsMailboxd) {
        SessionCache.startup();
        AuthTokenRegistry.startup(prov.getConfig(Provisioning.A_zimbraAuthTokenNotificationInterval).getIntAttr(Provisioning.A_zimbraAuthTokenNotificationInterval, 60000));
        dbSessionCleanup();
        if (!redoLog.isSlave()) {
            boolean useDirectBuffers = server.isMailUseDirectBuffers();
            IoBuffer.setUseDirectBuffer(useDirectBuffers);
            ZimbraLog.misc.info("MINA setUseDirectBuffers(" + useDirectBuffers + ")");
            ServerManager.getInstance().startServers();
        }
        if (app.supports(WaitSetMgr.class.getName())) {
            WaitSetMgr.startup();
        }
        if (app.supports(MemoryStats.class.getName())) {
            MemoryStats.startup();
        }
        if (app.supports(ScheduledTaskManager.class.getName())) {
            ScheduledTaskManager.startup();
        }
        if (app.supports(PurgeThread.class.getName())) {
            PurgeThread.startup();
        }
        if (app.supports(AutoProvisionThread.class.getName())) {
            AutoProvisionThread.switchAutoProvThreadIfNecessary();
        }
        if (LC.smtp_to_lmtp_enabled.booleanValue()) {
            int smtpPort = LC.smtp_to_lmtp_port.intValue();
            int lmtpPort = Provisioning.getInstance().getLocalServer().getLmtpBindPort();
            SmtpToLmtp smtpServer = SmtpToLmtp.startup(smtpPort, "localhost", lmtpPort);
            smtpServer.setRecipientValidator(new SmtpRecipientValidator());
        }
        if (app.supports(AclPushTask.class)) {
            long pushInterval = server.getSharingUpdatePublishInterval();
            sTimer.schedule(new AclPushTask(), pushInterval, pushInterval);
        }
        if (app.supports(ExternalAccountManagerTask.class)) {
            long interval = server.getExternalAccountStatusCheckInterval();
            sTimer.schedule(new ExternalAccountManagerTask(), interval, interval);
        }
        if (prov.getLocalServer().isMessageChannelEnabled()) {
            try {
                MessageChannel.getInstance().startup();
            } catch (IOException e) {
                ZimbraLog.misc.warn("can't start notification channels", e);
            }
        }
        Server localServer = Provisioning.getInstance().getLocalServer();
        String provPort = localServer.getAttr(Provisioning.A_zimbraMailPort);
        String lcPort = LC.zimbra_mail_service_port.value();
        if (!lcPort.equals(provPort)) {
            LocalConfig lc;
            try {
                lc = new LocalConfig(null);
                lc.set(LC.zimbra_mail_service_port.key(), provPort);
                lc.save();
                LC.reload();
            } catch (DocumentException | ConfigException | IOException e) {
                ZimbraLog.misc.warn("Cannot set LC zimbra_mail_service_port", e);
            }
        }
        // should be last, so that other subsystems can add dynamic stats counters
        if (app.supports(ZimbraPerf.class.getName())) {
            ZimbraPerf.initialize();
        }
    }
    ExtensionUtil.postInitAll();
    // Register the service with ZooKeeper
    if (sIsMailboxd && isAlwaysOn()) {
        try {
            CuratorManager curatorManager = CuratorManager.getInstance();
            if (curatorManager == null) {
                throw ServiceException.FAILURE("ZooKeeper addresses not configured.", null);
            }
            curatorManager.start();
        } catch (Exception e) {
            throw ServiceException.FAILURE("Unable to start Distributed Lock service.", e);
        }
    }
    sInited = true;
}
Also used : PurgeThread(com.zimbra.cs.mailbox.PurgeThread) Server(com.zimbra.cs.account.Server) AclPushTask(com.zimbra.cs.mailbox.acl.AclPushTask) ConfigException(com.zimbra.common.localconfig.ConfigException) Provisioning(com.zimbra.cs.account.Provisioning) LdapProv(com.zimbra.cs.account.ldap.LdapProv) AutoProvisionThread(com.zimbra.cs.account.AutoProvisionThread) ScheduledTaskManager(com.zimbra.cs.mailbox.ScheduledTaskManager) RedoLogProvider(com.zimbra.cs.redolog.RedoLogProvider) DocumentException(org.dom4j.DocumentException) ExternalAccountManagerTask(com.zimbra.cs.account.ExternalAccountManagerTask) SmtpToLmtp(com.zimbra.common.lmtp.SmtpToLmtp) MemcachedConnector(com.zimbra.cs.memcached.MemcachedConnector) LocalConfig(com.zimbra.common.localconfig.LocalConfig) IOException(java.io.IOException) ServiceException(com.zimbra.common.service.ServiceException) DocumentException(org.dom4j.DocumentException) IOException(java.io.IOException) ConfigException(com.zimbra.common.localconfig.ConfigException) WaitSetMgr(com.zimbra.cs.session.WaitSetMgr) ServiceException(com.zimbra.common.service.ServiceException) ZimbraPerf(com.zimbra.cs.stats.ZimbraPerf) CuratorManager(com.zimbra.cs.zookeeper.CuratorManager) File(java.io.File)

Example 2 with RedoLogProvider

use of com.zimbra.cs.redolog.RedoLogProvider in project zm-mailbox by Zimbra.

the class CalendarMailSender method handleInviteAutoDeclinedNotification.

public static void handleInviteAutoDeclinedNotification(final OperationContext octxt, final Mailbox mbox, final Account fromAccount, String senderEmail, final Account senderAccount, boolean onBehalfOf, boolean applyToCalendar, int inviteMsgId, Invite invite) throws ServiceException {
    if (allowInviteAutoDeclinedNotification(mbox, fromAccount, senderEmail, senderAccount, applyToCalendar, invite.getMatchingAttendee(fromAccount))) {
        RedoableOp redoPlayer = octxt != null ? octxt.getPlayer() : null;
        RedoLogProvider redoProvider = RedoLogProvider.getInstance();
        // (e.g. archiving, galsync, ham/spam)
        if (redoProvider.isMaster() && (redoPlayer == null || redoProvider.getRedoLogManager().getInCrashRecovery())) {
            ItemId origMsgId = new ItemId(mbox, inviteMsgId);
            CalendarMailSender.sendInviteDeniedMessage(octxt, fromAccount, senderAccount, onBehalfOf, true, mbox, origMsgId, senderEmail, invite);
        }
    }
}
Also used : RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) RedoLogProvider(com.zimbra.cs.redolog.RedoLogProvider) ItemId(com.zimbra.cs.service.util.ItemId)

Example 3 with RedoLogProvider

use of com.zimbra.cs.redolog.RedoLogProvider 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)

Example 4 with RedoLogProvider

use of com.zimbra.cs.redolog.RedoLogProvider in project zm-mailbox by Zimbra.

the class Appointment 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;
    long opTime = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
    Account account = getMailbox().getAccount();
    boolean onBehalfOf = false;
    Account authAcct = account;
    if (octxt != null) {
        Account authuser = octxt.getAuthenticatedUser();
        if (authuser != null) {
            onBehalfOf = !account.getId().equalsIgnoreCase(authuser.getId());
            if (onBehalfOf)
                authAcct = authuser;
        }
    }
    boolean asAdmin = octxt != null ? octxt.isUsingAdminPrivileges() : false;
    boolean allowPrivateAccess = allowPrivateAccess(authAcct, asAdmin);
    String partStat = defaultPartStat;
    if (player != null) {
        String p = player.getCalendarItemPartStat();
        if (p != null)
            partStat = p;
    }
    // See if we have RSVP=FALSE for the attendee.  Let's assume RSVP was requested unless it is
    // explicitly set to FALSE.
    boolean rsvpRequested = true;
    ZAttendee attendee = invite.getMatchingAttendee(account);
    if (attendee != null) {
        Boolean rsvp = attendee.getRsvp();
        if (rsvp != null)
            rsvpRequested = rsvp.booleanValue();
    }
    RedoLogProvider redoProvider = RedoLogProvider.getInstance();
    // Don't send reply emails if we're not on master (in redo-driven master/replica setup).
    // Don't send reply emails if we're replaying redo for reasons other than crash recovery.
    // In other words, we DO want to send emails during crash recovery, because we're completing
    // an interrupted transaction.  But we don't send emails during redo reply phase of restore.
    // Also don't send emails for cancel invites.  (Organizer doesn't expect reply for cancels.)
    // And don't send emails for task requests.
    // Don't send reply emails from a system account. (e.g. archiving, galsync, ham/spam)
    boolean needReplyEmail = rsvpRequested && redoProvider.isMaster() && (player == null || redoProvider.getRedoLogManager().getInCrashRecovery()) && invite.hasOrganizer() && !invite.isCancel() && !invite.isTodo() && !account.isIsSystemResource();
    if (invite.isOrganizer()) {
        // Organizer always accepts.
        partStat = IcalXmlStrMap.PARTSTAT_ACCEPTED;
    } else if (account instanceof CalendarResource && octxt == null) {
        // Auto accept/decline processing should only occur during email delivery.  In particular,
        // don't do it if we're here during ics import.  We're in email delivery if octxt == null.
        // (There needs to be a better way to determine that...)
        boolean replySent = false;
        CalendarResource resource = (CalendarResource) account;
        Locale lc;
        Account organizer = invite.getOrganizerAccount();
        if (organizer != null)
            lc = organizer.getLocale();
        else
            lc = resource.getLocale();
        if (resource.autoAcceptDecline() || resource.autoDeclineIfBusy() || resource.autoDeclineRecurring()) {
            boolean replyListUpdated = false;
            // If auto-accept is enabled, assume it'll be accepted until it gets declined.
            if (resource.autoAcceptDecline())
                partStat = IcalXmlStrMap.PARTSTAT_ACCEPTED;
            if (isRecurring() && resource.autoDeclineRecurring()) {
                // Decline because resource is configured to decline all recurring appointments.
                partStat = IcalXmlStrMap.PARTSTAT_DECLINED;
                if (needReplyEmail) {
                    String reason = L10nUtil.getMessage(MsgKey.calendarResourceDeclineReasonRecurring, lc);
                    Invite replyInv = makeReplyInvite(account, authAcct, lc, onBehalfOf, allowPrivateAccess, invite, invite.getRecurId(), CalendarMailSender.VERB_DECLINE);
                    CalendarMailSender.sendResourceAutoReply(octxt, mbox, true, CalendarMailSender.VERB_DECLINE, false, reason + "\r\n", this, invite, new Invite[] { replyInv }, mmInv);
                    replySent = true;
                }
            } else if (resource.autoDeclineIfBusy()) {
                // Auto decline is enabled.  Let's check for conflicts.
                int maxNumConflicts = resource.getMaxNumConflictsAllowed();
                int maxPctConflicts = resource.getMaxPercentConflictsAllowed();
                ConflictCheckResult checkResult = checkAvailability(opTime, invite, maxNumConflicts, maxPctConflicts);
                if (checkResult != null) {
                    List<Conflict> conflicts = checkResult.getConflicts();
                    if (conflicts.size() > 0) {
                        if (invite.isRecurrence() && !checkResult.tooManyConflicts()) {
                            // There are some conflicts, but within resource's allowed limit.
                            if (resource.autoAcceptDecline()) {
                                // Let's accept partially.  (Accept the series and decline conflicting instances.)
                                List<Invite> replyInvites = new ArrayList<Invite>();
                                // the REPLY for the ACCEPT of recurrence series
                                Invite acceptInv = makeReplyInvite(account, authAcct, lc, onBehalfOf, allowPrivateAccess, invite, invite.getRecurId(), CalendarMailSender.VERB_ACCEPT);
                                for (Conflict conflict : conflicts) {
                                    Instance inst = conflict.getInstance();
                                    InviteInfo invInfo = inst.getInviteInfo();
                                    Invite inv = getInvite(invInfo.getMsgId(), invInfo.getComponentId());
                                    RecurId rid = inst.makeRecurId(inv);
                                    // Record the decline status in reply list.
                                    getReplyList().modifyPartStat(resource, rid, null, resource.getName(), null, null, IcalXmlStrMap.PARTSTAT_DECLINED, false, invite.getSeqNo(), opTime);
                                    replyListUpdated = true;
                                    // Make REPLY VEVENT for the declined instance.
                                    Invite replyInv = makeReplyInvite(account, authAcct, lc, onBehalfOf, allowPrivateAccess, inv, rid, CalendarMailSender.VERB_DECLINE);
                                    replyInvites.add(replyInv);
                                }
                                if (needReplyEmail) {
                                    ICalTimeZone tz = chooseReplyTZ(invite);
                                    // Send one email to accept the series.
                                    String declinedInstances = getDeclinedTimesString(octxt, mbox, conflicts, invite.isAllDayEvent(), tz, lc);
                                    String msg = L10nUtil.getMessage(MsgKey.calendarResourceDeclinedInstances, lc) + "\r\n\r\n" + declinedInstances;
                                    CalendarMailSender.sendResourceAutoReply(octxt, mbox, true, CalendarMailSender.VERB_ACCEPT, true, msg, this, invite, new Invite[] { acceptInv }, mmInv);
                                    // Send another email to decline instances, all in one email.
                                    String conflictingTimes = getBusyTimesString(octxt, mbox, conflicts, tz, lc, false);
                                    msg = L10nUtil.getMessage(MsgKey.calendarResourceDeclinedInstances, lc) + "\r\n\r\n" + declinedInstances + "\r\n" + L10nUtil.getMessage(MsgKey.calendarResourceDeclineReasonConflict, lc) + "\r\n\r\n" + conflictingTimes;
                                    CalendarMailSender.sendResourceAutoReply(octxt, mbox, true, CalendarMailSender.VERB_DECLINE, true, msg, this, invite, replyInvites.toArray(new Invite[0]), mmInv);
                                    replySent = true;
                                }
                            } else {
                            // Auto-accept is not enabled.  Auto-decline is enabled, but there weren't
                            // enough conflicting instances to decline outright.  So we just stay
                            // silent and let the human admin deal with it.  This case is rather
                            // ambiguous, and can be avoided by configuring the resource to allow
                            // zero conflicting instance.
                            }
                        } else {
                            // Too many conflicts.  Decline outright.
                            partStat = IcalXmlStrMap.PARTSTAT_DECLINED;
                            if (needReplyEmail) {
                                ICalTimeZone tz = chooseReplyTZ(invite);
                                String msg = L10nUtil.getMessage(MsgKey.calendarResourceDeclineReasonConflict, lc) + "\r\n\r\n" + getBusyTimesString(octxt, mbox, conflicts, tz, lc, checkResult.hasMoreConflicts());
                                Invite replyInv = makeReplyInvite(account, authAcct, lc, onBehalfOf, allowPrivateAccess, invite, invite.getRecurId(), CalendarMailSender.VERB_DECLINE);
                                CalendarMailSender.sendResourceAutoReply(octxt, mbox, true, CalendarMailSender.VERB_DECLINE, false, msg, this, invite, new Invite[] { replyInv }, mmInv);
                                replySent = true;
                            }
                        }
                    }
                }
            }
            if (!replySent && IcalXmlStrMap.PARTSTAT_ACCEPTED.equals(partStat)) {
                if (needReplyEmail) {
                    Invite replyInv = makeReplyInvite(account, authAcct, lc, onBehalfOf, allowPrivateAccess, invite, invite.getRecurId(), CalendarMailSender.VERB_ACCEPT);
                    CalendarMailSender.sendResourceAutoReply(octxt, mbox, true, CalendarMailSender.VERB_ACCEPT, false, null, this, invite, new Invite[] { replyInv }, mmInv);
                }
            }
            // Record the final outcome in the replies list.
            if (IcalXmlStrMap.PARTSTAT_NEEDS_ACTION.equals(partStat)) {
                getReplyList().modifyPartStat(resource, invite.getRecurId(), null, resource.getName(), null, null, partStat, false, invite.getSeqNo(), opTime);
                replyListUpdated = true;
            }
            if (forCreate && replyListUpdated)
                saveMetadata();
        }
    }
    CreateCalendarItemRecorder recorder = (CreateCalendarItemRecorder) mbox.getRedoRecorder();
    recorder.setCalendarItemPartStat(partStat);
    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 : Locale(java.util.Locale) Account(com.zimbra.cs.account.Account) InviteInfo(com.zimbra.cs.mailbox.calendar.InviteInfo) FBInstance(com.zimbra.cs.fb.FreeBusy.FBInstance) ArrayList(java.util.ArrayList) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) CreateCalendarItemPlayer(com.zimbra.cs.redolog.op.CreateCalendarItemPlayer) CreateCalendarItemRecorder(com.zimbra.cs.redolog.op.CreateCalendarItemRecorder) RedoLogProvider(com.zimbra.cs.redolog.RedoLogProvider) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) CalendarResource(com.zimbra.cs.account.CalendarResource) Invite(com.zimbra.cs.mailbox.calendar.Invite) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone)

Example 5 with RedoLogProvider

use of com.zimbra.cs.redolog.RedoLogProvider in project zm-mailbox by Zimbra.

the class Zimbra method shutdown.

public static synchronized void shutdown() throws ServiceException {
    if (!sInited)
        return;
    sInited = false;
    if (sIsMailboxd) {
        PurgeThread.shutdown();
        AutoProvisionThread.shutdown();
    }
    ZimbraApplication app = ZimbraApplication.getInstance();
    app.shutdown();
    if (sIsMailboxd) {
        if (app.supports(MemoryStats.class.getName())) {
            MemoryStats.shutdown();
        }
        if (app.supports(WaitSetMgr.class.getName())) {
            WaitSetMgr.shutdown();
        }
    }
    RedoLogProvider redoLog = RedoLogProvider.getInstance();
    if (sIsMailboxd) {
        if (!redoLog.isSlave()) {
            ServerManager.getInstance().stopServers();
        }
        dbSessionCleanup();
        SessionCache.shutdown();
        CuratorManager curatorManager = CuratorManager.getInstance();
        if (curatorManager != null) {
            curatorManager.stop();
        }
    }
    MailboxIndex.shutdown();
    if (sIsMailboxd) {
        redoLog.shutdown();
    }
    if (app.supports(ExtensionUtil.class.getName())) {
        ExtensionUtil.destroyAll();
    }
    if (app.supports(MemcachedConnector.class.getName())) {
        MemcachedConnector.shutdown();
    }
    if (app.supports(EhcacheManager.class.getName())) {
        EhcacheManager.getInstance().shutdown();
    }
    MailboxManager.getInstance().shutdown();
    if (sIsMailboxd) {
        StoreManager.getInstance().shutdown();
    }
    ZimbraHttpConnectionManager.shutdownReaperThread();
    sTimer.cancel();
    try {
        DbPool.shutdown();
    } catch (Exception ignored) {
    }
    EphemeralStore.getFactory().shutdown();
}
Also used : WaitSetMgr(com.zimbra.cs.session.WaitSetMgr) MemcachedConnector(com.zimbra.cs.memcached.MemcachedConnector) RedoLogProvider(com.zimbra.cs.redolog.RedoLogProvider) CuratorManager(com.zimbra.cs.zookeeper.CuratorManager) ServiceException(com.zimbra.common.service.ServiceException) DocumentException(org.dom4j.DocumentException) IOException(java.io.IOException) ConfigException(com.zimbra.common.localconfig.ConfigException) ExtensionUtil(com.zimbra.cs.extension.ExtensionUtil)

Aggregations

RedoLogProvider (com.zimbra.cs.redolog.RedoLogProvider)5 ConfigException (com.zimbra.common.localconfig.ConfigException)2 ServiceException (com.zimbra.common.service.ServiceException)2 Account (com.zimbra.cs.account.Account)2 CalendarResource (com.zimbra.cs.account.CalendarResource)2 Invite (com.zimbra.cs.mailbox.calendar.Invite)2 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)2 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)2 MemcachedConnector (com.zimbra.cs.memcached.MemcachedConnector)2 RedoableOp (com.zimbra.cs.redolog.op.RedoableOp)2 ItemId (com.zimbra.cs.service.util.ItemId)2 WaitSetMgr (com.zimbra.cs.session.WaitSetMgr)2 CuratorManager (com.zimbra.cs.zookeeper.CuratorManager)2 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2 DocumentException (org.dom4j.DocumentException)2 PrefCalendarApptVisibility (com.zimbra.common.account.ZAttrProvisioning.PrefCalendarApptVisibility)1 ICalTimeZone (com.zimbra.common.calendar.ICalTimeZone)1 ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)1 ICalTok (com.zimbra.common.calendar.ZCalendar.ICalTok)1