1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.helper;
17
18 import static org.codelibs.core.stream.StreamUtil.split;
19 import static org.codelibs.core.stream.StreamUtil.stream;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.lang.reflect.Field;
24 import java.net.JarURLConnection;
25 import java.net.URISyntaxException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Enumeration;
29 import java.util.List;
30 import java.util.jar.JarEntry;
31 import java.util.jar.JarFile;
32
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.Logger;
35 import org.codelibs.core.exception.ClassNotFoundRuntimeException;
36 import org.codelibs.core.exception.NoSuchFieldRuntimeException;
37 import org.codelibs.core.lang.ClassUtil;
38 import org.codelibs.core.lang.StringUtil;
39 import org.codelibs.fess.mylasta.direction.FessConfig;
40 import org.codelibs.fess.util.ComponentUtil;
41
42 import jakarta.annotation.PostConstruct;
43
44
45
46
47
48
49 public class ProtocolHelper {
50 private static final Logger logger = LogManager.getLogger(ProtocolHelper.class);
51
52
53 protected String[] webProtocols = StringUtil.EMPTY_STRINGS;
54
55
56 protected String[] fileProtocols = StringUtil.EMPTY_STRINGS;
57
58
59
60
61
62 public ProtocolHelper() {
63
64 }
65
66
67
68
69
70
71 @PostConstruct
72 public void init() {
73 final FessConfig fessConfig = ComponentUtil.getFessConfig();
74 webProtocols = split(fessConfig.getCrawlerWebProtocols(), ",")
75 .get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim() + ":").toArray(n -> new String[n]));
76 fileProtocols = split(fessConfig.getCrawlerFileProtocols(), ",")
77 .get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim() + ":").toArray(n -> new String[n]));
78
79 loadProtocols("org.codelibs.fess.net.protocol");
80
81 if (logger.isDebugEnabled()) {
82 logger.debug("Web protocols: protocols={}", Arrays.toString(webProtocols));
83 logger.debug("File protocols: protocols={}", Arrays.toString(fileProtocols));
84 }
85 }
86
87
88
89
90
91
92
93
94 protected void loadProtocols(final String basePackage) {
95 final List<String> subPackages = new ArrayList<>();
96 final String path = basePackage.replace('.', '/');
97 final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
98 try {
99 final Enumeration<java.net.URL> resources = classLoader.getResources(path);
100
101 while (resources.hasMoreElements()) {
102 final java.net.URL resource = resources.nextElement();
103 logger.debug("Loading resource: url={}", resource);
104
105 if ("file".equals(resource.getProtocol())) {
106 final File directory;
107 try {
108 directory = new File(resource.toURI());
109 } catch (final URISyntaxException e) {
110 logger.warn("Invalid URI for resource: url={}", resource, e);
111 continue;
112 }
113 if (directory.exists() && directory.isDirectory()) {
114 final File[] files = directory.listFiles(File::isDirectory);
115 if (files != null) {
116 for (final File file : files) {
117 final String name = file.getName();
118 subPackages.add(name);
119 logger.debug("Found subpackage: name={}, resource={}", name, resource);
120 }
121 }
122 }
123 } else if ("jar".equals(resource.getProtocol())) {
124 final JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
125 try (JarFile jarFile = jarURLConnection.getJarFile()) {
126 final Enumeration<JarEntry> entries = jarFile.entries();
127 while (entries.hasMoreElements()) {
128 final JarEntry entry = entries.nextElement();
129 final String entryName = entry.getName();
130 if (entryName.endsWith("/") && entryName.startsWith(path) && entryName.length() > path.length() + 1) {
131 final String name = entryName.substring(path.length() + 1, entryName.length() - 1);
132 if (name.indexOf('/') == -1) {
133 subPackages.add(name);
134 logger.debug("Found subpackage: name={}, resource={}", name, resource);
135 }
136 }
137 }
138 }
139 }
140 }
141 } catch (final IOException e) {
142 logger.warn("Cannot load subpackages: basePackage={}", basePackage, e);
143 }
144
145 subPackages.stream().forEach(protocol -> {
146 try {
147 final Class<Object> handlerClazz = ClassUtil.forName(basePackage + "." + protocol + ".Handler");
148 final Field protocolTypeField = ClassUtil.getDeclaredField(handlerClazz, "PROTOCOL_TYPE");
149 if (protocolTypeField.get(null) instanceof final String protocolType) {
150 if ("web".equalsIgnoreCase(protocolType)) {
151 addWebProtocol(protocol);
152 } else if ("file".equalsIgnoreCase(protocolType)) {
153 addFileProtocol(protocol);
154 } else {
155 logger.warn("Unknown protocol: protocol={}", protocol);
156 }
157 }
158 } catch (final ClassNotFoundRuntimeException e) {
159 logger.debug("{}.{}.Handler does not exist.", basePackage, protocol, e);
160 } catch (final NoSuchFieldRuntimeException e) {
161 logger.debug("{}.{}.Handler does not contain PROTOCOL_TYPE.", basePackage, protocol);
162 } catch (final Exception e) {
163 logger.warn("Cannot load Handler from {}.{}", basePackage, protocol, e);
164 }
165 });
166 }
167
168
169
170
171
172
173 public String[] getWebProtocols() {
174 return webProtocols;
175 }
176
177
178
179
180
181
182 public String[] getFileProtocols() {
183 return fileProtocols;
184 }
185
186
187
188
189
190
191
192 public boolean isValidWebProtocol(final String url) {
193 return stream(webProtocols).get(stream -> stream.anyMatch(s -> url.startsWith(s)));
194 }
195
196
197
198
199
200
201
202 public boolean isValidFileProtocol(final String url) {
203 return stream(fileProtocols).get(stream -> stream.anyMatch(s -> url.startsWith(s)));
204 }
205
206
207
208
209
210
211
212 public void addWebProtocol(final String protocol) {
213 final String prefix = protocol + ":";
214 if (stream(webProtocols).get(stream -> stream.anyMatch(s -> s.equals(prefix)))) {
215 logger.debug("Web protocols already contains: protocol={}", protocol);
216 return;
217 }
218 webProtocols = Arrays.copyOf(webProtocols, webProtocols.length + 1);
219 webProtocols[webProtocols.length - 1] = prefix;
220 }
221
222
223
224
225
226
227
228 public void addFileProtocol(final String protocol) {
229 final String prefix = protocol + ":";
230 if (stream(fileProtocols).get(stream -> stream.anyMatch(s -> s.equals(prefix)))) {
231 logger.debug("File protocols already contains: protocol={}", protocol);
232 return;
233 }
234 fileProtocols = Arrays.copyOf(fileProtocols, fileProtocols.length + 1);
235 fileProtocols[fileProtocols.length - 1] = prefix;
236 }
237
238
239
240
241
242
243
244
245 public boolean isFilePathProtocol(final String url) {
246 return url.startsWith("smb:") || url.startsWith("smb1:") || url.startsWith("file:") || url.startsWith("ftp:")
247 || url.startsWith("s3:") || url.startsWith("gcs:");
248 }
249
250
251
252
253
254
255
256
257 public boolean isFileSystemPath(final String url) {
258 return url.startsWith("file:") || url.startsWith("smb:") || url.startsWith("smb1:") || url.startsWith("ftp:")
259 || url.startsWith("storage:") || url.startsWith("s3:") || url.startsWith("gcs:");
260 }
261
262
263
264
265
266
267
268
269 public boolean shouldSkipUrlDecode(final String url) {
270 return url.startsWith("smb:") || url.startsWith("smb1:") || url.startsWith("ftp:") || url.startsWith("s3:")
271 || url.startsWith("gcs:");
272 }
273
274
275
276
277
278
279
280
281 public boolean hasKnownProtocol(final String path) {
282 return path.startsWith("http:") || path.startsWith("https:") || path.startsWith("smb:") || path.startsWith("smb1:")
283 || path.startsWith("ftp:") || path.startsWith("storage:") || path.startsWith("s3:") || path.startsWith("gcs:");
284 }
285 }