KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > frontend > ExternalIncludeTransformer


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.frontend;
17
18 import org.apache.cocoon.transformation.AbstractTransformer;
19 import org.apache.cocoon.environment.SourceResolver;
20 import org.apache.cocoon.environment.Request;
21 import org.apache.cocoon.environment.ObjectModelHelper;
22 import org.apache.cocoon.ProcessingException;
23 import org.apache.cocoon.util.HashMap;
24 import org.apache.cocoon.matching.helpers.WildcardHelper;
25 import org.apache.cocoon.xml.IncludeXMLConsumer;
26 import org.apache.cocoon.xml.SaxBuffer;
27 import org.apache.cocoon.components.source.SourceUtil;
28 import org.apache.avalon.framework.parameters.Parameters;
29 import org.apache.avalon.framework.service.Serviceable;
30 import org.apache.avalon.framework.service.ServiceManager;
31 import org.apache.avalon.framework.service.ServiceException;
32 import org.apache.avalon.framework.context.Contextualizable;
33 import org.apache.avalon.framework.context.Context;
34 import org.apache.avalon.framework.context.ContextException;
35 import org.apache.excalibur.source.Source;
36 import org.apache.excalibur.xml.sax.SAXParser;
37 import org.apache.excalibur.store.Store;
38 import org.apache.commons.lang.exception.ExceptionUtils;
39 import org.xml.sax.SAXException JavaDoc;
40 import org.xml.sax.Attributes JavaDoc;
41 import org.xml.sax.helpers.AttributesImpl JavaDoc;
42 import org.xml.sax.helpers.DefaultHandler JavaDoc;
43 import org.outerj.daisy.frontend.util.CacheHelper;
44 import org.outerj.daisy.frontend.util.WikiDataDirHelper;
45
46 import java.util.Map JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.regex.Pattern JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.File JavaDoc;
52 import java.net.URI JavaDoc;
53 import java.net.InetAddress JavaDoc;
54 import java.net.UnknownHostException JavaDoc;
55 import java.net.URISyntaxException JavaDoc;
56
57 /**
58  * Transformer to handle includes of non-"daisy:" URIs. The normal Cocoon include
59  * transformers were not useable due to the way they handle errors (mostly not).
60  */

