logo

class

web::UserAgent

sys::Obj
  web::UserAgent
   1  //
   2  // Copyright (c) 2006, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   24 Jul 06  Andy Frank  Creation
   7  //
   8  
   9  **
  10  ** UserAgent identifies a user agent.
  11  **
  12  class UserAgent
  13  {
  14  
  15  //////////////////////////////////////////////////////////////////////////
  16  // Constructor
  17  //////////////////////////////////////////////////////////////////////////
  18  
  19    **
  20    ** Construct a new UserAgent with this user agent string.
  21    **
  22    new make(Str userAgentStr)
  23    {
  24      // TODO - this does not allow for multiple spaces, which no one
  25      // uses in real user agents, but should be allow for them?
  26  
  27      // Tokens
  28      arr := userAgentStr.split(" ")
  29      for (i:=0; i<arr.size; i++)
  30      {
  31        s := arr[i]
  32        if (s.startsWith("("))
  33          while (!arr[i].endsWith(")"))
  34            s += " " + arr[++i]
  35        tokens.push(s)
  36      }
  37      tokens = tokens.ro
  38  
  39      // Browser
  40      isIE      = tokens.any |Str s->Bool| { return s.contains("MSIE") }
  41      isFirefox = tokens.any |Str s->Bool| { return s.startsWith("Firefox/") }
  42      isSafari  = tokens.any |Str s->Bool| { return s.startsWith("Safari/") }
  43      isOpera   = tokens.any |Str s->Bool| { return s.startsWith("Opera/") }
  44  
  45      // Version
  46      for (i:=0; i<tokens.size; i++)
  47      {
  48        s := tokens[i]
  49  
  50        if (s.contains("MSIE"))
  51        {
  52          start := s.index("MSIE") + 5
  53          end   := s.index(";", start)
  54          version = parseVer(s[start...end])
  55          break
  56        }
  57  
  58        if (s.startsWith("Firefox/"))
  59        {
  60          version = parseVer(s["Firefox/".size..-1])
  61          break
  62        }
  63  
  64        if (s.startsWith("Safari/"))
  65        {
  66          version = parseVer(s["Safari/".size..-1])
  67          break
  68        }
  69  
  70        if (s.startsWith("Opera/"))
  71        {
  72          version = parseVer(s["Opera/".size..-1])
  73          break
  74        }
  75      }
  76  
  77      // Check for valid version
  78      if (version == null) version = Version.fromStr("0.0")
  79    }
  80  
  81    **
  82    ** Parse this version string into a Version object.  This
  83    ** method is more forgiving than straight Version.fromStr().
  84    ** It will strip out invalid characters and attempt to
  85    ** return a reasonable version match.  Returns null if
  86    ** nothing could be salvaged.
  87    **
  88    **   "7.0b"  -> 7.0
  89    **   "2.1a2" -> 2.1
  90    **   "1b"    -> 1
  91    **   "abc"   -> null
  92    **
  93    internal Version parseVer(Str s)
  94    {
  95      v := ""
  96      for (i:=0; i<s.size; i++)
  97      {
  98        curr := s[i]
  99        next := (i < s.size-1) ? s[i+1] : -1
 100        last := (i > 0) ? s[i-1] : -1
 101  
 102        if (curr === '.')
 103        {
 104          if (last === '.') break
 105          if (next === '.') break
 106          if (!next.isDigit) break
 107  
 108        }
 109        else if (!curr.isDigit) break
 110  
 111        v += s[i].toChar
 112      }
 113  
 114      try { return Version.fromStr(v) } catch {}
 115      return null
 116    }
 117  
 118  //////////////////////////////////////////////////////////////////////////
 119  // Obj
 120  //////////////////////////////////////////////////////////////////////////
 121  
 122    **
 123    ** Return a string representation of this object. The result
 124    ** of this method will match the original Str that was used
 125    ** to parse this UserAgent.
 126    **
 127    override Str toStr()
 128    {
 129      s := ""
 130      tokens.each |Str p, Int i|
 131      {
 132        if (i > 0) s += " "
 133        s += p
 134      }
 135      return s
 136    }
 137  
 138  //////////////////////////////////////////////////////////////////////////
 139  // Fields
 140  //////////////////////////////////////////////////////////////////////////
 141  
 142    **
 143    ** Is this user agent Microsoft Internet Explorer.
 144    **
 145    readonly Bool isIE
 146  
 147    **
 148    ** Is this user agent Mozilla Firefox.
 149    **
 150    readonly Bool isFirefox
 151  
 152    **
 153    ** Is this agent Apple Safari.
 154    **
 155    readonly Bool isSafari
 156  
 157    **
 158    ** Is this agent Opera.
 159    **
 160    readonly Bool isOpera
 161  
 162    **
 163    ** The primary version for this user agent.  This field is
 164    ** only valid if the user agent is IE, Firefox, Safari, or
 165    ** Opera. For all other browsers, or if the above list has
 166    ** an invalid version string, this field will default
 167    ** to "0.0".
 168    **
 169    readonly Version version
 170  
 171    **
 172    ** The tokens identifying this user agent in the order
 173    ** they were parsed. This list is readonly.
 174    **
 175    Str[] tokens := Str[,]
 176  
 177  }