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