001/* 002 * (C) Copyright 2017 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 * Funsho David 018 * 019 */ 020 021package org.nuxeo.ecm.core.filter; 022 023import com.google.common.base.CharMatcher; 024import org.apache.commons.lang.StringEscapeUtils; 025import org.nuxeo.ecm.core.api.DataModel; 026import org.nuxeo.ecm.core.api.DocumentModel; 027import org.nuxeo.ecm.core.api.impl.DataModelImpl; 028import org.nuxeo.ecm.core.api.model.DocumentPart; 029import org.nuxeo.ecm.core.api.model.Property; 030import org.nuxeo.ecm.core.api.model.impl.ArrayProperty; 031import org.nuxeo.ecm.core.api.model.impl.ComplexProperty; 032import org.nuxeo.ecm.core.api.model.impl.ListProperty; 033import org.nuxeo.ecm.core.api.model.impl.primitives.StringProperty; 034import org.nuxeo.runtime.model.ComponentInstance; 035import org.nuxeo.runtime.model.DefaultComponent; 036 037import java.io.Serializable; 038import java.util.List; 039 040/** 041 * @since 9.1 042 */ 043public class CharacterFilteringServiceImpl extends DefaultComponent implements CharacterFilteringService { 044 045 public static final String FILTERING_XP = "filtering"; 046 047 protected CharacterFilteringServiceDescriptor desc; 048 049 protected CharMatcher charsToRemove; 050 051 @Override 052 public void registerContribution(Object contrib, String point, ComponentInstance contributor) { 053 if (FILTERING_XP.equals(point)) { 054 055 desc = (CharacterFilteringServiceDescriptor) contrib; 056 057 CharMatcher charsToPreserve = CharMatcher.anyOf("\r\n\t"); 058 CharMatcher allButPreserved = charsToPreserve.negate(); 059 charsToRemove = CharMatcher.JAVA_ISO_CONTROL.and(allButPreserved); 060 charsToRemove = charsToRemove.or(CharMatcher.INVISIBLE.and(CharMatcher.WHITESPACE.negate())); 061 062 List<String> additionalChars = desc.getDisallowedChars(); 063 String otherCharsToRemove = ""; 064 if (additionalChars != null && !additionalChars.isEmpty()) { 065 for (String c : additionalChars) { 066 otherCharsToRemove += StringEscapeUtils.unescapeJava(c); 067 } 068 charsToRemove = charsToRemove.or(CharMatcher.anyOf(otherCharsToRemove)); 069 } 070 } else { 071 throw new RuntimeException("Unknown extension point: " + point); 072 } 073 } 074 075 @Override 076 public String filter(String value) { 077 return charsToRemove.removeFrom(value); 078 } 079 080 @Override 081 public void filter(DocumentModel docModel) { 082 if (desc.isEnabled()) { 083 // check only loaded datamodels to find the dirty ones 084 for (DataModel dm : docModel.getDataModelsCollection()) { // only loaded 085 if (!dm.isDirty()) { 086 continue; 087 } 088 DocumentPart part = ((DataModelImpl) dm).getDocumentPart(); 089 for (Property prop : part.getChildren()) { 090 filterProperty(prop, docModel); 091 } 092 } 093 } 094 } 095 096 private void filterProperty(Property prop, DocumentModel docModel) { 097 if (!prop.isDirty()) { 098 return; 099 } 100 if (prop instanceof StringProperty) { 101 String p = (String) prop.getValue(); 102 if (p != null && charsToRemove.matchesAnyOf(p)) { 103 String filteredProp = filter(p); 104 docModel.setPropertyValue(prop.getXPath(), filteredProp); 105 } 106 } else if (prop instanceof ArrayProperty) { 107 Serializable value = prop.getValue(); 108 if (value instanceof Object[]) { 109 Object[] arrayProp = (Object[]) value; 110 boolean modified = false; 111 for (int i = 0; i < arrayProp.length; i++) { 112 if (arrayProp[i] instanceof String) { 113 String p = (String) arrayProp[i]; 114 if (charsToRemove.matchesAnyOf(p)) { 115 arrayProp[i] = filter(p); 116 modified = true; 117 } 118 } 119 } 120 if (modified) { 121 docModel.setPropertyValue(prop.getXPath(), arrayProp); 122 } 123 } 124 } else if (prop instanceof ComplexProperty) { 125 ComplexProperty complexProp = (ComplexProperty) prop; 126 for (Property subProp : complexProp.getChildren()) { 127 filterProperty(subProp, docModel); 128 } 129 } else if (prop instanceof ListProperty) { 130 ListProperty listProp = (ListProperty) prop; 131 for (Property p : listProp) { 132 filterProperty(p, docModel); 133 } 134 } 135 } 136}