/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.wicket.protocol.http.request; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.wicket.core.request.ClientInfo; import org.apache.wicket.markup.html.pages.BrowserInfoPage; import org.apache.wicket.protocol.http.ClientProperties; import org.apache.wicket.protocol.http.servlet.ServletWebRequest; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.util.string.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default client info object for web applications. * * @author Eelco Hillenius */ public class WebClientInfo extends ClientInfo { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(WebClientInfo.class); /** * The user agent string from the User-Agent header, app. Theoretically, this might differ from * {@link org.apache.wicket.protocol.http.ClientProperties#isNavigatorJavaEnabled()} property, which is * not set until an actual reply from a browser (e.g. using {@link BrowserInfoPage} is set. */ private final String userAgent; /** Client properties object. */ private final ClientProperties properties; /** * Construct. * * @param requestCycle * the request cycle */ public WebClientInfo(RequestCycle requestCycle) { this(requestCycle, new ClientProperties()); } /** * Construct. * * @param requestCycle * the request cycle */ public WebClientInfo(RequestCycle requestCycle, ClientProperties properties) { this(requestCycle, ((ServletWebRequest)requestCycle.getRequest()).getContainerRequest() .getHeader("User-Agent"), properties); } /** * Construct. * * @param requestCycle * the request cycle * @param userAgent * The User-Agent string */ public WebClientInfo(final RequestCycle requestCycle, final String userAgent) { this(requestCycle, userAgent, new ClientProperties()); } /** * Construct. * * @param requestCycle * the request cycle * @param userAgent * The User-Agent string * @param properties * properties of client */ public WebClientInfo(final RequestCycle requestCycle, final String userAgent, final ClientProperties properties) { super(); this.userAgent = userAgent; this.properties = properties; properties.setRemoteAddress(getRemoteAddr(requestCycle)); init(); } /** * Gets the client properties object. * * @return the client properties object */ public final ClientProperties getProperties() { return properties; } /** * returns the user agent string. * * @return the user agent string */ public final String getUserAgent() { return userAgent; } /** * returns the user agent string (lower case). * * @return the user agent string */ private String getUserAgentStringLc() { return (getUserAgent() != null) ? getUserAgent().toLowerCase() : ""; } /** * When using ProxyPass, requestCycle().getHttpServletRequest(). getRemoteAddr() returns the IP * of the machine forwarding the request. In order to maintain the clients ip address, the * server places it in the <a * href="http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#x-headers">X-Forwarded-For</a> * Header. * * Proxies may also mask the original client IP with tokens like "hidden" or "unknown". * If so, the last proxy ip address is returned. * * @param requestCycle * the request cycle * @return remoteAddr IP address of the client, using the X-Forwarded-For header and defaulting * to: getHttpServletRequest().getRemoteAddr() */ protected String getRemoteAddr(RequestCycle requestCycle) { ServletWebRequest request = (ServletWebRequest)requestCycle.getRequest(); HttpServletRequest req = request.getContainerRequest(); String remoteAddr = request.getHeader("X-Forwarded-For"); if (remoteAddr != null) { if (remoteAddr.contains(",")) { // sometimes the header is of form client ip,proxy 1 ip,proxy 2 ip,...,proxy n ip, // we just want the client remoteAddr = Strings.split(remoteAddr, ',')[0].trim(); } try { // If ip4/6 address string handed over, simply does pattern validation. InetAddress.getByName(remoteAddr); } catch (UnknownHostException e) { remoteAddr = req.getRemoteAddr(); } } else { remoteAddr = req.getRemoteAddr(); } return remoteAddr; } /** * Initialize the client properties object */ private void init() { setInternetExplorerProperties(); setOperaProperties(); setMozillaProperties(); setKonquerorProperties(); setChromeProperties(); setSafariProperties(); if (log.isDebugEnabled()) { log.debug("determined user agent: " + properties); } } /** * sets the konqueror specific properties */ private void setKonquerorProperties() { properties.setBrowserKonqueror(UserAgent.KONQUEROR.matches(getUserAgent())); if (properties.isBrowserKonqueror()) { // e.g.: Mozilla/5.0 (compatible; Konqueror/4.2; Linux) KHTML/4.2.96 (like Gecko) setMajorMinorVersionByPattern("konqueror/(\\d+)\\.(\\d+)"); } } /** * sets the chrome specific properties */ private void setChromeProperties() { properties.setBrowserChrome(UserAgent.CHROME.matches(getUserAgent())); if (properties.isBrowserChrome()) { // e.g.: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) // Chrome/12.0.702.0 Safari/534.24 setMajorMinorVersionByPattern("chrome/(\\d+)\\.(\\d+)"); } } /** * sets the safari specific properties */ private void setSafariProperties() { properties.setBrowserSafari(UserAgent.SAFARI.matches(getUserAgent())); if (properties.isBrowserSafari()) { String userAgent = getUserAgentStringLc(); if (userAgent.contains("version/")) { // e.g.: Mozilla/5.0 (Windows; U; Windows NT 6.1; sv-SE) AppleWebKit/533.19 // (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 setMajorMinorVersionByPattern("version/(\\d+)\\.(\\d+)"); } } } /** * sets the mozilla/firefox specific properties */ private void setMozillaProperties() { properties.setBrowserMozillaFirefox(UserAgent.FIREFOX.matches(getUserAgent())); properties.setBrowserMozilla(UserAgent.MOZILLA.matches(getUserAgent())); if (properties.isBrowserMozilla()) { if (properties.isBrowserMozillaFirefox()) { // e.g.: Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 // Ubuntu/9.25 (jaunty) Firefox/3.8 setMajorMinorVersionByPattern("firefox/(\\d+)\\.(\\d+)"); } } } /** * sets the opera specific properties */ private void setOperaProperties() { properties.setBrowserOpera(UserAgent.OPERA.matches(getUserAgent())); if (properties.isBrowserOpera()) { String userAgent = getUserAgentStringLc(); if (userAgent.startsWith("opera/") && userAgent.contains("version/")) { // e.g.: Opera/9.80 (Windows NT 6.0; U; nl) Presto/2.6.30 Version/10.60 setMajorMinorVersionByPattern("version/(\\d+)\\.(\\d+)"); } else if (userAgent.startsWith("opera/") && !userAgent.contains("version/")) { // e.g.: Opera/9.80 (Windows NT 6.0; U; nl) Presto/2.6.30 setMajorMinorVersionByPattern("opera/(\\d+)\\.(\\d+)"); } else { // e.g.: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; tr) Opera 10.10 setMajorMinorVersionByPattern("opera (\\d+)\\.(\\d+)"); } } } /** * sets the ie specific properties */ private void setInternetExplorerProperties() { properties.setBrowserInternetExplorer(UserAgent.INTERNET_EXPLORER.matches(getUserAgent())); if (properties.isBrowserInternetExplorer()) { // modern IE browsers (>=IE11) uses new user agent format if (getUserAgentStringLc().contains("like gecko")) { setMajorMinorVersionByPattern("rv:(\\d+)\\.(\\d+)"); } else { setMajorMinorVersionByPattern("msie (\\d+)\\.(\\d+)"); } } } /** * extracts the major and minor version out of the userAgentString string. * * @param patternString * The pattern must contain two matching groups */ private void setMajorMinorVersionByPattern(String patternString) { String userAgent = getUserAgentStringLc(); Matcher matcher = Pattern.compile(patternString).matcher(userAgent); if (matcher.find()) { properties.setBrowserVersionMajor(Integer.parseInt(matcher.group(1))); properties.setBrowserVersionMinor(Integer.parseInt(matcher.group(2))); } } }