61 public class ExternalIncludeTransformer extends AbstractTransformer implements Serviceable, Contextualizable {
62     private static final String JavaDoc NAMESPACE = "http://outerx.org/daisy/1.0#externalinclude";
63     private Context context;
64     private ServiceManager serviceManager;
65     private SourceResolver sourceResolver;
66     private Request request;
67     private PermissionRule[] rules;
68
69     public void setup(SourceResolver sourceResolver, Map objectModel, String JavaDoc s, Parameters parameters) throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
70         this.sourceResolver = sourceResolver;
71         this.request = ObjectModelHelper.getRequest(objectModel);
72         this.rules = null;
73     }
74
75     public void contextualize(Context context) throws ContextException {
76         this.context = context;
77     }
78
79     public void service(ServiceManager serviceManager) throws ServiceException {
80         this.serviceManager = serviceManager;
81     }
82
83     public void recycle() {
84         this.rules = null;
85         this.request = null;
86         this.sourceResolver = null;
87     }
88
89     public void startElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
90         if (namespaceURI.equals(NAMESPACE) && localName.equals("include")) {
91             String JavaDoc src = attributes.getValue("src");
92             src = src.trim();
93             if (src == null || src.equals("")) {
94                 throw new SAXException JavaDoc("Missing src attribute on " + qName + " element.");
95             }
96
97             if (src.startsWith("cocoon:/") && !src.startsWith("cocoon://")) {
98                 // a relative cocoon include, make sure it gets resolved against the 'daisy root sitemap',
99
// otherwise processing these includes from e.g. extension sitemaps might give unexpected
100
// results
101
String JavaDoc daisyCocoonPath = WikiHelper.getDaisyCocoonPath(request);
102                 src = "cocoon:/" + daisyCocoonPath + src.substring("cocoon:".length());
103             }
104
105             Source source = null;
106             try {
107                 boolean canRead;
108                 if (src.startsWith("cocoon:")) {
109                     // for cocoon sources, calling getURI causes already the building of the pipeline which might
110
// have unwanted side effects
111
canRead = canRead("cocoon", src);
112                 } else {
113                     source = sourceResolver.resolveURI(src);
114                     canRead = canRead(source.getScheme(), source.getURI());
115                 }
116
117                 if (!canRead) {
118                     throw new Exception JavaDoc("Inclusion of this URL is not allowed.");
119                 }
120
121                 if (source == null)
122                     source = sourceResolver.resolveURI(src);
123                 SaxBuffer buffer = new SaxBuffer();
124                 SourceUtil.toSAX(source, buffer);
125                 buffer.toSAX(new IncludeXMLConsumer(xmlConsumer));
126             } catch (Throwable JavaDoc e) {
127                 getLogger().error("Error processing include of " + src, e);
128                 AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
129                 attrs.addAttribute("", "class", "class", "CDATA", "daisy-error");
130                 contentHandler.startElement("", "p", "p", attrs);
131                 StringBuffer JavaDoc message = new StringBuffer JavaDoc("Error processing inclusion of ");
132                 message.append(src).append(" : ").append(e.getMessage());
133                 Throwable JavaDoc cause = e;
134                 while ((cause = ExceptionUtils.getCause(cause)) != null) {
135                     message.append(", cause: ").append(cause.getMessage());
136                 }
137                 contentHandler.characters(message.toString().toCharArray(), 0, message.length());
138                 contentHandler.endElement("", "p", "p");
139             }
140         } else {
141             super.startElement(namespaceURI, localName, qName, attributes);
142         }
143     }
144
145     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qName) throws SAXException JavaDoc {
146         if (namespaceURI.equals(NAMESPACE) && localName.equals("include")) {
147             // ignore
148
} else {
149             super.endElement(namespaceURI, localName, qName);
150         }
151     }
152
153     private boolean canRead(String JavaDoc scheme, String JavaDoc uri) throws Exception JavaDoc {
154         PermissionRule[] rules = getPermissionRules();
155         PermissionCheckContext context = new PermissionCheckContext(scheme, uri);
156         AccessOutcome outcome = new AccessOutcome();
157         for (int i = 0; i < rules.length; i++) {
158             try {
159                 rules[i].evaluate(context, outcome);
160             } catch (Throwable JavaDoc e) {
161                 throw new Exception JavaDoc("Error evaluating external include permission rules.", e);
162             }
163         }
164         return outcome.getPermission() == Permission.GRANT;
165     }
166
167     static class PermissionCheckContext {
168         private String JavaDoc scheme;
169         private String JavaDoc uriString;
170         private String JavaDoc ip;
171         private URI JavaDoc uri;
172
173         public PermissionCheckContext(String JavaDoc scheme, String JavaDoc uriString) {
174             this.scheme = scheme;
175             this.uriString = uriString;
176         }
177
178         public String JavaDoc getScheme() {
179             return scheme;
180         }
181
182         public String JavaDoc getUriString() {
183             return uriString;
184         }
185
186         public String JavaDoc getIp() throws UnknownHostException JavaDoc, URISyntaxException JavaDoc {
187             if (ip == null) {
188                 URI JavaDoc uri = getUri();
189                 String JavaDoc host = uri.getHost();
190                 InetAddress JavaDoc address = InetAddress.getByName(host);
191                 ip = address.getHostAddress();
192             }
193             return ip;
194         }
195
196         public URI JavaDoc getUri() throws URISyntaxException JavaDoc {
197             if (uri == null) {
198                 uri = new URI JavaDoc(uriString);
199             }
200             return uri;
201         }
202     }
203
204     static class Permission {
205         String JavaDoc name;
206
207         private Permission(String JavaDoc name) {
208             this.name = name;
209         }
210
211         public String JavaDoc toString() {
212             return name;
213         }
214
215         static final Permission GRANT = new Permission("grant");
216         static final Permission DENY = new Permission("deny");
217     }
218
219     static class AccessOutcome {
220         Permission permission = Permission.DENY;
221
222         public Permission getPermission() {
223             return permission;
224         }
225
226         public void setPermission(Permission permission) {
227             this.permission = permission;
228         }
229     }
230
231     static class PermissionRule {
232         private final String JavaDoc scheme;
233         private final ValueType valueType;
234         private final Matcher matcher;
235         private final Permission permission;
236         private final int portFrom;
237         private final int portTo;
238
239         public PermissionRule(String JavaDoc scheme, ValueType valueType, Matcher matcher, Permission permission, int portFrom, int portTo) {
240             this.scheme = scheme;
241             this.valueType = valueType;
242             this.matcher = matcher;
243             this.permission = permission;
244             this.portFrom = portFrom;
245             this.portTo = portTo;
246             if ((portFrom != -1 || portTo != -1) && !scheme.equals("http"))
247                 throw new IllegalArgumentException JavaDoc("portFrom/portTo can only be used with the http scheme.");
248         }
249
250         public void evaluate(PermissionCheckContext context, AccessOutcome outcome) throws Exception JavaDoc {
251             if (!scheme.equalsIgnoreCase(context.getScheme()))
252                 return;
253
254             if (valueType != null) {
255                 if (matcher.matches(valueType.getValue(context), context.getUri())) {
256                     if (portFrom != -1 || portTo != -1) {
257                         int port = context.getUri().getPort();
258                         if (port == -1)
259                             port = 80;
260                         if ((portFrom != -1 && port < portFrom) || (portTo != -1 && port > portTo)) {
261                             return;
262                         }
263                     }
264                     outcome.setPermission(permission);
265                 }
266             } else {
267                 outcome.setPermission(permission);
268             }
269         }
270     }
271
272     static interface ValueType {
273         String JavaDoc getValue(PermissionCheckContext context) throws Exception JavaDoc;
274     }
275
276     static interface Matcher {
277         boolean matches(String JavaDoc value, URI JavaDoc uri) throws Exception JavaDoc;
278     }
279
280     static class StringMatcher implements Matcher {
281         private final String JavaDoc matchValue;
282
283         public StringMatcher(String JavaDoc matchValue) {
284             this.matchValue = matchValue;
285         }
286
287         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
288             return matchValue.equals(value);
289         }
290     }
291
292     static class StringIgnoreCaseMatcher implements Matcher {
293         private final String JavaDoc matchValue;
294
295         public StringIgnoreCaseMatcher(String JavaDoc matchValue) {
296             this.matchValue = matchValue;
297         }
298
299         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
300             return matchValue.equalsIgnoreCase(value);
301         }
302     }
303
304     static class WildcardMatcher implements Matcher {
305         private final int[] expression;
306
307         public WildcardMatcher(String JavaDoc matchValue) {
308             this.expression = WildcardHelper.compilePattern(matchValue);
309         }
310
311         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
312             return WildcardHelper.match(new HashMap(), value, expression);
313         }
314     }
315
316     static class WildcardIgnoreCaseMatcher implements Matcher {
317         private final int[] expression;
318
319         public WildcardIgnoreCaseMatcher(String JavaDoc matchValue) {
320             this.expression = WildcardHelper.compilePattern(matchValue.toLowerCase());
321         }
322
323         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
324             return WildcardHelper.match(new HashMap(), value.toLowerCase(), expression);
325         }
326     }
327
328     static class RegExpMatcher implements Matcher {
329         private final Pattern JavaDoc pattern;
330
331         public RegExpMatcher(String JavaDoc matchValue) {
332             this.pattern = Pattern.compile(matchValue);
333         }
334
335         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
336             java.util.regex.Matcher JavaDoc matcher = pattern.matcher(value);
337             return matcher.matches();
338         }
339     }
340
341     static class RegExpIgnoreCaseMatcher implements Matcher {
342         private final Pattern JavaDoc pattern;
343
344         public RegExpIgnoreCaseMatcher(String JavaDoc matchValue) {
345             this.pattern = Pattern.compile(matchValue, Pattern.CASE_INSENSITIVE);
346         }
347
348         public boolean matches(String JavaDoc value, URI JavaDoc uri) {
349             java.util.regex.Matcher JavaDoc matcher = pattern.matcher(value);
350             return matcher.matches();
351         }
352     }
353
354     static class SubdirMatcher implements Matcher {
355         private final String JavaDoc matchValue;
356
357         public SubdirMatcher(String JavaDoc matchValue) throws IOException JavaDoc {
358             this.matchValue = new File JavaDoc(matchValue).getCanonicalPath();
359         }
360
361         public boolean matches(String JavaDoc value, URI JavaDoc uri) throws Exception JavaDoc {
362             File JavaDoc file = new File JavaDoc(uri);
363             return file.getCanonicalPath().startsWith(matchValue);
364         }
365     }
366
367     static class UriValueType implements ValueType {
368         public String JavaDoc getValue(PermissionCheckContext context) {
369             return context.getUriString();
370         }
371     }
372     private static final UriValueType URI_VALUE_TYPE = new UriValueType();
373
374     static class HostValueType implements ValueType {
375         public String JavaDoc getValue(PermissionCheckContext context) throws Exception JavaDoc {
376             return context.getUri().getHost();
377         }
378     }
379     private static final HostValueType HOST_VALUE_TYPE = new HostValueType();
380
381     static class IpAddressValueType implements ValueType {
382         public String JavaDoc getValue(PermissionCheckContext context) throws Exception JavaDoc {
383             return context.getIp();
384         }
385     }
386     private static final IpAddressValueType IP_ADDRESS_VALUE_TYPE = new IpAddressValueType();
387
388     private PermissionRule[] getPermissionRules() throws Exception JavaDoc {
389         loadRules();
390         return this.rules;
391     }
392
393     private void loadRules() throws Exception JavaDoc {
394         if (this.rules != null)
395             return;
396         
397         String JavaDoc src = WikiDataDirHelper.getWikiDataDir(context) + "/external-include-rules.xml";
398         Source source = null;
399         SAXParser parser = null;
400         Store store = null;
401         try {
402             source = sourceResolver.resolveURI(src);
403             store = (Store)serviceManager.lookup(Store.TRANSIENT_STORE);
404             this.rules = (PermissionRule[]) CacheHelper.getFromCache(store, source, this.getClass().getName());
405             if (this.rules == null) {
406                 if (!source.exists())
407                     throw new Exception JavaDoc("No external-include-rules.xml file found.");
408
409                 parser = (SAXParser)serviceManager.lookup(SAXParser.ROLE);
410                 PermissionRuleBuilder builder = new PermissionRuleBuilder();
411                 parser.parse(SourceUtil.getInputSource(source), builder);
412                 this.rules = builder.getRules();
413                 CacheHelper.setInCache(store, rules, source, this.getClass().getName());
414             }
415         } finally {
416             if (source != null)
417                 sourceResolver.release(source);
418             if (parser != null)
419                 serviceManager.release(parser);
420             if (store != null)
421                 serviceManager.release(store);
422         }
423     }
424
425     static class PermissionRuleBuilder extends DefaultHandler JavaDoc {
426         private List JavaDoc rules = new ArrayList JavaDoc();
427
428         public PermissionRule[] getRules() {
429             return (PermissionRule[])rules.toArray(new PermissionRule[rules.size()]);
430         }
431
432         public void startElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
433             if (uri.equals("") && localName.equals("rule")) {
434                 try {
435                     String JavaDoc scheme = attributes.getValue("scheme");
436                     if (scheme == null || scheme.trim().length() == 0)
437                         throw new SAXException JavaDoc("Missing or empty scheme attribute.");
438
439                     String JavaDoc permission = attributes.getValue("permission");
440                     if (permission == null || permission.trim().length() == 0)
441                         throw new SAXException JavaDoc("Missing or empty permission attribute.");
442                     Permission perm;
443                     if (permission.equalsIgnoreCase("grant"))
444                         perm = Permission.GRANT;
445                     else if (permission.equalsIgnoreCase("deny"))
446                         perm = Permission.DENY;
447                     else
448                         throw new SAXException JavaDoc("Invalid value for permission attribute: " + permission);
449
450                     PermissionRule rule;
451                     String JavaDoc value = attributes.getValue("value");
452                     if (value == null || value.trim().length() == 0) {
453                         rule = new PermissionRule(scheme, null, null, perm, -1, -1);
454                     } else {
455                         ValueType valueType;
456                         if (value.equalsIgnoreCase("uri"))
457                             valueType = URI_VALUE_TYPE;
458                         else if (value.equalsIgnoreCase("host"))
459                             valueType = HOST_VALUE_TYPE;
460                         else if (value.equalsIgnoreCase("ip"))
461                             valueType = IP_ADDRESS_VALUE_TYPE;
462                         else
463                             throw new SAXException JavaDoc("Invalid value for 'value' attribute: " + value);
464
465                         String JavaDoc matchValue = attributes.getValue("matchValue");
466                         if (matchValue == null || matchValue.trim().length() == 0)
467                             throw new SAXException JavaDoc("Missing or empty matchValue attribute.");
468
469                         String JavaDoc matchType = attributes.getValue("matchType");
470                         if (matchType == null || matchType.trim().length() == 0)
471                             throw new SAXException JavaDoc("Missing or empty matchType attribute.");
472                         Matcher matcher;
473                         if (matchType.equalsIgnoreCase("string"))
474                             matcher = new StringMatcher(matchValue);
475                         else if (matchType.equalsIgnoreCase("stringIgnoreCase"))
476                             matcher = new StringIgnoreCaseMatcher(matchValue);
477                         else if (matchType.equalsIgnoreCase("wildcard"))
478                             matcher = new WildcardMatcher(matchValue);
479                         else if (matchType.equalsIgnoreCase("wildcardIgnoreCase"))
480                             matcher = new WildcardIgnoreCaseMatcher(matchValue);
481                         else if (matchType.equalsIgnoreCase("regexp"))
482                             matcher = new RegExpMatcher(matchValue);
483                         else if (matchType.equalsIgnoreCase("regexpIgnoreCase"))
484                             matcher = new RegExpIgnoreCaseMatcher(matchValue);
485                         else if (matchType.equalsIgnoreCase("subdir"))
486                             matcher = new SubdirMatcher(matchValue);
487                         else
488                             throw new SAXException JavaDoc("Invalid value for matchType attribute: " + matchType);
489
490                         String JavaDoc portFromString = attributes.getValue("portFrom");
491                         int portFrom = -1;
492                         if (portFromString != null)
493                             portFrom = Integer.parseInt(portFromString);
494
495                         String JavaDoc portToString = attributes.getValue("portTo");
496                         int portTo = -1;
497                         if (portToString != null)
498                             portTo = Integer.parseInt(portToString);
499
500
501                         rule = new PermissionRule(scheme, valueType, matcher, perm, portFrom, portTo);
502                     }
503                     rules.add(rule);
504                 } catch (Throwable JavaDoc e) {
505                     if (e instanceof Exception JavaDoc)
506                         throw new SAXException JavaDoc("Error processing a rule.", (Exception JavaDoc)e);
507                     else
508                         throw new SAXException JavaDoc("Error processing a rule: " + e.getMessage());
509                 }
510             }
511         }
512     }
513 }
514
Popular Tags