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 }