001package org.apache.turbine.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022/**
023 * This class parses the user agent string and sets javasciptOK and
024 * cssOK following the rules described below.  If you want to check
025 * for specific browsers/versions then use this class to parse the
026 * user agent string and use the accessor methods in this class.
027 *
028 * JavaScriptOK means that the browser understands JavaScript on the
029 * same level the Navigator 3 does.  Specifically, it can use named
030 * images.  This allows easier rollovers.  If a browser doesn't do
031 * this (Nav 2 or MSIE 3), then we just assume it can't do any
032 * JavaScript.  Referencing images by load order is too hard to
033 * maintain.
034 *
035 * CSSOK is kind of sketchy in that Nav 4 and MSIE work differently,
036 * but they do seem to have most of the functionality.  MSIE 4 for the
037 * Mac has buggy CSS support, so we let it do JavaScript, but no CSS.
038 *
039 * Ported from Leon's PHP code at
040 * http://www.working-dogs.com/freetrade by Frank.
041 *
042 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
043 * @author <a href="mailto:leon@clearink.com">Leon Atkisnon</a>
044 * @author <a href="mailto:mospaw@polk-county.com">Chris Mospaw</a>
045 * @author <a href="mailto:bgriffin@cddb.com">Benjamin Elijah Griffin</a>
046 * @version $Id: BrowserDetector.java 1706239 2015-10-01 13:18:35Z tv $
047 */
048public class BrowserDetector
049{
050    /** Internet Explorer */
051    public static final String MSIE = "MSIE";
052    /** Opera */
053    public static final String OPERA = "Opera";
054    /** Mozilla, Firefox and friends */
055    public static final String MOZILLA = "Mozilla";
056
057    /** Running on Windows */
058    public static final String WINDOWS = "Windows";
059    /** Running on Unix */
060    public static final String UNIX = "Unix";
061    /** Running on Mac OS X */
062    public static final String MACINTOSH = "Macintosh";
063
064    /** The user agent string. */
065    private String userAgentString = "";
066
067    /** The browser name specified in the user agent string. */
068    private String browserName = "";
069
070    /**
071     * The browser version specified in the user agent string.  If we
072     * can't parse the version just assume an old browser.
073     */
074    private float browserVersion = (float) 1.0;
075
076    /**
077     * The browser platform specified in the user agent string.
078     */
079    private String browserPlatform = "unknown";
080
081    /** Whether or not javascript works in this browser. */
082    private boolean javascriptOK = false;
083
084    /** Whether or not CSS works in this browser. */
085    private boolean cssOK = false;
086
087    /** Whether or not file upload works in this browser. */
088    private boolean fileUploadOK = false;
089
090    /**
091     * Constructor used to initialize this class.
092     *
093     * @param userAgentString A String with the user agent field.
094     */
095    public BrowserDetector(String userAgentString)
096    {
097        this.userAgentString = userAgentString;
098        parse();
099    }
100
101    /**
102     * Constructor used to initialize this class.
103     *
104     * @param data The Turbine RunData object.
105     */
106    public BrowserDetector(RunData data)
107    {
108        this.userAgentString = data.getUserAgent();
109        parse();
110    }
111
112    /**
113     * Whether or not CSS works in this browser.
114     *
115     * @return True if CSS works in this browser.
116     */
117    public boolean isCssOK()
118    {
119        return cssOK;
120    }
121
122    /**
123     * Whether or not file upload works in this browser.
124     *
125     * @return True if file upload works in this browser.
126     */
127    public boolean isFileUploadOK()
128    {
129        return fileUploadOK;
130    }
131
132    /**
133     * Whether or not Javascript works in this browser.
134     *
135     * @return True if Javascript works in this browser.
136     */
137    public boolean isJavascriptOK()
138    {
139        return javascriptOK;
140    }
141
142    /**
143     * The browser name specified in the user agent string.
144     *
145     * @return A String with the browser name.
146     */
147    public String getBrowserName()
148    {
149        return browserName;
150    }
151
152    /**
153     * The browser platform specified in the user agent string.
154     *
155     * @return A String with the browser platform.
156     */
157    public String getBrowserPlatform()
158    {
159        return browserPlatform;
160    }
161
162    /**
163     * The browser version specified in the user agent string.
164     *
165     * @return A String with the browser version.
166     */
167    public float getBrowserVersion()
168    {
169        return browserVersion;
170    }
171
172    /**
173     * The user agent string for this class.
174     *
175     * @return A String with the user agent.
176     */
177    public String getUserAgentString()
178    {
179        return userAgentString;
180    }
181
182    /**
183     * Helper method to initialize this class.
184     */
185    private void parse()
186    {
187        int versionStartIndex = userAgentString.indexOf("/");
188        int versionEndIndex = userAgentString.indexOf(" ");
189
190        // Get the browser name and version.
191        browserName = userAgentString.substring(0, versionStartIndex);
192        try
193        {
194            // Not all user agents will have a space in the reported
195            // string.
196            String agentSubstring = null;
197            if (versionEndIndex < 0)
198            {
199                agentSubstring
200                        = userAgentString.substring(versionStartIndex + 1);
201            }
202            else
203            {
204                agentSubstring = userAgentString
205                        .substring(versionStartIndex + 1, versionEndIndex);
206            }
207            browserVersion = toFloat(agentSubstring);
208        }
209        catch (NumberFormatException e)
210        {
211            // Just use the default value.
212        }
213
214        // MSIE lies about its name.  Of course...
215        if (userAgentString.indexOf(MSIE) != -1)
216        {
217            // Ex: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)
218            versionStartIndex = (userAgentString.indexOf(MSIE)
219                    + MSIE.length() + 1);
220            versionEndIndex = userAgentString.indexOf(";", versionStartIndex);
221
222            browserName = MSIE;
223            try
224            {
225                browserVersion = toFloat(userAgentString
226                        .substring(versionStartIndex, versionEndIndex));
227            }
228            catch (NumberFormatException e)
229            {
230                // Just use the default value.
231            }
232
233            // PHP code
234            // $Browser_Name = "MSIE";
235            // $Browser_Version = strtok("MSIE");
236            // $Browser_Version = strtok(" ");
237            // $Browser_Version = strtok(";");
238        }
239
240        // Opera isn't completely honest, either...
241        // Modificaton by Chris Mospaw <mospaw@polk-county.com>
242        if (userAgentString.indexOf(OPERA) != -1)
243        {
244            // Ex: Mozilla/4.0 (Windows NT 4.0;US) Opera 3.61  [en]
245            // Ex: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.02
246            versionStartIndex = (userAgentString.indexOf(OPERA)
247                    + OPERA.length() + 1);
248            versionEndIndex = userAgentString.indexOf(" ", versionStartIndex);
249            if (versionEndIndex == -1)
250            {
251                versionEndIndex = userAgentString.length();
252            }
253
254            browserName = OPERA;
255            try
256            {
257                browserVersion = toFloat(userAgentString
258                        .substring(versionStartIndex, versionEndIndex));
259            }
260            catch (NumberFormatException e)
261            {
262                // Just use the default value.
263            }
264
265            // PHP code
266            // $Browser_Name = "Opera";
267            // $Browser_Version = strtok("Opera");
268            // $Browser_Version = strtok("/");
269            // $Browser_Version = strtok(";");
270        }
271
272
273        // Try to figure out what platform.
274        if ((userAgentString.indexOf("Windows") != -1)
275                || (userAgentString.indexOf("WinNT") != -1)
276                || (userAgentString.indexOf("Win98") != -1)
277                || (userAgentString.indexOf("Win95") != -1))
278        {
279            browserPlatform = WINDOWS;
280        }
281
282        if (userAgentString.indexOf("Mac") != -1)
283        {
284            browserPlatform = MACINTOSH;
285        }
286
287        if (userAgentString.indexOf("X11") != -1)
288        {
289            browserPlatform = UNIX;
290        }
291
292        if (browserPlatform == WINDOWS)
293        {
294            if (browserName.equals(MOZILLA))
295            {
296                if (browserVersion >= 3.0)
297                {
298                    javascriptOK = true;
299                    fileUploadOK = true;
300                }
301                if (browserVersion >= 4.0)
302                {
303                    cssOK = true;
304                }
305            }
306            else if (browserName == MSIE)
307            {
308                if (browserVersion >= 4.0)
309                {
310                    javascriptOK = true;
311                    fileUploadOK = true;
312                    cssOK = true;
313                }
314            }
315            else if (browserName == OPERA)
316            {
317                if (browserVersion >= 3.0)
318                {
319                    javascriptOK = true;
320                    fileUploadOK = true;
321                    cssOK = true;
322                }
323            }
324        }
325        else if (browserPlatform == MACINTOSH)
326        {
327            if (browserName.equals(MOZILLA))
328            {
329                if (browserVersion >= 3.0)
330                {
331                    javascriptOK = true;
332                    fileUploadOK = true;
333                }
334                if (browserVersion >= 4.0)
335                {
336                    cssOK = true;
337                }
338            }
339            else if (browserName == MSIE)
340            {
341                if (browserVersion >= 4.0)
342                {
343                    javascriptOK = true;
344                    fileUploadOK = true;
345                }
346                if (browserVersion > 4.0)
347                {
348                    cssOK = true;
349                }
350            }
351        }
352        else if (browserPlatform == UNIX)
353        {
354            if (browserName.equals(MOZILLA))
355            {
356                if (browserVersion >= 3.0)
357                {
358                    javascriptOK = true;
359                    fileUploadOK = true;
360                }
361                if (browserVersion >= 4.0)
362                {
363                    cssOK = true;
364                }
365            }
366        }
367    }
368
369    /**
370     * Helper method to convert String to a float.
371     *
372     * @param s A String.
373     * @return The String converted to float.
374     */
375    private static final float toFloat(String s)
376    {
377        return Float.valueOf(s).floatValue();
378    }
379
380}