1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.joran.event;
15
16 import static ch.qos.logback.core.CoreConstants.XML_PARSING;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import javax.xml.parsers.ParserConfigurationException;
25 import javax.xml.parsers.SAXParser;
26 import javax.xml.parsers.SAXParserFactory;
27
28 import org.xml.sax.Attributes;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.Locator;
31 import org.xml.sax.SAXException;
32 import org.xml.sax.SAXParseException;
33 import org.xml.sax.helpers.DefaultHandler;
34
35 import ch.qos.logback.core.Context;
36 import ch.qos.logback.core.joran.spi.ElementPath;
37 import ch.qos.logback.core.joran.spi.JoranException;
38 import ch.qos.logback.core.spi.ContextAware;
39 import ch.qos.logback.core.spi.ContextAwareImpl;
40 import ch.qos.logback.core.status.Status;
41
42 public class SaxEventRecorder extends DefaultHandler implements ContextAware {
43
44
45 final ContextAwareImpl contextAwareImpl;
46 final ElementPath elementPath;
47 List<SaxEvent> saxEventList = new ArrayList<SaxEvent>();
48 Locator locator;
49
50
51 public SaxEventRecorder(Context context) {
52 this(context, new ElementPath());
53 }
54
55
56 public SaxEventRecorder(Context context, ElementPath elementPath) {
57 contextAwareImpl = new ContextAwareImpl(context, this);
58 this.elementPath = elementPath;
59 }
60
61
62
63
64
65
66
67
68
69
70
71
72
73 @Override
74 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
75 addWarn("Document Type Declaration (DOCTYPE) with external file reference is");
76 addWarn("disallowed to prevent Server-Side Request Forgery (SSRF) attacks.");
77 addWarn("returning contents of SYSTEM " +systemId+ " as a white space");
78 return new InputSource(new ByteArrayInputStream(" ".getBytes()));
79 }
80
81 final public void recordEvents(InputStream inputStream) throws JoranException {
82 recordEvents(new InputSource(inputStream));
83 }
84
85 public void recordEvents(InputSource inputSource) throws JoranException {
86 SAXParser saxParser = buildSaxParser();
87 try {
88
89
90
91
92 saxParser.parse(inputSource, this);
93
94 return;
95 } catch (IOException ie) {
96 handleError("I/O error occurred while parsing xml file", ie);
97 } catch (SAXException se) {
98
99 throw new JoranException("Problem parsing XML document. See previously reported errors.", se);
100 } catch (Exception ex) {
101 handleError("Unexpected exception while parsing XML document.", ex);
102 }
103 throw new IllegalStateException("This point can never be reached");
104 }
105
106 private void handleError(String errMsg, Throwable t) throws JoranException {
107 addError(errMsg, t);
108 throw new JoranException(errMsg, t);
109 }
110
111 private SAXParser buildSaxParser() throws JoranException {
112 try {
113 SAXParserFactory spf = SAXParserFactory.newInstance();
114 spf.setValidating(false);
115
116
117 spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
118 spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
119 spf.setNamespaceAware(true);
120 return spf.newSAXParser();
121 } catch (ParserConfigurationException pce) {
122 String errMsg = "Error during SAX paser configuration. See https://logback.qos.ch/codes.html#saxParserConfiguration";
123 addError(errMsg, pce);
124 throw new JoranException(errMsg, pce);
125 } catch (SAXException pce) {
126 String errMsg = "Error during parser creation or parser configuration";
127 addError(errMsg, pce);
128 throw new JoranException(errMsg, pce);
129 }
130 }
131
132 public void startDocument() {
133 }
134
135 public Locator getLocator() {
136 return locator;
137 }
138
139 public void setDocumentLocator(Locator l) {
140 locator = l;
141 }
142
143 protected boolean shouldIgnoreForElementPath(String tagName) {
144 return false;
145 }
146
147 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
148 String tagName = getTagName(localName, qName);
149 if (!shouldIgnoreForElementPath(tagName)) {
150 elementPath.push(tagName);
151 }
152 ElementPath current = elementPath.duplicate();
153 saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, getLocator()));
154 }
155
156 public void characters(char[] ch, int start, int length) {
157 String bodyStr = new String(ch, start, length);
158 SaxEvent lastEvent = getLastEvent();
159 if (lastEvent instanceof BodyEvent) {
160 BodyEvent be = (BodyEvent) lastEvent;
161 be.append(bodyStr);
162 } else {
163
164 if (!isSpaceOnly(bodyStr)) {
165 saxEventList.add(new BodyEvent(bodyStr, getLocator()));
166 }
167 }
168 }
169
170 boolean isSpaceOnly(String bodyStr) {
171 String bodyTrimmed = bodyStr.trim();
172 return (bodyTrimmed.length() == 0);
173 }
174
175 SaxEvent getLastEvent() {
176 if (saxEventList.isEmpty()) {
177 return null;
178 }
179 int size = saxEventList.size();
180 return saxEventList.get(size - 1);
181 }
182
183 public void endElement(String namespaceURI, String localName, String qName) {
184 saxEventList.add(new EndEvent(namespaceURI, localName, qName, getLocator()));
185 String tagName = getTagName(localName, qName);
186 if (!shouldIgnoreForElementPath(tagName)) {
187 elementPath.pop();
188 }
189 }
190
191 String getTagName(String localName, String qName) {
192 String tagName = localName;
193 if ((tagName == null) || (tagName.length() < 1)) {
194 tagName = qName;
195 }
196 return tagName;
197 }
198
199 public void error(SAXParseException spe) throws SAXException {
200 addError(XML_PARSING + " - Parsing error on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber());
201 addError(spe.toString());
202 }
203
204 public void fatalError(SAXParseException spe) throws SAXException {
205 addError(XML_PARSING + " - Parsing fatal error on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber());
206 addError(spe.toString());
207 }
208
209 public void warning(SAXParseException spe) throws SAXException {
210 addWarn(XML_PARSING + " - Parsing warning on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber(), spe);
211 }
212
213 public void addError(String msg) {
214 contextAwareImpl.addError(msg);
215 }
216
217 public void addError(String msg, Throwable ex) {
218 contextAwareImpl.addError(msg, ex);
219 }
220
221 public void addInfo(String msg) {
222 contextAwareImpl.addInfo(msg);
223 }
224
225 public void addInfo(String msg, Throwable ex) {
226 contextAwareImpl.addInfo(msg, ex);
227 }
228
229 public void addStatus(Status status) {
230 contextAwareImpl.addStatus(status);
231 }
232
233 public void addWarn(String msg) {
234 contextAwareImpl.addWarn(msg);
235 }
236
237 public void addWarn(String msg, Throwable ex) {
238 contextAwareImpl.addWarn(msg, ex);
239 }
240
241 public Context getContext() {
242 return contextAwareImpl.getContext();
243 }
244
245 public void setContext(Context context) {
246 contextAwareImpl.setContext(context);
247 }
248
249 public List<SaxEvent> getSaxEventList() {
250 return saxEventList;
251 }
252
253 }