001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.filemanager.core.listener;
023
024import java.io.IOException;
025import java.security.DigestInputStream;
026import java.security.MessageDigest;
027import java.security.NoSuchAlgorithmException;
028import java.util.List;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.common.utils.Base64;
033import org.nuxeo.ecm.core.api.Blob;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.PropertyException;
036import org.nuxeo.ecm.core.api.model.Property;
037import org.nuxeo.ecm.core.event.Event;
038import org.nuxeo.ecm.core.event.EventContext;
039import org.nuxeo.ecm.core.event.EventListener;
040import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
041import org.nuxeo.ecm.platform.filemanager.api.FileManager;
042import org.nuxeo.runtime.api.Framework;
043
044import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ABOUT_TO_CREATE;
045import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE;
046
047public class DigestComputer implements EventListener {
048
049    private boolean initDone = false;
050
051    private List<String> xpathFields;
052
053    private String digestAlgo = "sha-256";
054
055    private Boolean activateDigestComputation = false;
056
057    private static final Log log = LogFactory.getLog(DigestComputer.class);
058
059    private boolean initIfNeeded() {
060        if (!initDone) {
061            FileManager fm = Framework.getService(FileManager.class);
062            xpathFields = fm.getFields();
063            digestAlgo = fm.getDigestAlgorithm();
064            activateDigestComputation = fm.isDigestComputingEnabled();
065            initDone = true;
066        }
067        return initDone;
068    }
069
070    private void addDigestToDocument(DocumentModel doc) {
071        for (String xpathField : xpathFields) {
072            Property blobProp = null;
073            try {
074                blobProp = doc.getProperty(xpathField);
075            } catch (PropertyException e) {
076                log.debug("Property " + xpathField + " not found on doc, skipping");
077            }
078            if (blobProp != null && !blobProp.isPhantom() && blobProp.isDirty()) {
079                try {
080                    Blob blob = (Blob) blobProp.getValue();
081                    if (blob != null) {
082                        String digest = computeDigest(blob);
083                        if (!digest.equals(blob.getDigest())) {
084                            blob.setDigest(digest);
085                        }
086                    }
087                } catch (PropertyException | IOException e) {
088                    log.error("Error while trying to save blob digest", e);
089                }
090            }
091        }
092    }
093
094    private String computeDigest(Blob blob) throws IOException {
095
096        MessageDigest md;
097        try {
098            md = MessageDigest.getInstance(digestAlgo);
099        } catch (NoSuchAlgorithmException e) {
100            throw new RuntimeException(e);
101        }
102
103        DigestInputStream dis = new DigestInputStream(blob.getStream(), md);
104        while (dis.available() > 0) {
105            dis.read();
106        }
107        byte[] b = md.digest();
108        return Base64.encodeBytes(b);
109    }
110
111    public void handleEvent(Event event) {
112        if (!initIfNeeded()) {
113            return;
114        }
115
116        if (!activateDigestComputation) {
117            return;
118        }
119
120        EventContext ctx = event.getContext();
121        String evt = event.getName();
122        if (ABOUT_TO_CREATE.equals(evt) || BEFORE_DOC_UPDATE.equals(evt)) {
123
124            if (ctx instanceof DocumentEventContext) {
125                DocumentEventContext docCtx = (DocumentEventContext) ctx;
126                DocumentModel doc = docCtx.getSourceDocument();
127                if (doc == null || doc.isProxy()) {
128                    return;
129                }
130                addDigestToDocument(doc);
131            }
132        }
133    }
134
135}