001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.platform.filemanager.core.listener;
021
022import java.io.IOException;
023import java.security.DigestInputStream;
024import java.security.MessageDigest;
025import java.security.NoSuchAlgorithmException;
026import java.util.List;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.common.utils.Base64;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.PropertyException;
034import org.nuxeo.ecm.core.api.model.Property;
035import org.nuxeo.ecm.core.event.Event;
036import org.nuxeo.ecm.core.event.EventContext;
037import org.nuxeo.ecm.core.event.EventListener;
038import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
039import org.nuxeo.ecm.platform.filemanager.api.FileManager;
040import org.nuxeo.runtime.api.Framework;
041
042import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ABOUT_TO_CREATE;
043import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE;
044
045public class DigestComputer implements EventListener {
046
047    private boolean initDone = false;
048
049    private List<String> xpathFields;
050
051    private String digestAlgo = "sha-256";
052
053    private Boolean activateDigestComputation = false;
054
055    private static final Log log = LogFactory.getLog(DigestComputer.class);
056
057    private boolean initIfNeeded() {
058        if (!initDone) {
059            FileManager fm = Framework.getService(FileManager.class);
060            xpathFields = fm.getFields();
061            digestAlgo = fm.getDigestAlgorithm();
062            activateDigestComputation = fm.isDigestComputingEnabled();
063            initDone = true;
064        }
065        return initDone;
066    }
067
068    private void addDigestToDocument(DocumentModel doc) {
069        for (String xpathField : xpathFields) {
070            Property blobProp = null;
071            try {
072                blobProp = doc.getProperty(xpathField);
073            } catch (PropertyException e) {
074                log.debug("Property " + xpathField + " not found on doc, skipping");
075            }
076            if (blobProp != null && !blobProp.isPhantom() && blobProp.isDirty()) {
077                try {
078                    Blob blob = (Blob) blobProp.getValue();
079                    if (blob != null) {
080                        String digest = computeDigest(blob);
081                        if (!digest.equals(blob.getDigest())) {
082                            blob.setDigest(digest);
083                        }
084                    }
085                } catch (PropertyException | IOException e) {
086                    log.error("Error while trying to save blob digest", e);
087                }
088            }
089        }
090    }
091
092    private String computeDigest(Blob blob) throws IOException {
093
094        MessageDigest md;
095        try {
096            md = MessageDigest.getInstance(digestAlgo);
097        } catch (NoSuchAlgorithmException e) {
098            throw new RuntimeException(e);
099        }
100
101        DigestInputStream dis = new DigestInputStream(blob.getStream(), md);
102        while (dis.available() > 0) {
103            dis.read();
104        }
105        byte[] b = md.digest();
106        return Base64.encodeBytes(b);
107    }
108
109    public void handleEvent(Event event) {
110        if (!initIfNeeded()) {
111            return;
112        }
113
114        if (!activateDigestComputation) {
115            return;
116        }
117
118        EventContext ctx = event.getContext();
119        String evt = event.getName();
120        if (ABOUT_TO_CREATE.equals(evt) || BEFORE_DOC_UPDATE.equals(evt)) {
121
122            if (ctx instanceof DocumentEventContext) {
123                DocumentEventContext docCtx = (DocumentEventContext) ctx;
124                DocumentModel doc = docCtx.getSourceDocument();
125                if (doc == null || doc.isProxy()) {
126                    return;
127                }
128                addDigestToDocument(doc);
129            }
130        }
131    }
132
133}