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 *     <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
016 *
017 * $Id: InputFileMimetypeValidator.java 28610 2008-01-09 17:13:52Z sfermigier $
018 */
019
020package org.nuxeo.ecm.platform.ui.web.component.file;
021
022import javax.faces.component.StateHolder;
023import javax.faces.component.UIComponent;
024import javax.faces.context.FacesContext;
025import javax.faces.validator.Validator;
026import javax.faces.validator.ValidatorException;
027
028import org.apache.commons.lang.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032import com.sun.faces.util.MessageFactory;
033
034/**
035 * Input file mimetype validator.
036 * <p>
037 * Validates an {@link InputFileInfo} blob value in case it's been uploaded. Accepted mimetypes are set using the
038 * "extensions" attribute, representing the list of accepted extension suffixes separated by commas (for instance:
039 * ".jpeg, .png").
040 * <p>
041 * Validation is done on the filename, no actual mimetype check is done for now.
042 *
043 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
044 */
045public class InputFileMimetypeValidator implements Validator, StateHolder {
046
047    public static final String VALIDATOR_ID = "InputFileMimetypeValidator";
048
049    @SuppressWarnings("unused")
050    private static final Log log = LogFactory.getLog(InputFileSizeValidator.class);
051
052    private String[] extensions;
053
054    private boolean authorized = true;
055
056    private boolean hidden = false;
057
058    private boolean transientValue = false;
059
060    /**
061     * The message identifier of the {@link javax.faces.application.FacesMessage} to be created if the authorized
062     * extensions check fails. The message format string for this message may optionally include the following
063     * placeholders:
064     * <ul>
065     * <li><code>{0}</code> replaced by the configured auhtorized extensions.</li>
066     * </ul>
067     * </p>
068     */
069    public static final String MIMETYPE_AUTHORIZED_EXTENSIONS_MESSAGE_ID = "error.inputFile.authorizedExtensions";
070
071    /**
072     * The message identifier of the {@link javax.faces.application.FacesMessage} to be created if the unauthorized
073     * extensions check fails. The message format string for this message may optionally include the following
074     * placeholders:
075     * <ul>
076     * <li><code>{0}</code> replaced by the configured unauthorized extensions.</li>
077     * </ul>
078     * </p>
079     */
080    public static final String MIMETYPE_UNAUTHORIZED_EXTENSIONS_MESSAGE_ID = "error.inputFile.unauthorizedExtensions";
081
082    @Override
083    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
084        if (value != null && extensions != null && extensions.length > 0) {
085            if (value instanceof InputFileInfo) {
086                InputFileInfo info = (InputFileInfo) value;
087                String choice = info.getConvertedChoice();
088                if (!InputFileChoice.isUploadOrKeepTemp(choice)) {
089                    return;
090                }
091                String filename = info.getConvertedFilename();
092                if (filename != null) {
093                    String lowerCaseFilename = filename.toLowerCase();
094                    boolean error = authorized;
095                    for (String extension : extensions) {
096                        String lowerCaseExtension = extension.trim().toLowerCase();
097                        if (lowerCaseFilename.endsWith(lowerCaseExtension)) {
098                            error = !authorized;
099                            break;
100                        }
101                    }
102                    // TODO: handle content types
103                    if (error) {
104                        String messageId = authorized ? MIMETYPE_AUTHORIZED_EXTENSIONS_MESSAGE_ID
105                                : MIMETYPE_UNAUTHORIZED_EXTENSIONS_MESSAGE_ID;
106                        throw new ValidatorException(MessageFactory.getMessage(context, messageId,
107                                StringUtils.join(extensions, ", ")));
108                    }
109                }
110            }
111        }
112    }
113
114    public String[] getExtensions() {
115        return extensions;
116    }
117
118    public void setExtensions(String[] extensions) {
119        this.extensions = extensions;
120    }
121
122    public boolean isAuthorized() {
123        return authorized;
124    }
125
126    public void setAuthorized(boolean authorized) {
127        this.authorized = authorized;
128    }
129
130    public boolean isHidden() {
131        return hidden;
132    }
133
134    public void setHidden(boolean hidden) {
135        this.hidden = hidden;
136    }
137
138    @Override
139    public boolean isTransient() {
140        return transientValue;
141    }
142
143    @Override
144    public void setTransient(boolean newTransientValue) {
145        transientValue = newTransientValue;
146    }
147
148    @Override
149    public Object saveState(FacesContext context) {
150        Object[] values = new Object[3];
151        values[0] = extensions;
152        values[1] = authorized;
153        values[2] = hidden;
154        return values;
155    }
156
157    @Override
158    public void restoreState(FacesContext context, Object state) {
159        Object[] values = (Object[]) state;
160        extensions = (String[]) values[0];
161        authorized = ((Boolean) values[1]).booleanValue();
162        hidden = ((Boolean) values[2]).booleanValue();
163    }
164
165}