From 046a11e967e31e1be83b72625d40193e9728699a Mon Sep 17 00:00:00 2001
From: hboutemy@apache.org
Date: Sat, 13 Mar 2021 18:40:48 +0100
Subject: [PATCH] [MNG-7116] add support for mirrorOf external:http:*
         [PATCH] [MNG-7117] add support for blocked mirror
         [PATCH] [MNG-7118] block HTTP repositories by default

---
 .../repository/DefaultMirrorSelector.java     | 49 +++++++++++++++++--
 .../maven/bridge/MavenRepositorySystem.java   | 48 ++++++++++++++++--
 ...DefaultRepositorySystemSessionFactory.java |  4 +-
 maven-settings/pom.xml                        |  2 +-
 maven-settings/src/main/mdo/settings.mdo      | 17 ++++++-
 5 files changed, 106 insertions(+), 14 deletions(-)

diff --git a/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java b/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
index 6fa2c55..9ad4f47 100644
--- a/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
+++ b/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
@@ -41,6 +41,8 @@ public class DefaultMirrorSelector
 
     private static final String EXTERNAL_WILDCARD = "external:*";
 
+    private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*";
+
     public Mirror getMirror( ArtifactRepository repository, List<Mirror> mirrors )
     {
         String repoId = repository.getId();
@@ -68,9 +70,14 @@ public class DefaultMirrorSelector
     }
 
     /**
-     * This method checks if the pattern matches the originalRepository. Valid patterns: * =
-     * everything external:* = everything not on the localhost and not file based. repo,repo1 = repo
-     * or repo1 *,!repo1 = everything except repo1
+     * This method checks if the pattern matches the originalRepository. Valid patterns:
+     * <ul>
+     * <li>{@code *} = everything,</li>
+     * <li>{@code external:*} = everything not on the localhost and not file based,</li>
+     * <li>{@code external:http:*} = any repository not on the localhost using HTTP,</li>
+     * <li>{@code repo,repo1} = {@code repo} or {@code repo1},</li>
+     * <li>{@code *,!repo1} = everything except {@code repo1}.</li>
+     * </ul>
      *
      * @param originalRepository to compare for a match.
      * @param pattern used for match. Currently only '*' is supported.
@@ -115,6 +122,12 @@ public class DefaultMirrorSelector
                     result = true;
                     // don't stop processing in case a future segment explicitly excludes this repo
                 }
+		// check for external:http:*
+		else if ( EXTERNAL_HTTP_WILDCARD.equals( repo ) && isExternalHttpRepo( originalRepository ) )
+		{
+			result = true;
+			// don't stop processing in case a future segment explicitly excludes this repo
+                }
                 else if ( WILDCARD.equals( repo ) )
                 {
                     result = true;
@@ -136,9 +149,35 @@ public class DefaultMirrorSelector
         try
         {
             URL url = new URL( originalRepository.getUrl() );
-            return !( url.getHost().equals( "localhost" ) || url.getHost().equals( "127.0.0.1" )
-                            || url.getProtocol().equals( "file" ) );
+	    return !( isLocal( url.getHost() ) || url.getProtocol().equals( "file" ) );
         }
+	catch ( MalformedURLException e )
+	{
+	    // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it
+            return false;
+	}
+    }
+
+    private static boolean isLocal( String host )
+    {
+	 return "localhost".equals( host ) || "127.0.0.1".equals( host );
+    }
+
+    /**
+     * Checks the URL to see if this repository refers to a non-localhost repository using HTTP.
+     *
+     * @param originalRepository
+     * @return true if external.
+     */
+    static boolean isExternalHttpRepo( ArtifactRepository originalRepository )
+    {
+        try
+        {
+	     URL url = new URL( originalRepository.getUrl() );
+	     return ( "http".equalsIgnoreCase( url.getProtocol() ) || "dav".equalsIgnoreCase( url.getProtocol() )
+                 || "dav:http".equalsIgnoreCase( url.getProtocol() )
+		 || "dav+http".equalsIgnoreCase( url.getProtocol() ) ) && !isLocal( url.getHost() );
+	}
         catch ( MalformedURLException e )
         {
             // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it
diff --git a/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java b/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
index 84ad93c..1b1c1d5 100644
--- a/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
+++ b/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
@@ -622,6 +622,8 @@ public class MavenRepositorySystem
 
     private static final String EXTERNAL_WILDCARD = "external:*";
 
+    private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*";
+
     public static Mirror getMirror( ArtifactRepository repository, List<Mirror> mirrors )
     {
         String repoId = repository.getId();
@@ -649,8 +651,14 @@ public class MavenRepositorySystem
     }
 
     /**
-     * This method checks if the pattern matches the originalRepository. Valid patterns: * = everything external:* =
-     * everything not on the localhost and not file based. repo,repo1 = repo or repo1 *,!repo1 = everything except repo1
+     * This method checks if the pattern matches the originalRepository. Valid patterns:
+     * <ul>
+     * <li>{@code *} = everything,</li>
+     * <li>{@code external:*} = everything not on the localhost and not file based,</li>
+     * <li>{@code external:http:*} = any repository not on the localhost using HTTP,</li>
+     * <li>{@code repo,repo1} = {@code repo} or {@code repo1},</li>
+     * <li>{@code *,!repo1} = everything except {@code repo1}.</li>
+     * </ul>
      *
      * @param originalRepository to compare for a match.
      * @param pattern used for match. Currently only '*' is supported.
@@ -694,6 +702,12 @@ public class MavenRepositorySystem
                     result = true;
                     // don't stop processing in case a future segment explicitly excludes this repo
                 }
+		// check for external:http:*
+		else if ( EXTERNAL_HTTP_WILDCARD.equals( repo ) && isExternalHttpRepo( originalRepository ) )
+		{
+		    result = true;
+		    // don't stop processing in case a future segment explicitly excludes this repo
+                }
                 else if ( WILDCARD.equals( repo ) )
                 {
                     result = true;
@@ -715,8 +729,34 @@ public class MavenRepositorySystem
         try
         {
             URL url = new URL( originalRepository.getUrl() );
-            return !( url.getHost().equals( "localhost" ) || url.getHost().equals( "127.0.0.1" )
-                            || url.getProtocol().equals( "file" ) );
+	    return !( isLocal( url.getHost() ) || url.getProtocol().equals( "file" ) );
+	}
+	catch ( MalformedURLException e )
+	{
+            // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it
+	    return false;
+	}
+     }
+
+     private static boolean isLocal( String host )
+     {
+	  return "localhost".equals( host ) || "127.0.0.1".equals( host );
+     }
+
+     /**
+      * Checks the URL to see if this repository refers to a non-localhost repository using HTTP.
+      *
+      * @param originalRepository
+      * @return true if external.
+      */
+     static boolean isExternalHttpRepo( ArtifactRepository originalRepository )
+     {
+	 try
+	 {
+	     URL url = new URL( originalRepository.getUrl() );
+	     return ( "http".equalsIgnoreCase( url.getProtocol() ) || "dav".equalsIgnoreCase( url.getProtocol() )
+	         || "dav:http".equalsIgnoreCase( url.getProtocol() )
+		 || "dav+http".equalsIgnoreCase( url.getProtocol() ) ) && !isLocal( url.getHost() );
         }
         catch ( MalformedURLException e )
         {
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
index 248a3b6..f262ad2 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
@@ -177,8 +177,8 @@ public class DefaultRepositorySystemSessionFactory
         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
         for ( Mirror mirror : request.getMirrors() )
         {
-            mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(),
-                                mirror.getMirrorOfLayouts() );
+	    mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.isBlocked(),
+			        mirror.getMirrorOf(), mirror.getMirrorOfLayouts() );
         }
         session.setMirrorSelector( mirrorSelector );
 
diff --git a/maven-settings/pom.xml b/maven-settings/pom.xml
index c16e823..3242832 100644
--- a/maven-settings/pom.xml
+++ b/maven-settings/pom.xml
@@ -46,7 +46,7 @@ under the License.
         <groupId>org.codehaus.modello</groupId>
         <artifactId>modello-maven-plugin</artifactId>
         <configuration>
-          <version>1.1.0</version>
+          <version>1.2.0</version>
           <models>
             <model>src/main/mdo/settings.mdo</model>
           </models>
diff --git a/maven-settings/src/main/mdo/settings.mdo b/maven-settings/src/main/mdo/settings.mdo
index 7547a9c..ca88c3b 100644
--- a/maven-settings/src/main/mdo/settings.mdo
+++ b/maven-settings/src/main/mdo/settings.mdo
@@ -632,7 +632,16 @@
             The layouts of repositories being mirrored. This value can be used to restrict the usage
             of the mirror to repositories with a matching layout (apart from a matching id). Since Maven 3.
           </description>
-        </field>
+         </field>
+	 <field>
+	   <name>blocked</name>
+	   <version>1.2.0+</version>
+	   <type>boolean</type>
+	   <defaultValue>false</defaultValue>
+	   <description>
+	     Whether this mirror should be blocked from any download request but fail the download process, explaining why.
+	   </description>
+	 </field>
       </fields>
       <codeSegments>
         <codeSegment>
@@ -647,7 +656,11 @@
         sb.append( "id=" ).append( this.getId() );
         sb.append( ",mirrorOf=" ).append( mirrorOf );
         sb.append( ",url=" ).append( this.url );
-        sb.append( ",name=" ).append( this.name );
+	sb.append( ",name=" ).append( this.name );
+	if ( isBlocked() )
+	{
+	    sb.append( ",blocked" );
+	}
         sb.append( "]" );
         return sb.toString();
     }
-- 
2.23.0

