1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.app.web.go;
17
18 import java.io.IOException;
19 import java.io.UnsupportedEncodingException;
20 import java.net.URLEncoder;
21 import java.util.Map;
22
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.codelibs.core.lang.StringUtil;
26 import org.codelibs.core.net.URLUtil;
27 import org.codelibs.fess.Constants;
28 import org.codelibs.fess.app.web.base.FessSearchAction;
29 import org.codelibs.fess.app.web.error.ErrorAction;
30 import org.codelibs.fess.crawler.util.CharUtil;
31 import org.codelibs.fess.helper.PathMappingHelper;
32 import org.codelibs.fess.helper.SearchLogHelper;
33 import org.codelibs.fess.helper.ViewHelper;
34 import org.codelibs.fess.opensearch.log.exentity.ClickLog;
35 import org.codelibs.fess.util.ComponentUtil;
36 import org.codelibs.fess.util.DocumentUtil;
37 import org.dbflute.util.DfTypeUtil;
38 import org.lastaflute.web.Execute;
39 import org.lastaflute.web.response.ActionResponse;
40 import org.lastaflute.web.response.HtmlResponse;
41 import org.lastaflute.web.response.StreamResponse;
42
43 import jakarta.annotation.Resource;
44
45
46
47
48
49
50 public class GoAction extends FessSearchAction {
51
52
53
54
55 public GoAction() {
56 super();
57 }
58
59
60
61
62
63 private static final Logger logger = LogManager.getLogger(GoAction.class);
64
65
66 @Resource
67 protected PathMappingHelper pathMappingHelper;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 @Execute
90 public ActionResponse index(final GoForm form) throws IOException {
91 validate(form, messages -> {}, () -> asHtml(virtualHost(path_Error_ErrorJsp)));
92 if (isLoginRequired()) {
93 return redirectToLogin();
94 }
95
96 Map<String, Object> doc = null;
97 try {
98 doc = searchHelper
99 .getDocumentByDocId(form.docId, new String[] { fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldConfigId() },
100 getUserBean())
101 .orElse(null);
102 } catch (final Exception e) {
103 logger.warn("Failed to request: {}", form.docId, e);
104 }
105 if (doc == null) {
106 saveError(messages -> messages.addErrorsDocidNotFound(GLOBAL, form.docId));
107 return redirect(ErrorAction.class);
108 }
109 final String url = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
110 if (url == null) {
111 saveError(messages -> messages.addErrorsDocumentNotFound(GLOBAL, form.docId));
112 return redirect(ErrorAction.class);
113 }
114
115 if (fessConfig.isSearchLog()) {
116 final String userSessionId = userInfoHelper.getUserCode();
117 if (userSessionId != null) {
118 final SearchLogHelper searchLogHelper = ComponentUtil.getSearchLogHelper();
119 final ClickLog clickLog = new ClickLog();
120 clickLog.setUrlId((String) doc.get(fessConfig.getIndexFieldId()));
121 clickLog.setUrl(url);
122 clickLog.setRequestedAt(systemHelper.getCurrentTimeAsLocalDateTime());
123 clickLog.setQueryRequestedAt(DfTypeUtil.toLocalDateTime(Long.parseLong(form.rt)));
124 clickLog.setUserSessionId(userSessionId);
125 clickLog.setDocId(form.docId);
126 clickLog.setQueryId(form.queryId);
127 if (form.order != null) {
128 clickLog.setOrder(form.order);
129 }
130 searchLogHelper.addClickLog(clickLog);
131 }
132 }
133
134 final String targetUrl = pathMappingHelper.replaceUrl(url);
135
136 String hash;
137 if (StringUtil.isNotBlank(form.hash)) {
138 final String value = URLUtil.decode(form.hash, Constants.UTF_8);
139 if (targetUrl.indexOf('#') == -1) {
140 final StringBuilder buf = new StringBuilder(value.length() + 100);
141 for (final char c : value.toCharArray()) {
142 if (CharUtil.isUrlChar(c) || c == ' ') {
143 buf.append(c);
144 } else {
145 try {
146 buf.append(URLEncoder.encode(String.valueOf(c), Constants.UTF_8));
147 } catch (final UnsupportedEncodingException e) {
148
149 }
150 }
151 }
152 hash = buf.toString();
153 } else {
154 hash = StringUtil.EMPTY;
155 }
156 } else {
157 hash = StringUtil.EMPTY;
158 }
159
160 if (!isFileSystemPath(targetUrl)) {
161 if (isValidRedirectUrl(targetUrl)) {
162 return HtmlResponse.fromRedirectPathAsIs(DocumentUtil.encodeUrl(targetUrl + hash));
163 } else {
164 logger.warn("Invalid redirect URL detected: {}", targetUrl);
165 saveError(messages -> messages.addErrorsDocumentNotFound(GLOBAL, form.docId));
166 return redirect(ErrorAction.class);
167 }
168 }
169 if (!fessConfig.isSearchFileProxyEnabled()) {
170 return HtmlResponse.fromRedirectPathAsIs(targetUrl + hash);
171 }
172 final ViewHelper viewHelper = ComponentUtil.getViewHelper();
173 try {
174 final StreamResponse response = viewHelper.asContentResponse(doc);
175 if (response.getHttpStatus().orElse(200) == 404) {
176 logger.debug("Document not found: url={}", targetUrl);
177 saveError(messages -> messages.addErrorsNotFoundOnFileSystem(GLOBAL, targetUrl));
178 return redirect(ErrorAction.class);
179 }
180 return response;
181 } catch (final Exception e) {
182 logger.warn("Failed to load: {}", doc, e);
183 saveError(messages -> messages.addErrorsNotLoadFromServer(GLOBAL, targetUrl));
184 return redirect(ErrorAction.class);
185 }
186 }
187
188
189
190
191
192
193
194
195
196 protected boolean isFileSystemPath(final String url) {
197 return ComponentUtil.getProtocolHelper().isFileSystemPath(url);
198 }
199
200
201
202
203
204
205
206 protected boolean isValidRedirectUrl(final String url) {
207 if (StringUtil.isBlank(url)) {
208 return false;
209 }
210 final String lowerUrl = url.toLowerCase();
211 if (lowerUrl.startsWith("http://") || lowerUrl.startsWith("https://")) {
212 return true;
213 }
214 if (lowerUrl.startsWith("javascript:") || lowerUrl.startsWith("data:") || lowerUrl.startsWith("vbscript:")) {
215 return false;
216 }
217 return true;
218 }
219 }