1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2024, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.model.util;
16  
17  import ch.qos.logback.core.joran.action.ActionUtil;
18  import ch.qos.logback.core.model.ModelConstants;
19  import ch.qos.logback.core.model.PropertyModel;
20  import ch.qos.logback.core.spi.ContextAware;
21  import ch.qos.logback.core.spi.ContextAwareBase;
22  import ch.qos.logback.core.spi.ContextAwarePropertyContainer;
23  import ch.qos.logback.core.spi.PropertyContainer;
24  import ch.qos.logback.core.util.ContextUtil;
25  import ch.qos.logback.core.util.Loader;
26  import ch.qos.logback.core.util.OptionHelper;
27  
28  import java.io.FileInputStream;
29  import java.io.FileNotFoundException;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.net.URL;
33  import java.util.Properties;
34  
35  /**
36   * Given a {@link PropertyModel} offers methods to inject properties into a {@link PropertyContainer}.
37   *
38   * @since 1.5.1
39   */
40  public class PropertyModelHandlerHelper extends ContextAwareBase {
41  
42      public static final String HANDLE_PROPERTY_MODEL_METHOD_NAME = "handlePropertyModel";
43  
44      public PropertyModelHandlerHelper(ContextAware declaredOrigin) {
45          super(declaredOrigin);
46      }
47  
48      /**
49       * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer}
50       * parameter.
51       *
52       * @param capcm
53       * @param nameStr
54       * @param valueStr
55       * @param fileStr
56       * @param resourceStr
57       * @param scopeStr
58       *
59       */
60      public void handlePropertyModel(ContextAwarePropertyContainer capcm, String nameStr, String valueStr,
61              String fileStr, String resourceStr, String scopeStr) {
62          PropertyModel propertyModel = new PropertyModel();
63          propertyModel.setName(nameStr);
64          propertyModel.setValue(valueStr);
65          propertyModel.setFile(fileStr);
66          propertyModel.setResource(resourceStr);
67  
68          propertyModel.setScopeStr(scopeStr);
69  
70          handlePropertyModel(capcm, propertyModel);
71      }
72  
73      /**
74       * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer}
75       * parameter.
76       *
77       * @param capc
78       * @param propertyModel
79       */
80      public void handlePropertyModel(ContextAwarePropertyContainer capc, PropertyModel propertyModel) {
81          ActionUtil.Scope scope = ActionUtil.stringToScope(propertyModel.getScopeStr());
82  
83          if (checkFileAttributeSanity(propertyModel)) {
84              String file = propertyModel.getFile();
85              file = capc.subst(file);
86              try (FileInputStream istream = new FileInputStream(file)) {
87                  PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope);
88              } catch (FileNotFoundException e) {
89                  addError("Could not find properties file [" + file + "].");
90              } catch (IOException | IllegalArgumentException e1) { // IllegalArgumentException is thrown in case the file
91                  // is badly malformed, i.e a binary.
92                  addError("Could not read properties file [" + file + "].", e1);
93              }
94          } else if (checkResourceAttributeSanity(propertyModel)) {
95              String resource = propertyModel.getResource();
96              resource = capc.subst(resource);
97              URL resourceURL = Loader.getResourceBySelfClassLoader(resource);
98              if (resourceURL == null) {
99                  addError("Could not find resource [" + resource + "].");
100             } else {
101                 try (InputStream istream = resourceURL.openStream();) {
102                     PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope);
103                 } catch (IOException e) {
104                     addError("Could not read resource file [" + resource + "].", e);
105                 }
106             }
107         } else if (checkValueNameAttributesSanity(propertyModel)) {
108             // earlier versions performed Java '\' escapes for '\\' '\t' etc. Howevver, there is no
109             // need to do this. See RegularEscapeUtil.__UNUSED__basicEscape
110             String value = propertyModel.getValue();
111 
112             // now remove both leading and trailing spaces
113             value = value.trim();
114             value = capc.subst(value);
115             ActionUtil.setProperty(capc, propertyModel.getName(), value, scope);
116 
117         } else {
118             addError(ModelConstants.INVALID_ATTRIBUTES);
119         }
120     }
121 
122     public static boolean checkFileAttributeSanity(PropertyModel propertyModel) {
123         String file = propertyModel.getFile();
124         String name = propertyModel.getName();
125         String value = propertyModel.getValue();
126         String resource = propertyModel.getResource();
127 
128         return !(OptionHelper.isNullOrEmptyOrAllSpaces(file)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name)
129                 && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(resource));
130     }
131 
132     public static boolean checkResourceAttributeSanity(PropertyModel propertyModel) {
133         String file = propertyModel.getFile();
134         String name = propertyModel.getName();
135         String value = propertyModel.getValue();
136         String resource = propertyModel.getResource();
137 
138         return !(OptionHelper.isNullOrEmptyOrAllSpaces(resource)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name)
139                 && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(file));
140     }
141 
142     public static boolean checkValueNameAttributesSanity(PropertyModel propertyModel) {
143         String file = propertyModel.getFile();
144         String name = propertyModel.getName();
145         String value = propertyModel.getValue();
146         String resource = propertyModel.getResource();
147         return (!(OptionHelper.isNullOrEmptyOrAllSpaces(name) || OptionHelper.isNullOrEmptyOrAllSpaces(value)) && (
148                 OptionHelper.isNullOrEmptyOrAllSpaces(file) && OptionHelper.isNullOrEmptyOrAllSpaces(resource)));
149     }
150 
151     /**
152      * Add all the properties found in the argument named 'props' to an InterpretationContext.
153      */
154     static public void setProperty(ContextAwarePropertyContainer capc, String key, String value,
155             ActionUtil.Scope scope) {
156         switch (scope) {
157         case LOCAL:
158             capc.addSubstitutionProperty(key, value);
159             break;
160         case CONTEXT:
161             capc.getContext().putProperty(key, value);
162             break;
163         case SYSTEM:
164             OptionHelper.setSystemProperty(capc, key, value);
165         }
166     }
167 
168     /**
169      * Add all the properties found in the argument named 'props' to an InterpretationContext.
170      */
171     static public void setProperties(ContextAwarePropertyContainer capc, Properties props, ActionUtil.Scope scope) {
172         switch (scope) {
173         case LOCAL:
174             capc.addSubstitutionProperties(props);
175             break;
176         case CONTEXT:
177             ContextUtil cu = new ContextUtil(capc.getContext());
178             cu.addProperties(props);
179             break;
180         case SYSTEM:
181             OptionHelper.setSystemProperties(capc, props);
182         }
183     }
184 
185     static public void loadAndSetProperties(ContextAwarePropertyContainer capc, InputStream istream,
186             ActionUtil.Scope scope) throws IOException {
187         Properties props = new Properties();
188         props.load(istream);
189         PropertyModelHandlerHelper.setProperties(capc, props, scope);
190     }
191 }