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
039import static java.util.Objects.requireNonNull;
040
041/**
042 * A proxy selector that uses the {@link java.net.ProxySelector#getDefault() JRE's global proxy selector}. In
043 * combination with the system property {@code java.net.useSystemProxies}, this proxy selector can be employed to pick
044 * up the proxy configuration from the operating system, see <a
045 * href="http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html">Java Networking and Proxies</a> for
046 * details. The {@link java.net.Authenticator JRE's global authenticator} is used to look up credentials for a proxy
047 * when needed.
048 */
049public final class JreProxySelector
050    implements ProxySelector
051{
052
053    /**
054     * Creates a new proxy selector that delegates to {@link java.net.ProxySelector#getDefault()}.
055     */
056    public JreProxySelector()
057    {
058    }
059
060    public Proxy getProxy( RemoteRepository repository )
061    {
062        requireNonNull( repository, "repository cannot be null" );
063        List<java.net.Proxy> proxies = null;
064        try
065        {
066            URI uri = new URI( repository.getUrl() ).parseServerAuthority();
067            proxies = java.net.ProxySelector.getDefault().select( uri );
068        }
069        catch ( Exception e )
070        {
071            // URL invalid or not accepted by selector or no selector at all, simply use no proxy
072        }
073        if ( proxies != null )
074        {
075            for ( java.net.Proxy proxy : proxies )
076            {
077                if ( java.net.Proxy.Type.DIRECT.equals( proxy.type() ) )
078                {
079                    break;
080                }
081                if ( java.net.Proxy.Type.HTTP.equals( proxy.type() ) && isValid( proxy.address() ) )
082                {
083                    InetSocketAddress addr = (InetSocketAddress) proxy.address();
084                    return new Proxy( Proxy.TYPE_HTTP, addr.getHostName(), addr.getPort(),
085                                      JreProxyAuthentication.INSTANCE );
086                }
087            }
088        }
089        return null;
090    }
091
092    private static boolean isValid( SocketAddress address )
093    {
094        if ( address instanceof InetSocketAddress )
095        {
096            /*
097             * NOTE: On some platforms with java.net.useSystemProxies=true, unconfigured proxies show up as proxy
098             * objects with empty host and port 0.
099             */
100            InetSocketAddress addr = (InetSocketAddress) address;
101            if ( addr.getPort() <= 0 )
102            {
103                return false;
104            }
105            if ( addr.getHostName() == null || addr.getHostName().isEmpty() )
106            {
107                return false;
108            }
109            return true;
110        }
111        return false;
112    }
113
114    private static final class JreProxyAuthentication
115        implements Authentication
116    {
117
118        public static final Authentication INSTANCE = new JreProxyAuthentication();
119
120        public void fill( AuthenticationContext context, String key, Map<String, String> data )
121        {
122            requireNonNull( context, "digest cannot be null" );
123            Proxy proxy = context.getProxy();
124            if ( proxy == null )
125            {
126                return;
127            }
128            if ( !AuthenticationContext.USERNAME.equals( key ) && !AuthenticationContext.PASSWORD.equals( key ) )
129            {
130                return;
131            }
132
133            try
134            {
135                URL url;
136                try
137                {
138                    url = new URL( context.getRepository().getUrl() );
139                }
140                catch ( Exception e )
141                {
142                    url = null;
143                }
144
145                PasswordAuthentication auth =
146                    Authenticator.requestPasswordAuthentication( proxy.getHost(), null, proxy.getPort(), "http",
147                                                                 "Credentials for proxy " + proxy, null, url,
148                                                                 Authenticator.RequestorType.PROXY );
149                if ( auth != null )
150                {
151                    context.put( AuthenticationContext.USERNAME, auth.getUserName() );
152                    context.put( AuthenticationContext.PASSWORD, auth.getPassword() );
153                }
154                else
155                {
156                    context.put( AuthenticationContext.USERNAME, System.getProperty( "http.proxyUser" ) );
157                    context.put( AuthenticationContext.PASSWORD, System.getProperty( "http.proxyPassword" ) );
158                }
159            }
160            catch ( SecurityException e )
161            {
162                // oh well, let's hope the proxy can do without auth
163            }
164        }
165
166        public void digest( AuthenticationDigest digest )
167        {
168            requireNonNull( digest, "digest cannot be null" );
169            // we don't know anything about the JRE's current authenticator, assume the worst (i.e. interactive)
170            digest.update( UUID.randomUUID().toString() );
171        }
172
173        @Override
174        public boolean equals( Object obj )
175        {
176            return this == obj || ( obj != null && getClass().equals( obj.getClass() ) );
177        }
178
179        @Override
180        public int hashCode()
181        {
182            return getClass().hashCode();
183        }
184
185    }
186
187}