blob: 88d645f147f7e1a273beaef84d8e65f3107cdf92 [file] [log] [blame]
/*
* 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;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.TimeZone;
import jakarta.servlet.http.Cookie;
import org.apache.wicket.markup.html.pages.BrowserInfoPage;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.string.AppendingStringBuffer;
/**
* Description of various user agent (browser) properties. To fill the properties with values from
* the user agent you need to probe the browser using javascript and request header analysis. Wicket
* provides a default implementation of this in {@link BrowserInfoPage}.
* <p>
* A convenient way of letting Wicket do a sneaky redirect to {@link BrowserInfoPage} (and back
* again) is to put this in your Application's init method:
*
* <pre>
* getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
* </pre>
*
* </p>
*
* WARNING: Be sure you think about the dangers of depending on information you pull from the client
* too much. They may be easily spoofed or inaccurate in other ways, and properties like window and
* browser size are all too easy to be used naively.
*
* @see BrowserInfoPage
* @author Frank Bille (frankbille)
*/
public class ClientProperties implements IClusterable
{
private static final long serialVersionUID = 1L;
private int browserHeight = -1;
private int browserWidth = -1;
private boolean navigatorCookieEnabled;
private boolean navigatorJavaEnabled;
private String navigatorAppCodeName;
private String navigatorAppName;
private String navigatorAppVersion;
private String navigatorLanguage;
private String navigatorPlatform;
private String navigatorUserAgent;
private String remoteAddress;
private int screenColorDepth = -1;
private int screenHeight = -1;
private int screenWidth = -1;
private String utcDSTOffset;
private String utcOffset;
private String jsTimeZone;
private String hostname;
private boolean javaScriptEnabled;
/** Cached timezone for repeating calls to {@link #getTimeZone()} */
private transient TimeZone timeZone;
/**
* @return The browser height at the time it was measured
*/
public int getBrowserHeight()
{
return browserHeight;
}
/**
* @return The browser width at the time it was measured
*/
public int getBrowserWidth()
{
return browserWidth;
}
/**
* @return The client's navigator.appCodeName property.
*/
public String getNavigatorAppCodeName()
{
return navigatorAppCodeName;
}
/**
* @return The client's navigator.appName property.
*/
public String getNavigatorAppName()
{
return navigatorAppName;
}
/**
* @return The client's navigator.appVersion property.
*/
public String getNavigatorAppVersion()
{
return navigatorAppVersion;
}
/**
* @return The client's navigator.language (or navigator.userLanguage) property.
*/
public String getNavigatorLanguage()
{
return navigatorLanguage;
}
/**
* @return The client's navigator.platform property.
*/
public String getNavigatorPlatform()
{
return navigatorPlatform;
}
/**
* @return The client's navigator.userAgent property.
*/
public String getNavigatorUserAgent()
{
return navigatorUserAgent;
}
/**
* @return The client's remote/ip address.
*/
public String getRemoteAddress()
{
return remoteAddress;
}
/**
* @return The clients hostname shown in the browser
*/
public String getHostname()
{
return hostname;
}
/**
* @return Color depth of the screen in bits (integer).
*/
public int getScreenColorDepth()
{
return screenColorDepth;
}
/**
* @return Height of the screen in pixels (integer).
*/
public int getScreenHeight()
{
return screenHeight;
}
/**
* @return Height of the screen in pixels (integer).
*/
public int getScreenWidth()
{
return screenWidth;
}
/**
* Get the client's time zone if that could be detected.
*
* @return The client's time zone
*/
public TimeZone getTimeZone()
{
if (timeZone == null && jsTimeZone != null)
{
TimeZone temptimeZone = TimeZone.getTimeZone(jsTimeZone);
if (jsTimeZone.equals(temptimeZone.getID()))
{
timeZone = temptimeZone;
}
}
if (timeZone == null)
{
String utc = getUtcOffset();
if (utc != null)
{
// apparently it is platform dependent on whether you get the
// offset in a decimal form or not. This parses the decimal
// form of the UTC offset, taking into account several
// possibilities
// such as getting the format in +2.5 or -1.2
int dotPos = utc.indexOf('.');
if (dotPos >= 0)
{
String hours = utc.substring(0, dotPos);
String hourPart = utc.substring(dotPos + 1);
if (hours.startsWith("+"))
{
hours = hours.substring(1);
}
int offsetHours = Integer.parseInt(hours);
int offsetMins = (int)(Double.parseDouble(hourPart) * 6);
// construct a GMT timezone offset string from the retrieved
// offset which can be parsed by the TimeZone class.
AppendingStringBuffer sb = new AppendingStringBuffer("GMT");
sb.append(offsetHours > 0 ? '+' : '-');
sb.append(Math.abs(offsetHours));
sb.append(':');
if (offsetMins < 10)
{
sb.append('0');
}
sb.append(offsetMins);
timeZone = TimeZone.getTimeZone(sb.toString());
}
else
{
int offset = Integer.parseInt(utc);
if (offset < 0)
{
utc = utc.substring(1);
}
timeZone = TimeZone.getTimeZone("GMT" + ((offset > 0) ? '+' : '-') + utc);
}
String dstOffset = getUtcDSTOffset();
if (timeZone != null && dstOffset != null)
{
TimeZone dstTimeZone;
dotPos = dstOffset.indexOf('.');
if (dotPos >= 0)
{
String hours = dstOffset.substring(0, dotPos);
String hourPart = dstOffset.substring(dotPos + 1);
if (hours.startsWith("+"))
{
hours = hours.substring(1);
}
int offsetHours = Integer.parseInt(hours);
int offsetMins = (int)(Double.parseDouble(hourPart) * 6);
// construct a GMT timezone offset string from the
// retrieved
// offset which can be parsed by the TimeZone class.
AppendingStringBuffer sb = new AppendingStringBuffer("GMT");
sb.append(offsetHours > 0 ? '+' : '-');
sb.append(Math.abs(offsetHours));
sb.append(':');
if (offsetMins < 10)
{
sb.append('0');
}
sb.append(offsetMins);
dstTimeZone = TimeZone.getTimeZone(sb.toString());
}
else
{
int offset = Integer.parseInt(dstOffset);
if (offset < 0)
{
dstOffset = dstOffset.substring(1);
}
dstTimeZone = TimeZone.getTimeZone("GMT" + ((offset > 0) ? '+' : '-') +
dstOffset);
}
// if the dstTimezone (1 July) has a different offset then
// the real time zone (1 January) try to combine the 2.
if (dstTimeZone != null &&
dstTimeZone.getRawOffset() != timeZone.getRawOffset())
{
int dstSaving = Math.abs(dstTimeZone.getRawOffset() - timeZone.getRawOffset());
String[] availableIDs = TimeZone.getAvailableIDs(dstTimeZone.getRawOffset() < timeZone.getRawOffset() ? dstTimeZone.getRawOffset() : timeZone.getRawOffset());
for (String availableID : availableIDs)
{
TimeZone zone = TimeZone.getTimeZone(availableID);
if (zone.getDSTSavings() == dstSaving)
{
// this is a best guess... still the start and end of the DST should
// be needed to know to be completely correct, or better yet
// not just the GMT offset but the TimeZone ID should be transfered
// from the browser.
timeZone = zone;
break;
}
}
}
}
}
}
return timeZone;
}
/**
* @return The client's time DST offset from UTC in hours (note: if you do this yourself, use
* 'new Date(new Date().getFullYear(), 0, 6, 0, 0, 0, 0).getTimezoneOffset() / -60'
* (note the -)).
*/
public String getUtcDSTOffset()
{
return utcDSTOffset;
}
/**
* @return The client's time offset from UTC in hours (note: if you do this yourself, use 'new
* Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0).getTimezoneOffset() / -60' (note the
* -)).
*/
public String getUtcOffset()
{
return utcOffset;
}
/**
* Flag indicating support of JavaScript in the browser.
*
* @return True if JavaScript is enabled
*/
public boolean isJavaScriptEnabled() {
return javaScriptEnabled;
}
/**
*
*
* @return The client's navigator.cookieEnabled property.
*/
public boolean isNavigatorCookieEnabled()
{
if (!navigatorCookieEnabled && RequestCycle.get() != null)
{
Collection<Cookie> cookies = ((WebRequest)RequestCycle.get().getRequest()).getCookies();
navigatorCookieEnabled = cookies != null && cookies.size() > 0;
}
return navigatorCookieEnabled;
}
/**
* @return The client's navigator.javaEnabled property.
*/
public boolean isNavigatorJavaEnabled()
{
return navigatorJavaEnabled;
}
/**
* @param browserHeight
* The height of the browser
*/
public void setBrowserHeight(int browserHeight)
{
this.browserHeight = browserHeight;
}
/**
* @param browserWidth
* The browser width
*/
public void setBrowserWidth(int browserWidth)
{
this.browserWidth = browserWidth;
}
/**
* @param cookiesEnabled
* The client's navigator.cookieEnabled property.
*/
public void setNavigatorCookieEnabled(boolean cookiesEnabled)
{
this.navigatorCookieEnabled = cookiesEnabled;
}
/**
* @param navigatorJavaEnabled
* The client's navigator.javaEnabled property.
*/
public void setNavigatorJavaEnabled(boolean navigatorJavaEnabled)
{
this.navigatorJavaEnabled = navigatorJavaEnabled;
}
/**
* @param navigatorAppCodeName
* The client's navigator.appCodeName property.
*/
public void setNavigatorAppCodeName(String navigatorAppCodeName)
{
this.navigatorAppCodeName = navigatorAppCodeName;
}
/**
* @param navigatorAppName
* The client's navigator.appName property.
*/
public void setNavigatorAppName(String navigatorAppName)
{
this.navigatorAppName = navigatorAppName;
}
/**
* @param navigatorAppVersion
* The client's navigator.appVersion property.
*/
public void setNavigatorAppVersion(String navigatorAppVersion)
{
this.navigatorAppVersion = navigatorAppVersion;
}
/**
* @param navigatorLanguage
* The client's navigator.language (or navigator.userLanguage) property.
*/
public void setNavigatorLanguage(String navigatorLanguage)
{
this.navigatorLanguage = navigatorLanguage;
}
/**
* @param navigatorPlatform
* The client's navigator.platform property.
*/
public void setNavigatorPlatform(String navigatorPlatform)
{
this.navigatorPlatform = navigatorPlatform;
}
/**
* @param navigatorUserAgent
* The client's navigator.userAgent property.
*/
public void setNavigatorUserAgent(String navigatorUserAgent)
{
this.navigatorUserAgent = navigatorUserAgent;
}
/**
* @param remoteAddress
* The client's remote/ip address.
*/
public void setRemoteAddress(String remoteAddress)
{
this.remoteAddress = remoteAddress;
}
/**
* @param hostname
* the hostname shown in the browser.
*/
public void setHostname(String hostname)
{
this.hostname = hostname;
}
/**
* @param screenColorDepth
* Color depth of the screen in bits (integer).
*/
public void setScreenColorDepth(int screenColorDepth)
{
this.screenColorDepth = screenColorDepth;
}
/**
* @param screenHeight
* Height of the screen in pixels (integer).
*/
public void setScreenHeight(int screenHeight)
{
this.screenHeight = screenHeight;
}
/**
* @param screenWidth
* Height of the screen in pixels (integer).
*/
public void setScreenWidth(int screenWidth)
{
this.screenWidth = screenWidth;
}
/**
* Sets time zone.
*
* @param timeZone
*/
public void setTimeZone(TimeZone timeZone)
{
this.timeZone = timeZone;
}
/**
* @param utcDSTOffset
*/
public void setUtcDSTOffset(String utcDSTOffset)
{
this.utcDSTOffset = utcDSTOffset;
}
/**
* @param utcOffset
* The client's time offset from UTC in minutes (note: if you do this yourself, use
* 'new Date().getTimezoneOffset() / -60' (note the -)).
*/
public void setUtcOffset(String utcOffset)
{
this.utcOffset = utcOffset;
}
/**
* @param jsTimeZone
*/
public void setJsTimeZone(String jsTimeZone)
{
this.jsTimeZone = jsTimeZone;
}
/**
* @param javaScriptEnabled
* is JavaScript supported in the browser
*/
public void setJavaScriptEnabled(boolean javaScriptEnabled) {
this.javaScriptEnabled = javaScriptEnabled;
}
@Override
public String toString()
{
StringBuilder b = new StringBuilder();
Class<?> clazz = getClass();
while (clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
// Ignore these fields
if (Modifier.isStatic(field.getModifiers()) ||
Modifier.isTransient(field.getModifiers()) ||
field.isSynthetic())
{
continue;
}
field.setAccessible(true);
Object value;
try
{
value = field.get(this);
}
catch (IllegalArgumentException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
if (field.getType().equals(Integer.TYPE))
{
if (Integer.valueOf(-1).equals(value))
{
value = null;
}
}
if (value != null)
{
b.append(field.getName());
b.append('=');
b.append(value);
b.append('\n');
}
}
clazz = clazz.getSuperclass();
}
return b.toString();
}
/**
* Read parameters.
*
* @param parameters
* parameters sent from browser
*/
public void read(IRequestParameters parameters)
{
setNavigatorAppCodeName(parameters.getParameterValue("navigatorAppCodeName").toString("N/A"));
setNavigatorAppName(parameters.getParameterValue("navigatorAppName").toString("N/A"));
setNavigatorAppVersion(parameters.getParameterValue("navigatorAppVersion").toString("N/A"));
setNavigatorCookieEnabled(parameters.getParameterValue("navigatorCookieEnabled").toBoolean(false));
setNavigatorJavaEnabled(parameters.getParameterValue("navigatorJavaEnabled").toBoolean(false));
setNavigatorLanguage(parameters.getParameterValue("navigatorLanguage").toString("N/A"));
setNavigatorPlatform(parameters.getParameterValue("navigatorPlatform").toString("N/A"));
setNavigatorUserAgent(parameters.getParameterValue("navigatorUserAgent").toString("N/A"));
setScreenWidth(parameters.getParameterValue("screenWidth").toInt(-1));
setScreenHeight(parameters.getParameterValue("screenHeight").toInt(-1));
setScreenColorDepth(parameters.getParameterValue("screenColorDepth").toInt(-1));
setUtcOffset(parameters.getParameterValue("utcOffset").toString(null));
setUtcDSTOffset(parameters.getParameterValue("utcDSTOffset").toString(null));
setJsTimeZone(parameters.getParameterValue("jsTimeZone").toString(null));
setBrowserWidth(parameters.getParameterValue("browserWidth").toInt(-1));
setBrowserHeight(parameters.getParameterValue("browserHeight").toInt(-1));
setHostname(parameters.getParameterValue("hostname").toString("N/A"));
}
}