/** * OpenKM, Open Document Management System (http://www.openkm.com) * Copyright (c) 2006-2011 Paco Avila & Josep Llort * * No bytes were intentionally harmed during the development of this application. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.openkm.jcr; import java.io.File; import java.io.IOException; import java.security.PrivilegedAction; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFormatException; import javax.jcr.Workspace; import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import javax.jcr.query.InvalidQueryException; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.security.auth.Subject; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.api.jsr283.security.AccessControlList; import org.apache.jackrabbit.api.jsr283.security.AccessControlManager; import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy; import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicyIterator; import org.apache.jackrabbit.api.jsr283.security.Privilege; import org.apache.jackrabbit.core.NodeImpl; import org.apache.jackrabbit.core.RepositoryCopier; import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.lock.LockManager; import org.apache.jackrabbit.core.lock.LockManagerImpl; import org.apache.jackrabbit.core.security.principal.PrincipalImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.openkm.bean.Document; import com.openkm.bean.Folder; import com.openkm.bean.Mail; import com.openkm.bean.StatsInfo; import com.openkm.core.Config; import com.openkm.core.DatabaseException; import com.openkm.core.RepositoryException; import com.openkm.dao.LockTokenDAO; import com.openkm.dao.bean.LockToken; import com.openkm.module.direct.DirectAuthModule; import com.openkm.module.direct.DirectRepositoryModule; import com.openkm.util.StackTraceUtils; public class JCRUtils { private static Logger log = LoggerFactory.getLogger(JCRUtils.class); private static long activeSessions = 0; private static long sessionCreationCount = 0; private static long sessionDestroyCount = 0; /* //addd by vissu on 7nov private static String TAXONOMY_DOCUMENTS = "/jcr:root/okm:root//element(*,okm:document)"; private static String TEMPLATES_DOCUMENTS = "/jcr:root/okm:templates//element(*,okm:document)"; private static String PERSONAL_DOCUMENTS = "/jcr:root/okm:personal//element(*,okm:document)"; private static String TRASH_DOCUMENTS = "/jcr:root/okm:trash//element(*,okm:document)"; */ /** * Get parent node. */ public static String getParent(String path) { log.debug("getParent({})", path); int lastSlash = path.lastIndexOf('/'); String ret = (lastSlash > 0)?path.substring(0, lastSlash):""; log.debug("getParent: {}", ret); return ret; } /** * Get node name. */ public static String getName(String path) { log.debug("getName({})", path); String ret = path.substring(path.lastIndexOf('/') + 1); log.debug("getName: {}", ret); return ret; } /** * Eliminate dangerous chars in node name. * TODO Keep on sync with uploader:com.openkm.applet.Util.escape(String) */ public static String escape(String name) { log.debug("escape({})", name); String ret = name.replace('/', ' '); ret = ret.replace(':', ' '); ret = ret.replace('[', ' '); ret = ret.replace(']', ' '); ret = ret.replace('*', ' '); ret = ret.replace('\'', ' '); ret = ret.replace('"', ' '); ret = ret.replace('|', ' '); ret = ret.trim(); log.debug("escape: {}", ret); return ret; } /** * Convert a Value array to String array and add a user id. */ public static String[] usrValue2String(Value[] values, String usrId) throws ValueFormatException, IllegalStateException, javax.jcr.RepositoryException { ArrayList<String> list = new ArrayList<String>(); for (int i=0; i<values.length; i++) { // Admin and System user is not propagated across the child nodes if (!values[i].getString().equals(Config.SYSTEM_USER) && !values[i].getString().equals(Config.ADMIN_USER)) { list.add(values[i].getString()); } } if (Config.USER_ASSIGN_DOCUMENT_CREATION) { // No add an user twice if (!list.contains(usrId)) { list.add(usrId); } } return (String[]) list.toArray(new String[list.size()]); } /** * Convert a Value array to String array. */ public static String[] rolValue2String(Value[] values) throws ValueFormatException, IllegalStateException, javax.jcr.RepositoryException { ArrayList<String> list = new ArrayList<String>(); for (int i=0; i<values.length; i++) { // Do not propagate private OpenKM roles if (!values[i].getString().equals(Config.DEFAULT_ADMIN_ROLE)) { list.add(values[i].getString()); } } return (String[]) list.toArray(new String[list.size()]); } /** * */ public static String[] value2String(Value[] values) throws ValueFormatException, IllegalStateException, javax.jcr.RepositoryException { ArrayList<String> list = new ArrayList<String>(); for (int i=0; i<values.length; i++) { list.add(values[i].getString()); } return (String[]) list.toArray(new String[list.size()]); } /** * This method discards all pending changes currently recorded in this * Session that apply to this Node or any of its descendants. * * @param node The node to cancel. */ public static void discardsPendingChanges(Node node) { try { // JSR-170: page 173 // http://www.day.com/maven/jsr170/javadocs/jcr-1.0/javax/jcr/Item.html#refresh(boolean) if (node != null) { node.refresh(false); } else { log.warn("node == NULL"); } } catch (javax.jcr.RepositoryException e1) { e1.printStackTrace(); } } /** * This method discards all pending changes currently recorded in this * Session that apply to this Session. * * @param node The node to cancel. */ public static void discardsPendingChanges(Session session) { try { // http://www.day.com/maven/jsr170/javadocs/jcr-1.0/javax/jcr/Session.html#refresh(boolean) if (session != null) { session.refresh(false); } else { log.warn("session == NULL"); } } catch (javax.jcr.RepositoryException e1) { e1.printStackTrace(); } } /** * Make a silent logout * See http://jackrabbit.510166.n4.nabble.com/Lock-token-not-being-added-to-session-td2018601.html */ public static void logout(Session session) { if (session != null && session.isLive()) { for (String lt: session.getLockTokens()) { log.debug("Remove LockToken: {}", lt); session.removeLockToken(lt); } session.logout(); log.debug("#{} - {} Destroy session {} from {}", new Object[] { ++sessionDestroyCount, --activeSessions, session, StackTraceUtils.whoCalledMe() }); } } /** * Load lock tokens from database */ public static void loadLockTokens(Session session) throws DatabaseException, javax.jcr.RepositoryException { List<LockToken> ltList = LockTokenDAO.findByUser(session.getUserID()); for (Iterator<LockToken> it = ltList.iterator(); it.hasNext(); ) { LockToken lt = it.next(); session.addLockToken(lt.getToken()); } } /** * Add lock token to user data */ public static void addLockToken(Session session, Node node) throws DatabaseException, javax.jcr.RepositoryException { log.debug("addLockToken({}, {})", session, node); LockToken lt = new LockToken(); lt.setUser(session.getUserID()); lt.setToken(getLockToken(node.getUUID())); LockTokenDAO.add(lt); log.debug("addLockToken: void"); } /** * Remove lock token from user data */ public static void removeLockToken(Session session, Node node) throws DatabaseException, javax.jcr.RepositoryException { log.debug("removeLockToken({}, {})", session, node); LockTokenDAO.remove(session.getUserID(), getLockToken(node.getUUID())); log.debug("removeLockToken: void"); } /** * Obtain lock token from node */ public static String getLockToken(Session session, Node node) throws LockException, javax.jcr.RepositoryException { LockManager lm = ((SessionImpl)session).getLockManager(); Lock lock = ((LockManagerImpl) lm).getLock((NodeImpl)node); if (lock != null) { return lock.getLockToken(); } else { return null; } } /** * Obtain lock token from node id */ public static String getLockToken(String id) { StringBuffer buf = new StringBuffer(); buf.append(id.toString()); buf.append('-'); buf.append(getCheckDigit(id.toString())); return buf.toString(); } /** * Calculate check digit for lock token * * @see org.apache.jackrabbit.core.lock.LockToken.getCheckDigit(String uuid) */ private static char getCheckDigit(String uuid) { int result = 0; int multiplier = 36; for (int i = 0; i < uuid.length(); i++) { char c = uuid.charAt(i); if (c >= '0' && c <= '9') { int num = c - '0'; result += multiplier * num; multiplier--; } else if (c >= 'A' && c <= 'F') { int num = c - 'A' + 10; result += multiplier * num; multiplier--; } else if (c >= 'a' && c <= 'f') { int num = c - 'a' + 10; result += multiplier * num; multiplier--; } } int rem = result % 37; if (rem != 0) { rem = 37 - rem; } if (rem >= 0 && rem <= 9) { return (char) ('0' + rem); } else if (rem >= 10 && rem <= 35) { return (char) ('A' + rem - 10); } else { return '+'; } } /** * */ public static void grant(Session session, String path, String principal, String privilege) throws javax.jcr.RepositoryException { AccessControlManager acm = ((SessionImpl) session).getAccessControlManager(); AccessControlPolicyIterator acpi = acm.getApplicablePolicies(path); AccessControlPolicy acp = acpi.nextAccessControlPolicy(); Privilege[] privileges = new Privilege[] { acm.privilegeFromName(Privilege.JCR_ALL) }; ((AccessControlList) acp).addAccessControlEntry(new PrincipalImpl(principal), privileges); session.save(); } /** * Repository Hot-Backup */ public static File hotBackup() throws RepositoryException, IOException { log.debug("hotBackup()"); String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); String backDirName = Config.CONTEXT + "_" + date; File backDir = new File(System.getProperty("java.io.tmpdir") + File.separator + backDirName); FileUtils.deleteQuietly(backDir); backDir.mkdir(); boolean oldSystemReadonly = Config.SYSTEM_READONLY; try { Config.SYSTEM_READONLY = true; RepositoryCopier.copy((RepositoryImpl) DirectRepositoryModule.getRepository(), backDir); } catch (javax.jcr.RepositoryException e) { FileUtils.deleteQuietly(backDir); throw new RepositoryException(e.getMessage(), e); } finally { Config.SYSTEM_READONLY = oldSystemReadonly; } log.debug("hotBackup: {}", backDir); return backDir; } /** * Get JCR Session */ public static Session getSession() throws javax.jcr.LoginException, javax.jcr.RepositoryException, DatabaseException { Object obj = null; try { InitialContext ctx = new InitialContext(); Subject subject = (Subject) ctx.lookup("java:comp/env/security/subject"); obj = Subject.doAs(subject, new PrivilegedAction<Object>() { public Object run() { Session s = null; try { s = DirectRepositoryModule.getRepository().login(); } catch (javax.jcr.LoginException e) { return e; } catch (javax.jcr.RepositoryException e) { return e; } return s; } }); } catch (NamingException e) { throw new javax.jcr.LoginException(e.getMessage()); } if (obj instanceof javax.jcr.LoginException) { throw (javax.jcr.LoginException) obj; } else if (obj instanceof javax.jcr.RepositoryException) { throw (javax.jcr.LoginException) obj; } else if (obj instanceof javax.jcr.Session) { Session session = (javax.jcr.Session) obj; log.debug("#{} - {} Create session {} from {}", new Object[] { ++sessionCreationCount, ++activeSessions, session, StackTraceUtils.whoCalledMe() }); DirectAuthModule.loadUserData(session); return session; } else { return null; } } /** * Get node type */ public static String getNodeType(Node node) throws javax.jcr.RepositoryException { String ret = "unknown"; if (node.isNodeType(Document.TYPE)) { ret = Document.TYPE; } else if (node.isNodeType(Folder.TYPE)) { ret = Folder.TYPE; } else if (node.isNodeType(Mail.TYPE)) { ret = Mail.TYPE; } return ret; } /** * Get node uuid from path */ public static String getUUID(Session session, String path) throws javax.jcr.RepositoryException { Node rootNode = session.getRootNode(); Node node = rootNode.getNode(path.substring(1)); return node.getUUID(); } /** * Get node path from uuid */ public static String getPath(Session session, String uuid) throws javax.jcr.RepositoryException { Node node = session.getNodeByUUID(uuid); return node.getPath(); } /** * Calculate user quota */ public static long calculateQuota(Session session) throws javax.jcr.RepositoryException,DatabaseException { // "/jcr:root/okm:root//element(*, okm:document)[okm:content/@okm:author='"+session.getUserID()+"']"; //commented below by vissu to get the whole repository size on nov6 //String qs = "/jcr:root//element(*, okm:document)[okm:content/@okm:author='"+session.getUserID()+"']"; Session session1 = null; session1 = JCRUtils.getSession(); String qs = "/jcr:root/okm:root//element(*,okm:document)"; Workspace workspace = session1.getWorkspace(); QueryManager queryManager = workspace.getQueryManager(); Query query = queryManager.createQuery(qs, Query.XPATH); QueryResult result = query.execute(); long size = 0; for (NodeIterator nit = result.getNodes(); nit.hasNext(); ) { Node node = nit.nextNode(); Node contentNode = node.getNode(Document.CONTENT); size += contentNode.getProperty(Document.SIZE).getLong(); } return size; } }