/* ******************************************************************** Licensed to Jasig under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Jasig licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.bedework.calsvc; import org.bedework.calcorei.CalintfDefs; import org.bedework.calcorei.CoreEventInfo; import org.bedework.calcorei.CoreEventsI.UpdateEventResult; import org.bedework.caldav.util.sharing.AccessType; import org.bedework.caldav.util.sharing.InviteType; import org.bedework.caldav.util.sharing.UserType; import org.bedework.calfacade.BwCalendar; import org.bedework.calfacade.BwCategory; import org.bedework.calfacade.BwContact; import org.bedework.calfacade.BwEvent; import org.bedework.calfacade.BwEventAnnotation; import org.bedework.calfacade.BwEventProxy; import org.bedework.calfacade.BwFilterDef; import org.bedework.calfacade.BwLocation; import org.bedework.calfacade.BwPrincipal; import org.bedework.calfacade.BwResource; import org.bedework.calfacade.BwResourceContent; import org.bedework.calfacade.BwSystem; import org.bedework.calfacade.exc.CalFacadeAccessException; import org.bedework.calfacade.exc.CalFacadeException; import org.bedework.calfacade.svc.BwAdminGroup; import org.bedework.calfacade.svc.BwAuthUser; import org.bedework.calfacade.svc.BwCalSuite; import org.bedework.calfacade.svc.BwPreferences; import org.bedework.calfacade.svc.BwView; import org.bedework.calfacade.svc.EventInfo; import org.bedework.calsvci.RestoreIntf; import org.bedework.util.xml.tagdefs.AppleServerTags; import java.util.Collection; import java.util.Set; import java.util.TreeSet; /** Allow the restore process to work. * * @author Mike Douglass douglm@rpi.edu * @version 1.0 */ class RestoreImpl extends CalSvcDb implements RestoreIntf { private RestoreLogger log; private boolean debug; protected int currentMode = CalintfDefs.userMode; private boolean transactionStarted; private int curBatchSize; private int batchSize; RestoreImpl(final CalSvc svci) throws CalFacadeException { super(svci); } @Override public void setLogger(final RestoreLogger val) { log = val; debug = log.isDebugEnabled(); } @Override public void setBatchSize(final int val) { batchSize = val; } @Override public void endTransactionNow() throws Throwable { if (transactionStarted) { getSvc().endTransaction(); getSvc().close(); } transactionStarted = false; curBatchSize = 0; } @Override public void endTransaction() throws Throwable { if ((batchSize > 0) && (curBatchSize < batchSize)) { return; } endTransactionNow(); } @Override public void checkEmptySystem() throws Throwable { try { startTransaction(); if (getSvc().getSysparsHandler().present()) { throw new CalFacadeException("System is not empty - restore terminated"); } } finally { endTransaction(); } } @Override public void restoreSyspars(final BwSystem o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restorePrincipal(final BwPrincipal o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } catch (final Throwable t) { handleException(t, "Exception restoring user " + o); } finally { endTransaction(); } } @Override public void restoreAdminGroup(final BwAdminGroup o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); if (debug) { log.debug("Saved admin group " + o); } } finally { endTransaction(); } } @Override public void addAdminGroupMember(final BwAdminGroup o, final BwPrincipal pr) throws Throwable { try { startTransaction(); getCal().addMember(o, pr, true); } finally { endTransaction(); } } @Override public BwAdminGroup getAdminGroup(final String account) throws Throwable { startTransaction(); return (BwAdminGroup)getCal().findGroup(account, true); } @Override public void restoreAuthUser(final BwAuthUser o) throws Throwable { try { startTransaction(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreEvent(final EventInfo ei) throws Throwable { try { startTransaction(); final UpdateEventResult uer = getCal().addEvent(ei, false, // scheduling false); if (!uer.addedUpdated) { throw new CalFacadeException(uer.errorCode); } if (uer.failedOverrides != null) { error("Following overrides failed for event "); error(ei.getEvent().toString()); for (final BwEventProxy proxy: uer.failedOverrides) { error(proxy.toString()); } } } finally { endTransaction(); } } @Override public BwEvent getEvent(final BwPrincipal owner, final String colPath, final String recurrenceId, final String uid) throws Throwable { startTransaction(); final Collection<CoreEventInfo> ceis = getCal().getEvent(colPath, uid); if (ceis.size() != 1) { error("Expected one event for {" + colPath + ", " + recurrenceId + ", " + uid + "} found " + ceis.size()); return null; } final CoreEventInfo ei = ceis.iterator().next(); BwEvent ev = null; if (recurrenceId == null) { ev = ei.getEvent(); } else { for (final CoreEventInfo cei: ei.getOverrides()){ if (cei.getEvent().getRecurrenceId().equals(recurrenceId)) { ev = cei.getEvent(); break; } } } if (ev == null) { return null; } if (ev instanceof BwEventAnnotation) { ev = new BwEventProxy((BwEventAnnotation)ev); } return ev; } @Override public void restoreCategory(final BwCategory o) throws Throwable { try { startTransaction(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreCalSuite(final BwCalSuite o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreLocation(final BwLocation o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreContact(final BwContact o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreFilter(final BwFilterDef o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public void restoreResource(final BwResource o) throws Throwable { try { startTransaction(); o.markUnsaved(); getCal().saveOrUpdate(o); final BwResourceContent rc = o.getContent(); rc.markUnsaved(); getCal().saveOrUpdate(rc); } finally { endTransaction(); } } @Override public void restoreUserPrefs(final BwPreferences o) throws Throwable { try { startTransaction(); /* If the indexer or some other activity is running this can result in * a preferences object being created. See if one exists. */ BwPreferences p = getSvc().getPreferences(o.getOwnerHref()); if (p != null) { warn("Found instance of preferences for " + o.getOwnerHref()); o.setId(p.getId()); //noinspection UnusedAssignment p = (BwPreferences)getSvc().merge(o); } else { p = o; /* Ensure views are unsaved objects */ final Collection<BwView> v = p.getViews(); if (v != null) { for (final BwView view: v) { view.markUnsaved(); } } p.markUnsaved(); } getCal().saveOrUpdate(o); } finally { endTransaction(); } } @Override public BwCalendar getCalendar(final String path) throws Throwable { startTransaction(); return getCols().get(path); } @Override public BwCategory getCategory(final String uid) throws Throwable { startTransaction(); return getSvc().getCategoriesHandler().getPersistent(uid); } @Override public BwContact getContact(final String uid) throws Throwable { startTransaction(); return getSvc().getContactsHandler().getPersistent(uid); } @Override public BwLocation getLocation(final String uid) throws Throwable { startTransaction(); return getSvc().getLocationsHandler().getPersistent(uid); } @Override public BwPrincipal getPrincipal(final String href) throws CalFacadeException { startTransaction(); return getSvc().getUsersHandler().getPrincipal(href); } @Override public void saveRootCalendar(final BwCalendar val) throws Throwable { // Ensure id not set val.markUnsaved(); try { startTransaction(); getCal().saveOrUpdate(val); } finally { endTransaction(); } } @Override public void addCalendar(final BwCalendar o) throws Throwable { // Ensure id not set o.markUnsaved(); try { startTransaction(); getCal().saveOrUpdate(o); curBatchSize++; } finally { endTransaction(); } } @Override public FixAliasResult fixSharee(final BwCalendar col, final String shareeHref, final AccessType a) throws CalFacadeException { /* First ensure this alias is not circular */ final Set<String> paths = new TreeSet<>(); BwCalendar curCol = col; while (curCol.getInternalAlias()) { if (paths.contains(curCol.getPath())) { return FixAliasResult.circular; } paths.add(curCol.getPath()); try { curCol = getCols().resolveAliasIdx(curCol, false, false); } catch (final CalFacadeAccessException ignored) { // Just exit the loop here. We may have multiple levels of aliases and one or more may not be accessible, // but we can still check for circularity and broken aliases. break; } if (curCol == null) { return FixAliasResult.broken; } } // See if we are in the invite list final InviteType invite = getSvc().getSharingHandler().getInviteStatus(col); final String shareeCua = getSvc().getDirectories().userToCaladdr(shareeHref); UserType uentry = invite.finduser(shareeCua); if (uentry != null) { // Already in list of sharers return FixAliasResult.ok; } /* Now fix the sharing invite info */ uentry = new UserType(); uentry.setHref(shareeCua); uentry.setInviteStatus(AppleServerTags.inviteAccepted); //uentry.setCommonName(...); uentry.setAccess(a); //uentry.setSummary(s.getSummary()); invite.getUsers().add(uentry); try { col.setQproperty(AppleServerTags.invite, invite.toXml()); getCols().update(col); } catch (final CalFacadeException cfe) { throw cfe; } catch (final Throwable t) { throw new CalFacadeException(t); } return FixAliasResult.reshared; } /* ==================================================================== * Private methods * ==================================================================== */ private void startTransaction() throws CalFacadeException { if (transactionStarted) { return; } getSvc().open(); getSvc().beginTransaction(); transactionStarted = true; } private void handleException(final Throwable t, final String msg) { if (log == null) { return; } log.error(msg, t); } protected void info(final String msg) { if (log == null) { return; } log.info(msg); } @Override protected void warn(final String msg) { if (log == null) { return; } log.warn(msg); } @Override protected void error(final String msg) { if (log == null) { return; } log.error(msg); } }