1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl.filter.prefixes;
20
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.Files;
25 import java.nio.file.NoSuchFileException;
26 import java.nio.file.Path;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30
31 import org.eclipse.aether.repository.RemoteRepository;
32
33 import static java.util.Objects.requireNonNull;
34
35
36
37
38
39
40
41
42
43 public interface PrefixesSource {
44
45
46
47 RemoteRepository origin();
48
49
50
51
52 Path path();
53
54
55
56
57 String message();
58
59
60
61
62 boolean valid();
63
64
65
66
67 List<String> entries();
68
69
70
71
72
73
74
75
76
77 static PrefixesSource of(RemoteRepository origin, Path path) {
78 requireNonNull(origin, "origin is null");
79 requireNonNull(path, "path is null");
80 return new Parser(origin, path).parse();
81 }
82
83 final class Parser {
84 private static final String PREFIX_MAGIC = "## repository-prefixes/2.0";
85 private static final String PREFIX_LEGACY_MAGIC = "# Prefix file generated by Sonatype Nexus";
86 private static final String PREFIX_UNSUPPORTED = "@ unsupported";
87 private static final int MAX_ENTRIES = 100_000;
88 private static final int MAX_LINE_LENGTH = 250;
89
90 private final RemoteRepository origin;
91 private final Path path;
92
93 private Parser(RemoteRepository origin, Path path) {
94 this.origin = origin;
95 this.path = path;
96 }
97
98 private PrefixesSource parse() {
99 try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
100 ArrayList<String> entries = new ArrayList<>();
101 String line = reader.readLine();
102 if (!PREFIX_MAGIC.equals(line)
103 && !PREFIX_LEGACY_MAGIC.equals(line)
104 && !PREFIX_UNSUPPORTED.equals(line)) {
105 return invalid(origin, path, "No expected magic in file");
106 }
107 while (line != null) {
108 line = line.trim();
109 if (PREFIX_UNSUPPORTED.equals(line)) {
110
111 return invalid(origin, path, "Declares itself unsupported");
112 }
113 if (!line.startsWith("#") && !line.isEmpty()) {
114
115 if (line.length() > MAX_LINE_LENGTH) {
116 return invalid(origin, path, "Contains too long line");
117 }
118
119 if (!line.chars().allMatch(c -> c < 128)) {
120 return invalid(origin, path, "Contains non-ASCII characters");
121 }
122
123 if (line.contains(":")
124 || line.contains("<")
125 || line.contains(">")
126 || line.contains("\\")
127 || line.contains("//")) {
128 return invalid(origin, path, "Contains forbidden characters");
129 }
130
131
132 while (line.startsWith(".")) {
133 line = line.substring(1);
134 }
135 entries.add(line);
136 }
137 line = reader.readLine();
138
139
140 if (entries.size() > MAX_ENTRIES) {
141 return invalid(origin, path, "Contains too many entries");
142 }
143 }
144 return new Impl(origin, path, "OK", true, Collections.unmodifiableList(entries));
145 } catch (NoSuchFileException e) {
146 return invalid(origin, path, "No such file");
147 } catch (IOException e) {
148 return invalid(origin, path, "Could not read the file: " + e.getMessage());
149 }
150 }
151
152 private static PrefixesSource invalid(RemoteRepository origin, Path path, String message) {
153 return new Impl(origin, path, message, false, Collections.emptyList());
154 }
155
156 private static class Impl implements PrefixesSource {
157 private final RemoteRepository origin;
158 private final Path path;
159 private final String message;
160 private final boolean valid;
161 private final List<String> entries;
162
163 private Impl(RemoteRepository origin, Path path, String message, boolean valid, List<String> entries) {
164 this.origin = requireNonNull(origin);
165 this.path = requireNonNull(path);
166 this.message = message;
167 this.valid = valid;
168 this.entries = entries;
169 }
170
171 @Override
172 public RemoteRepository origin() {
173 return origin;
174 }
175
176 @Override
177 public Path path() {
178 return path;
179 }
180
181 @Override
182 public String message() {
183 return message;
184 }
185
186 @Override
187 public boolean valid() {
188 return valid;
189 }
190
191 @Override
192 public List<String> entries() {
193 return entries;
194 }
195 }
196 }
197 }