001package org.eclipse.aether.util.repository;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.net.Authenticator;
023import java.net.InetSocketAddress;
024import java.net.PasswordAuthentication;
025import java.net.SocketAddress;
026import java.net.URI;
027import java.net.URL;
028import java.util.List;
029import java.util.Map;
030import java.util.UUID;
031
032import org.eclipse.aether.repository.Authentication;
033import org.eclipse.aether.repository.AuthenticationContext;
034import org.eclipse.aether.repository.AuthenticationDigest;
035import org.eclipse.aether.repository.Proxy;
036import org.eclipse.aether.repository.ProxySelector;
037import org.eclipse.aether.repository.RemoteRepository;
038
039/**
040 * A proxy selector that uses the {@link java.net.ProxySelector#getDefault() JRE's global proxy selector}. In
041 * combination with the system property {@code java.net.useSystemProxies}, this proxy selector can be employed to pick
042 * up the proxy configuration from the operating system, see <a
043 * href="http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html">Java Networking and Proxies</a> for
044 * details. The {@link java.net.Authenticator JRE's global authenticator} is used to look up credentials for a proxy
045 * when needed.
046 */
047public final class JreProxySelector
048    implements ProxySelector
049{
050
051    /**
052     * Creates a new proxy selector that delegates to {@link java.net.ProxySelector#getDefault()}.
053     */
054    public JreProxySelector()
055    {
056    }
057
058    public Proxy getProxy( RemoteRepository repository )
059    {
060        List<java.net.Proxy> proxies = null;
061        try
062        {
063            URI uri = new URI( repository.getUrl() ).parseServerAuthority();
064            proxies = java.net.ProxySelector.getDefault().select( uri );
065        }
066        catch ( Exception e )
067        {
068            // URL invalid or not accepted by selector or no selector at all, simply use no proxy
069        }
070        if ( proxies != null )
071        {
072            for ( java.net.Proxy proxy : proxies )
073            {
074                if ( java.net.Proxy.Type.DIRECT.equals( proxy.type() ) )
075                {
076                    break;
077                }
078                if ( java.net.Proxy.Type.HTTP.equals( proxy.type() ) && isValid( proxy.address() ) )
079                {
080                    InetSocketAddress addr = (InetSocketAddress) proxy.address();
081                    return new Proxy( Proxy.TYPE_HTTP, addr.getHostName(), addr.getPort(),
082                                      JreProxyAuthentication.INSTANCE );
083                }
084            }
085        }
086        return null;
087    }
088
089    private static boolean isValid( SocketAddress address )
090    {
091        if ( address instanceof InetSocketAddress )
092        {
093            /*
094             * NOTE: On some platforms with java.net.useSystemProxies=true, unconfigured proxies show up as proxy
095             * objects with empty host and port 0.
096             */
097            InetSocketAddress addr = (InetSocketAddress) address;
098            if ( addr.getPort() <= 0 )
099            {
100                return false;
101            }
102            if ( addr.getHostName() == null || addr.getHostName().length() <= 0 )
103            {
104                return false;
105            }
106            return true;
107        }
108        return false;
109    }
110
111    private static final class JreProxyAuthentication
112        implements Authentication
113    {
114
115        public static final Authentication INSTANCE = new JreProxyAuthentication();
116
117        public void fill( AuthenticationContext context, String key, Map<String, String> data )
118        {
119            Proxy proxy = context.getProxy();
120            if ( proxy == null )
121            {
122                return;
123            }
124            if ( !AuthenticationContext.USERNAME.equals( key ) && !AuthenticationContext.PASSWORD.equals( key ) )
125            {
126                return;
127            }
128
129            try
130            {
131                URL url;
132                try
133                {
134                    url = new URL( context.getRepository().getUrl() );
135                }
136                catch ( Exception e )
137                {
138                    url = null;
139                }
140
141                PasswordAuthentication auth =
142                    Authenticator.requestPasswordAuthentication( proxy.getHost(), null, proxy.getPort(), "http",
143                                                                 "Credentials for proxy " + proxy, null, url,
144                                                                 Authenticator.RequestorType.PROXY );
145                if ( auth != null )
146                {
147                    context.put( AuthenticationContext.USERNAME, auth.getUserName() );
148                    context.put( AuthenticationContext.PASSWORD, auth.getPassword() );
149                }
150                else
151                {
152                    context.put( AuthenticationContext.USERNAME, System.getProperty( "http.proxyUser" ) );
153                    context.put( AuthenticationContext.PASSWORD, System.getProperty( "http.proxyPassword" ) );
154                }
155            }
156            catch ( SecurityException e )
157            {
158                // oh well, let's hope the proxy can do without auth
159            }
160        }
161
162        public void digest( AuthenticationDigest digest )
163        {
164            // we don't know anything about the JRE's current authenticator, assume the worst (i.e. interactive)
165            digest.update( UUID.randomUUID().toString() );
166        }
167
168        @Override
169        public boolean equals( Object obj )
170        {
171            return this == obj || ( obj != null && getClass().equals( obj.getClass() ) );
172        }
173
174        @Override
175        public int hashCode()
176        {
177            return getClass().hashCode();
178        }
179
180    }
181
182}