View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /**
6    * Licensed to the Apache Software Foundation (ASF) under one
7    * or more contributor license agreements. See the NOTICE file
8    * distributed with this work for additional information
9    * regarding copyright ownership. The ASF licenses this file
10   * to you under the Apache License, Version 2.0 (the
11   * "License"); you may not use this file except in compliance
12   * with the License. You may obtain a copy of the License at
13   *
14   * http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing,
17   * software distributed under the License is distributed on an
18   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19   * KIND, either express or implied. See the License for the
20   * specific language governing permissions and limitations
21   * under the License.
22   */
23  package com.sun.org.apache.xml.internal.security.utils.resolver.implementations;
24  
25  import java.io.ByteArrayOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.net.InetSocketAddress;
29  import java.net.MalformedURLException;
30  import java.net.Proxy;
31  import java.net.URISyntaxException;
32  import java.net.URI;
33  import java.net.URL;
34  import java.net.URLConnection;
35  
36  import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
37  import com.sun.org.apache.xml.internal.security.utils.Base64;
38  import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverContext;
39  import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException;
40  import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi;
41  
42  /**
43   * A simple ResourceResolver for HTTP requests. This class handles only 'pure'
44   * HTTP URIs which means without a fragment. The Fragment handling is done by the
45   * {@link ResolverFragment} class.
46   * <BR>
47   * If the user has a corporate HTTP proxy which is to be used, the usage can be
48   * switched on by setting properties for the resolver:
49   * <PRE>
50   * resourceResolver.setProperty("http.proxy.host", "proxy.company.com");
51   * resourceResolver.setProperty("http.proxy.port", "8080");
52   *
53   * // if we need a password for the proxy
54   * resourceResolver.setProperty("http.proxy.username", "proxyuser3");
55   * resourceResolver.setProperty("http.proxy.password", "secretca");
56   * </PRE>
57   *
58   * @see <A HREF="http://www.javaworld.com/javaworld/javatips/jw-javatip42_p.html">Java Tip 42: Write Java apps that work with proxy-based firewalls</A>
59   * @see <A HREF="http://docs.oracle.com/javase/1.4.2/docs/guide/net/properties.html">SUN J2SE docs for network properties</A>
60   * @see <A HREF="http://metalab.unc.edu/javafaq/javafaq.html#proxy">The JAVA FAQ Question 9.5: How do I make Java work with a proxy server?</A>
61   */
62  public class ResolverDirectHTTP extends ResourceResolverSpi {
63  
64      /** {@link org.apache.commons.logging} logging facility */
65      private static java.util.logging.Logger log =
66          java.util.logging.Logger.getLogger(ResolverDirectHTTP.class.getName());
67  
68      /** Field properties[] */
69      private static final String properties[] = {
70                                                   "http.proxy.host", "http.proxy.port",
71                                                   "http.proxy.username", "http.proxy.password",
72                                                   "http.basic.username", "http.basic.password"
73                                                 };
74  
75      /** Field HttpProxyHost */
76      private static final int HttpProxyHost = 0;
77  
78      /** Field HttpProxyPort */
79      private static final int HttpProxyPort = 1;
80  
81      /** Field HttpProxyUser */
82      private static final int HttpProxyUser = 2;
83  
84      /** Field HttpProxyPass */
85      private static final int HttpProxyPass = 3;
86  
87      /** Field HttpProxyUser */
88      private static final int HttpBasicUser = 4;
89  
90      /** Field HttpProxyPass */
91      private static final int HttpBasicPass = 5;
92  
93      @Override
94      public boolean engineIsThreadSafe() {
95          return true;
96      }
97  
98      /**
99       * Method resolve
100      *
101      * @param uri
102      * @param baseURI
103      *
104      * @throws ResourceResolverException
105      * @return
106      * $todo$ calculate the correct URI from the attribute and the baseURI
107      */
108     @Override
109     public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
110         throws ResourceResolverException {
111         try {
112 
113             // calculate new URI
114             URI uriNew = getNewURI(context.uriToResolve, context.baseUri);
115             URL url = uriNew.toURL();
116             URLConnection urlConnection;
117             urlConnection = openConnection(url);
118 
119             // check if Basic authentication is required
120             String auth = urlConnection.getHeaderField("WWW-Authenticate");
121 
122             if (auth != null && auth.startsWith("Basic")) {
123                 // do http basic authentication
124                 String user =
125                     engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpBasicUser]);
126                 String pass =
127                     engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpBasicPass]);
128 
129                 if ((user != null) && (pass != null)) {
130                     urlConnection = openConnection(url);
131 
132                     String password = user + ":" + pass;
133                     String encodedPassword = Base64.encode(password.getBytes("ISO-8859-1"));
134 
135                     // set authentication property in the http header
136                     urlConnection.setRequestProperty("Authorization",
137                                                      "Basic " + encodedPassword);
138                 }
139             }
140 
141             String mimeType = urlConnection.getHeaderField("Content-Type");
142             InputStream inputStream = urlConnection.getInputStream();
143             ByteArrayOutputStream baos = new ByteArrayOutputStream();
144             byte buf[] = new byte[4096];
145             int read = 0;
146             int summarized = 0;
147 
148             while ((read = inputStream.read(buf)) >= 0) {
149                 baos.write(buf, 0, read);
150                 summarized += read;
151             }
152 
153             if (log.isLoggable(java.util.logging.Level.FINE)) {
154                 log.log(java.util.logging.Level.FINE, "Fetched " + summarized + " bytes from URI " + uriNew.toString());
155             }
156 
157             XMLSignatureInput result = new XMLSignatureInput(baos.toByteArray());
158 
159             result.setSourceURI(uriNew.toString());
160             result.setMIMEType(mimeType);
161 
162             return result;
163         } catch (URISyntaxException ex) {
164             throw new ResourceResolverException("generic.EmptyMessage", ex, context.attr, context.baseUri);
165         } catch (MalformedURLException ex) {
166             throw new ResourceResolverException("generic.EmptyMessage", ex, context.attr, context.baseUri);
167         } catch (IOException ex) {
168             throw new ResourceResolverException("generic.EmptyMessage", ex, context.attr, context.baseUri);
169         } catch (IllegalArgumentException e) {
170             throw new ResourceResolverException("generic.EmptyMessage", e, context.attr, context.baseUri);
171         }
172     }
173 
174     private URLConnection openConnection(URL url) throws IOException {
175 
176         String proxyHostProp =
177                 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyHost]);
178         String proxyPortProp =
179                 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyPort]);
180         String proxyUser =
181                 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyUser]);
182         String proxyPass =
183                 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyPass]);
184 
185         Proxy proxy = null;
186         if ((proxyHostProp != null) && (proxyPortProp != null)) {
187             int port = Integer.parseInt(proxyPortProp);
188             proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHostProp, port));
189         }
190 
191         URLConnection urlConnection;
192         if (proxy != null) {
193             urlConnection = url.openConnection(proxy);
194 
195             if ((proxyUser != null) && (proxyPass != null)) {
196                 String password = proxyUser + ":" + proxyPass;
197                 String authString = "Basic " + Base64.encode(password.getBytes("ISO-8859-1"));
198 
199                 urlConnection.setRequestProperty("Proxy-Authorization", authString);
200             }
201         } else {
202             urlConnection = url.openConnection();
203         }
204 
205         return urlConnection;
206     }
207 
208     /**
209      * We resolve http URIs <I>without</I> fragment...
210      *
211      * @param uri
212      * @param baseURI
213      * @return true if can be resolved
214      */
215     public boolean engineCanResolveURI(ResourceResolverContext context) {
216         if (context.uriToResolve == null) {
217             if (log.isLoggable(java.util.logging.Level.FINE)) {
218                 log.log(java.util.logging.Level.FINE, "quick fail, uri == null");
219             }
220             return false;
221         }
222 
223         if (context.uriToResolve.equals("") || (context.uriToResolve.charAt(0)=='#')) {
224             if (log.isLoggable(java.util.logging.Level.FINE)) {
225                 log.log(java.util.logging.Level.FINE, "quick fail for empty URIs and local ones");
226             }
227             return false;
228         }
229 
230         if (log.isLoggable(java.util.logging.Level.FINE)) {
231             log.log(java.util.logging.Level.FINE, "I was asked whether I can resolve " + context.uriToResolve);
232         }
233 
234         if (context.uriToResolve.startsWith("http:") ||
235             (context.baseUri != null && context.baseUri.startsWith("http:") )) {
236             if (log.isLoggable(java.util.logging.Level.FINE)) {
237                 log.log(java.util.logging.Level.FINE, "I state that I can resolve " + context.uriToResolve);
238             }
239             return true;
240         }
241 
242         if (log.isLoggable(java.util.logging.Level.FINE)) {
243             log.log(java.util.logging.Level.FINE, "I state that I can't resolve " + context.uriToResolve);
244         }
245 
246         return false;
247     }
248 
249     /**
250      * @inheritDoc
251      */
252     public String[] engineGetPropertyKeys() {
253         return ResolverDirectHTTP.properties.clone();
254     }
255 
256     private static URI getNewURI(String uri, String baseURI) throws URISyntaxException {
257         URI newUri = null;
258         if (baseURI == null || "".equals(baseURI)) {
259             newUri = new URI(uri);
260         } else {
261             newUri = new URI(baseURI).resolve(uri);
262         }
263 
264         // if the URI contains a fragment, ignore it
265         if (newUri.getFragment() != null) {
266             URI uriNewNoFrag =
267                 new URI(newUri.getScheme(), newUri.getSchemeSpecificPart(), null);
268             return uriNewNoFrag;
269         }
270         return newUri;
271     }
272 
273 }