OpenSencillo  2015.009
Long live the simplicity of PHP
 All Data Structures Namespaces Files Functions Pages
userAgent.parser.usagpa.php
1 <?php
61 {
62  // browser regex => browser ID
63  // if there are aliases, the common name should be last
64  static protected $browsers = array(
65  'abrowse' => 'AB',
66  'amaya' => 'AM',
67  'amigavoyager' => 'AV',
68  'amiga-aweb' => 'AW',
69  'arora' => 'AR',
70  'beonex' => 'BE',
71  // BlackBerry smartphones and tablets
72  'blackberry' => 'BB', // BlackBerry 6 and PlayBook adopted webkit
73  'bb10' => 'B2', // BlackBerry 10
74  'playbook' => 'BP',
75  'browsex' => 'BX',
76  // Camino (and earlier incarnation)
77  'chimera' => 'CA',
78  'camino' => 'CA',
79  'cheshire' => 'CS',
80  // Chrome, Chromium, and ChromePlus
81  'crmo' => 'CH',
82  'chrome' => 'CH',
83  // Chrome Frame
84  'chromeframe' => 'CF',
85  'cometbird' => 'CO',
86  'dillo' => 'DI',
87  'elinks' => 'EL',
88  'epiphany' => 'EP',
89  'fennec' => 'FE',
90  // Dolfin (or Dolphin)
91  'dolfin' => 'DF',
92  // Firefox (in its many incarnations and rebranded versions)
93  'phoenix' => 'PX',
94  'mozilla firebird' => 'FB',
95  'firebird' => 'FB',
96  'bonecho' => 'FF',
97  'minefield' => 'FF',
98  'namoroka' => 'FF',
99  'shiretoko' => 'FF',
100  'granparadiso' => 'FF',
101  'iceweasel' => 'FF',
102  'icecat' => 'FF',
103  'firefox' => 'FF',
104  'thunderbird' => 'TB',
105  'flock' => 'FL',
106  'fluid' => 'FD',
107  'galeon' => 'GA',
108  'google earth' => 'GE',
109  'hana' => 'HA',
110  'hotjava' => 'HJ',
111  'ibrowse' => 'IB',
112  'icab' => 'IC',
113  // IE (including shells: Acoo, AOL, Avant, Crazy Browser, Green Browser, KKMAN, Maxathon)
114  'msie' => 'IE',
115  'trident' => 'IE',
116  'microsoft internet explorer' => 'IE',
117  'internet explorer' => 'IE',
118  'iron' => 'IR',
119  'kapiko' => 'KP',
120  'kazehakase' => 'KZ',
121  'k-meleon' => 'KM',
122  'konqueror' => 'KO',
123  'links' => 'LI',
124  'lynx' => 'LX',
125  'midori' => 'MI',
126  // SeaMonkey (formerly Mozilla Suite) (and rebranded versions)
127  'mozilla' => 'MO',
128  'gnuzilla' => 'SM',
129  'iceape' => 'SM',
130  'seamonkey' => 'SM',
131  // NCSA Mosaic (and incarnations)
132  'mosaic' => 'MC',
133  'ncsa mosaic' => 'MC',
134  // Netscape Navigator
135  'navigator' => 'NS',
136  'netscape6' => 'NS',
137  'netscape' => 'NS',
138  'nx' => 'NF',
139  'netfront' => 'NF',
140  'omniweb' => 'OW',
141  // Opera
142  'nitro) opera' => 'OP',
143  'opera' => 'OP',
144  'rekonq' => 'RK',
145  // Safari
146  'safari' => 'SF',
147  'applewebkit' => 'SF',
148  'titanium' => 'TI',
149  'webos' => 'WO',
150  'webpro' => 'WP',
151  );
152  // browser family (by layout engine)
153  static protected $browserType = array(
154  'ie' => array('IE'),
155  'gecko' => array('NS', 'PX', 'FF', 'FB', 'CA', 'GA', 'KM', 'MO', 'SM', 'CO', 'FE', 'KP', 'KZ', 'TB'),
156  'khtml' => array('KO'),
157  'webkit' => array('SF', 'CH', 'OW', 'AR', 'EP', 'FL', 'WO', 'AB', 'IR', 'CS', 'FD', 'HA', 'MI', 'GE', 'DF', 'BB', 'BP', 'TI', 'CF', 'RK', 'B2', 'NF'),
158  'opera' => array('OP'),
159  );
160  // WebKit version numbers to Apple Safari version numbers (if Version/X.Y.Z not present)
161  static protected $safariVersions = array(
162  '536.25' => array('6', '0'),
163  '534.48' => array('5', '1'),
164  '533.16' => array('5', '0'),
165  '533.4' => array('4', '1'),
166  '526.11.2' => array('4', '0'),
167  '525.26' => array('3', '2'),
168  '525.13' => array('3', '1'),
169  '522.11' => array('3', '0'),
170  '412' => array('2', '0'),
171  '312' => array('1', '3'),
172  '125' => array('1', '2'),
173  '100' => array('1', '1'),
174  '85' => array('1', '0'),
175  '73' => array('0', '9'),
176  '48' => array('0', '8'),
177  );
178  // OmniWeb build numbers to OmniWeb version numbers (if Version/X.Y.Z not present)
179  static protected $omniWebVersions = array(
180  '622.15' => array('5', '11'),
181  '622.10' => array('5', '10'),
182  '622.8' => array('5', '9'),
183  '622.3' => array('5', '8'),
184  '621' => array('5', '7'),
185  '613' => array('5', '6'),
186  '607' => array('5', '5'),
187  '563.34' => array('5', '1'),
188  '558.36' => array('5', '0'),
189  '496' => array('4', '5'),
190  );
191  // OS regex => OS ID
192  static protected $operatingSystems = array(
193  'Android' => 'AND',
194  'Maemo' => 'MAE',
195  'CrOS ' => 'LIN',
196  'Linux' => 'LIN',
197  'Xbox' => 'XBX',
198  // workaround for vendors who changed the WinPhone 7 user agent
199  'WP7' => 'WPH',
200  'CYGWIN_NT-6.2' => 'WI8',
201  'Windows NT 6.2' => 'WI8',
202  'Windows NT 6.3' => 'WI8',
203  'Windows 8' => 'WI8',
204  'CYGWIN_NT-6.1' => 'WI7',
205  'Windows NT 6.1' => 'WI7',
206  'Windows 7' => 'WI7',
207  'CYGWIN_NT-6.0' => 'WVI',
208  'Windows NT 6.0' => 'WVI',
209  'Windows Vista' => 'WVI',
210  'CYGWIN_NT-5.2' => 'WS3',
211  'Windows NT 5.2' => 'WS3',
212  'Windows Server 2003 / XP x64' => 'WS3',
213  'CYGWIN_NT-5.1' => 'WXP',
214  'Windows NT 5.1' => 'WXP',
215  'Windows XP' => 'WXP',
216  'CYGWIN_NT-5.0' => 'W2K',
217  'Windows NT 5.0' => 'W2K',
218  'Windows 2000' => 'W2K',
219  'CYGWIN_NT-4.0' => 'WNT',
220  'Windows NT 4.0' => 'WNT',
221  'WinNT' => 'WNT',
222  'Windows NT' => 'WNT',
223  'CYGWIN_ME-4.90' => 'WME',
224  'Win 9x 4.90' => 'WME',
225  'Windows ME' => 'WME',
226  'CYGWIN_98-4.10' => 'W98',
227  'Win98' => 'W98',
228  'Windows 98' => 'W98',
229  'CYGWIN_95-4.0' => 'W95',
230  'Win32' => 'W95',
231  'Win95' => 'W95',
232  'Windows 95' => 'W95',
233  // Windows Phone OS 7 and above
234  'Windows Phone OS' => 'WPH',
235  // Windows Mobile 6.x and some later versions of Windows Mobile 5
236  'IEMobile' => 'WMO', // fallback
237  'Windows Mobile' => 'WMO',
238  // Windows CE, Pocket PC, and Windows Mobile 5 are indistinguishable without vendor/device specific detection
239  'Windows CE' => 'WCE',
240  'iPod' => 'IPD',
241  'iPad' => 'IPA',
242  'iPhone' => 'IPH',
243  // 'iOS' => 'IOS',
244  'Darwin' => 'MAC',
245  'Macintosh' => 'MAC',
246  'Power Macintosh' => 'MAC',
247  'Mac_PowerPC' => 'MAC',
248  'Mac PPC' => 'MAC',
249  'PPC' => 'MAC',
250  'Mac PowerPC' => 'MAC',
251  'Mac OS' => 'MAC',
252  'webOS' => 'WOS',
253  'Palm webOS' => 'WOS',
254  'PalmOS' => 'POS',
255  'Palm OS' => 'POS',
256  'BB10' => 'BBX',
257  'BlackBerry' => 'BLB',
258  'RIM Tablet OS' => 'QNX',
259  'QNX' => 'QNX',
260  'SymbOS' => 'SYM',
261  'Symbian OS' => 'SYM',
262  'SymbianOS' => 'SYM',
263  'bada' => 'SBA',
264  'SunOS' => 'SOS',
265  'AIX' => 'AIX',
266  'HP-UX' => 'HPX',
267  'OpenVMS' => 'VMS',
268  'FreeBSD' => 'BSD',
269  'NetBSD' => 'NBS',
270  'OpenBSD' => 'OBS',
271  'DragonFly' => 'DFB',
272  'Syllable' => 'SYL',
273  'Nintendo WiiU' => 'WIU',
274  'Nintendo Wii' => 'WII',
275  'Nitro' => 'NDS',
276  'Nintendo DSi' => 'DSI',
277  'Nintendo DS' => 'NDS',
278  'Nintendo 3DS' => '3DS',
279  'PlayStation Vita' => 'PSV',
280  'PlayStation Portable' => 'PSP',
281  'PlayStation 3' => 'PS3',
282  'IRIX' => 'IRI',
283  'OSF1' => 'T64',
284  'OS/2' => 'OS2',
285  'BEOS' => 'BEO',
286  'Amiga' => 'AMI',
287  'AmigaOS' => 'AMI',
288  );
289  // os family
290  // NOTE: The keys in this array are used by plugins/UserSettings/functions.php . Any changes
291  // made here should also be made in that file.
292  static protected $osType = array(
293  'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95'),
294  'Linux' => array('LIN'),
295  'Mac' => array('MAC'),
296  'iOS' => array('IPD', 'IPA', 'IPH'),
297  'Android' => array('AND'),
298  'Windows Mobile' => array('WPH', 'WMO', 'WCE'),
299  'Gaming Console' => array('WII', 'WIU', 'PS3', 'XBX'),
300  'Mobile Gaming Console' => array('PSP', 'PSV', 'NDS', 'DSI', '3DS'),
301  'Unix' => array('SOS', 'AIX', 'HP-UX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
302  'Other Mobile' => array('MAE', 'WOS', 'POS', 'BLB', 'QNX', 'SYM', 'SBA'),
303  'Other' => array('VMS', 'OS2', 'BEOS', 'AMI')
304  );
305  static protected $browserIdToName;
306  static protected $browserIdToShortName;
307  static protected $operatingSystemsIdToName;
308  static protected $operatingSystemsIdToShortName;
309  static private $init = false;
320  static public function getOperatingSystem($userAgent)
321  {
322  $userAgent = self::cleanupUserAgent($userAgent);
323  self::init();
324  $info = array(
325  'id' => '',
326  'name' => '',
327  'short_name' => '',
328  );
329  foreach (self::$operatingSystems as $key => $value) {
330  if (stristr($userAgent, $key) !== false) {
331  $info['id'] = $value;
332  break;
333  }
334  }
335  if (empty($info['id'])) {
336  return false;
337  }
338  $info['name'] = self::getOperatingSystemNameFromId($info['id']);
339  $info['short_name'] = self::getOperatingSystemShortNameFromId($info['id']);
340  return $info;
341  }
342  static protected function cleanupUserAgent($userAgent)
343  {
344  // in case value is URL encoded
345  return urldecode($userAgent);
346  }
361  static public function getBrowser($userAgent)
362  {
363  $userAgent = self::cleanupUserAgent($userAgent);
364  self::init();
365  $info = array(
366  'id' => '',
367  'name' => '',
368  'short_name' => '',
369  'major_number' => '',
370  'minor_number' => '',
371  'version' => '',
372  );
373  $browsers = self::$browsers;
374  // derivative browsers often clone the base browser's useragent
375  unset($browsers['firefox']);
376  unset($browsers['mozilla']);
377  unset($browsers['safari']);
378  unset($browsers['applewebkit']);
379  $browsersPattern = str_replace(')', '\)', implode('|', array_keys($browsers)));
380  $results = array();
381  // Misbehaving IE add-ons
382  $userAgent = preg_replace('/[; ]Mozilla\/[0-9.]+ \([^)]+\)/', '', $userAgent);
383  // Clean-up BlackBerry device UAs
384  $userAgent = preg_replace('~^BlackBerry\d+/~', 'BlackBerry/', $userAgent);
385  if (preg_match_all("/($browsersPattern)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
386  || (strpos($userAgent, 'Shiira') === false && preg_match_all("/(firefox|thunderbird|safari)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results))
387  || preg_match_all("/(applewebkit)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
388  || preg_match_all("/^(mozilla)\/([0-9]+)([\.0-9a-z-]+)?(?: \[[a-z]{2}\])? (?:\([^)]*\))$/i", $userAgent, $results)
389  || preg_match_all("/^(mozilla)\/[0-9]+(?:[\.0-9a-z-]+)?\s\(.* rv:([0-9]+)([.0-9a-z]+)\) gecko(\/[0-9]{8}|$)(?:.*)/i", $userAgent, $results)
390  || (strpos($userAgent, 'Nintendo 3DS') !== false && preg_match_all("/^(mozilla).*version\/([0-9]+)([.0-9a-z]+)?/i", $userAgent, $results))
391  ) {
392  // browser code (usually the first match)
393  $count = 0;
394  $info['id'] = self::$browsers[strtolower($results[1][0])];
395  // sometimes there's a better match at the end
396  if (strpos($userAgent, 'chromeframe') !== false) {
397  $count = count($results[0]) - 1;
398  $info['id'] = 'CF';
399  } elseif (($info['id'] == 'IE' || $info['id'] == 'LX') && (count($results[0]) > 1)) {
400  $count = count($results[0]) - 1;
401  $info['id'] = self::$browsers[strtolower($results[1][$count])];
402  }
403  // Netscape fix
404  if ($info['id'] == 'MO' && $count == 0) {
405  if (stripos($userAgent, 'PlayStation') !== false) {
406  return false;
407  }
408  if (strpos($userAgent, 'Nintendo 3DS') !== false) {
409  $info['id'] = 'NF';
410  } elseif (count($results) == 4) {
411  $info['id'] = 'NS';
412  }
413  } // BlackBerry devices
414  elseif (strpos($userAgent, 'BlackBerry') !== false) {
415  $info['id'] = 'BB';
416  } elseif (strpos($userAgent, 'RIM Tablet OS') !== false) {
417  $info['id'] = 'BP';
418  } elseif (strpos($userAgent, 'BB10') !== false) {
419  $info['id'] = 'B2';
420  } elseif (strpos($userAgent, 'Playstation Vita') !== false) {
421  $info['id'] = 'NF';
422  if (preg_match_all("/(silk)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults)) {
423  $results = $newResults;
424  $count = count($results[0]) - 1;
425  }
426  }
427  // Version/X.Y.Z override
428  if (preg_match_all("/(version)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults)) {
429  $results = $newResults;
430  $count = count($results[0]) - 1;
431  }
432  // major version number (1 in mozilla 1.7)
433  $info['major_number'] = $results[2][$count];
434  // is an minor version number ? If not, 0
435  $match = array();
436  preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i', $results[3][$count], $match);
437  if (isset($match[1])) {
438  // find minor version number (7 in mozilla 1.7, 9 in firefox 0.9.3)
439  $dot = strpos(substr($match[1], 1), '.');
440  if ($dot !== false) {
441  $info['minor_number'] = substr($match[1], 1, $dot);
442  } else {
443  $info['minor_number'] = substr($match[1], 1);
444  }
445  } else {
446  $info['minor_number'] = '0';
447  }
448  $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
449  // IE compatibility mode
450  if ($info['id'] == 'IE'
451  && (strncmp($userAgent, 'Mozilla/4.0', 11) == 0 || strncmp($userAgent, 'Mozilla/5.0', 11) == 0)
452  && preg_match('~ Trident/([0-9]+)\.[0-9]+~', $userAgent, $tridentVersion)
453  ) {
454  $info['major_number'] = $tridentVersion[1] + 4;
455  $info['minor_number'] = '0';
456  $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
457  }
458  // Safari fix
459  if ($info['id'] == 'SF') {
460  foreach (self::$safariVersions as $buildVersion => $productVersion) {
461  if (version_compare($info['version'], $buildVersion) >= 0) {
462  $info['major_number'] = $productVersion[0];
463  $info['minor_number'] = $productVersion[1];
464  $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
465  break;
466  }
467  }
468  }
469  // OmniWeb fix
470  if ($info['id'] == 'OW') {
471  foreach (self::$omniWebVersions as $buildVersion => $productVersion) {
472  if (version_compare($info['version'], $buildVersion) >= 0) {
473  $info['major_number'] = $productVersion[0];
474  $info['minor_number'] = $productVersion[1];
475  $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
476  break;
477  }
478  }
479  }
480  // SeaMonkey fix
481  if ($info['id'] == 'MO' && $info['version'] == '1.9') {
482  $info['id'] = 'SM';
483  }
484  $info['name'] = self::getBrowserNameFromId($info['id']);
485  $info['short_name'] = self::getBrowserShortNameFromId($info['id']);
486  return $info;
487  }
488  return false;
489  }
490  static protected function init()
491  {
492  if (self::$init) {
493  return;
494  }
495  self::$init = true;
496  // init browser names and short names
497  self::$browserIdToName = array_map('ucwords', array_flip(self::$browsers));
498  self::$browserIdToName['AB'] = 'ABrowse';
499  self::$browserIdToName['AV'] = 'AmigaVoyager';
500  self::$browserIdToName['AW'] = 'Amiga AWeb';
501  self::$browserIdToName['BB'] = 'BlackBerry';
502  self::$browserIdToName['BP'] = 'PlayBook';
503  self::$browserIdToName['B2'] = 'BlackBerry';
504  self::$browserIdToName['BX'] = 'BrowseX';
505  self::$browserIdToName['CF'] = 'Chrome Frame';
506  self::$browserIdToName['CO'] = 'CometBird';
507  self::$browserIdToName['EL'] = 'ELinks';
508  self::$browserIdToName['FF'] = 'Firefox';
509  self::$browserIdToName['HJ'] = 'HotJava';
510  self::$browserIdToName['IB'] = 'IBrowse';
511  self::$browserIdToName['IC'] = 'iCab';
512  self::$browserIdToName['KM'] = 'K-Meleon';
513  self::$browserIdToName['MC'] = 'NCSA Mosaic';
514  self::$browserIdToName['NF'] = 'NetFront';
515  self::$browserIdToName['OW'] = 'OmniWeb';
516  self::$browserIdToName['SF'] = 'Safari';
517  self::$browserIdToName['SM'] = 'SeaMonkey';
518  self::$browserIdToName['WO'] = 'Palm webOS';
519  self::$browserIdToName['WP'] = 'WebPro';
520  self::$browserIdToShortName = self::$browserIdToName;
521  self::$browserIdToShortName['AW'] = 'AWeb';
522  self::$browserIdToShortName['FB'] = 'Firebird';
523  self::$browserIdToShortName['IE'] = 'IE';
524  self::$browserIdToShortName['MC'] = 'Mosaic';
525  self::$browserIdToShortName['BP'] = 'PlayBook';
526  self::$browserIdToShortName['WO'] = 'webOS';
527  // init OS names and short names
528  $operatingSystemsIdToName = array(
529  'IPD' => 'iPod',
530  'IPA' => 'iPad',
531  'WME' => 'Windows Me',
532  'BEO' => 'BeOS',
533  'T64' => 'Tru64',
534  'NDS' => 'Nintendo DS',
535  'WIU' => 'Nintendo Wii U',
536  '3DS' => 'Nintendo 3DS',
537  // These are for BC purposes only
538  'W75' => 'WinPhone 7.5',
539  'WP7' => 'WinPhone 7',
540  'W65' => 'WinMo 6.5',
541  'W61' => 'WinMo 6.1',
542  );
543  self::$operatingSystemsIdToName = array_merge(array_flip(self::$operatingSystems), $operatingSystemsIdToName);
544  $operatingSystemsIdToShortName = array(
545  'PS3' => 'PS3',
546  'PSP' => 'PSP',
547  'WII' => 'Wii',
548  'WIU' => 'Wii U',
549  'NDS' => 'DS',
550  'DSI' => 'DSi',
551  '3DS' => '3DS',
552  'PSV' => 'PS Vita',
553  'WI8' => 'Win 8',
554  'WI7' => 'Win 7',
555  'WVI' => 'Win Vista',
556  'WS3' => 'Win S2003',
557  'WXP' => 'Win XP',
558  'W98' => 'Win 98',
559  'W2K' => 'Win 2000',
560  'WNT' => 'Win NT',
561  'WME' => 'Win Me',
562  'W95' => 'Win 95',
563  'WPH' => 'WinPhone',
564  'WMO' => 'WinMo',
565  'WCE' => 'Win CE',
566  'WOS' => 'webOS',
567  'UNK' => 'Unknown',
568  );
569  self::$operatingSystemsIdToShortName = array_merge(self::$operatingSystemsIdToName, $operatingSystemsIdToShortName);
570  }
571  static public function getBrowserNameFromId($browserId)
572  {
573  self::init();
574  if (isset(self::$browserIdToName[$browserId])) {
575  return self::$browserIdToName[$browserId];
576  }
577  if(class_exists('DeviceDetector\\Parser\\Client\\Browser')) {
578  $browsers = DeviceDetector\Parser\Client\Browser::getAvailableBrowsers();
579  if( array_key_exists($browserId, $browsers)) {
580  return $browsers[$browserId];
581  }
582  }
583  return false;
584  }
585  static public function getBrowserShortNameFromId($browserId)
586  {
587  self::init();
588  if (isset(self::$browserIdToShortName[$browserId])) {
589  return self::$browserIdToShortName[$browserId];
590  }
591  return false;
592  }
593  static public function getBrowserFamilyFromId($browserId)
594  {
595  self::init();
596  $familyNameToUse = 'unknown';
597  foreach (self::$browserType as $familyName => $aBrowsers) {
598  if (in_array($browserId, $aBrowsers)) {
599  $familyNameToUse = $familyName;
600  break;
601  }
602  }
603  return $familyNameToUse;
604  }
605  static public function getOperatingSystemNameFromId($osId)
606  {
607  self::init();
608  if (isset(self::$operatingSystemsIdToName[$osId])) {
609  return self::$operatingSystemsIdToName[$osId];
610  }
611  if(class_exists('DeviceDetector\\Parser\\OperatingSystem')) {
612  if ($osId == 'BOT') {
613  return 'Bot';
614  }
615  return DeviceDetector\Parser\OperatingSystem::getNameFromId($osId);
616  }
617  return false;
618  }
619  static public function getOperatingSystemShortNameFromId($osId)
620  {
621  self::init();
622  if (isset(self::$operatingSystemsIdToShortName[$osId])) {
623  return self::$operatingSystemsIdToShortName[$osId];
624  }
625  return false;
626  }
627  static public function getOperatingSystemIdFromName($osName)
628  {
629  return isset(self::$operatingSystems[$osName]) ? self::$operatingSystems[$osName] : false;
630  }
631  static public function getOperatingSystemFamilyFromId($osId)
632  {
633  self::init();
634  foreach (self::$osType as $familyName => $aSystems) {
635  if (in_array($osId, $aSystems)) {
636  return $familyName;
637  }
638  }
639  return 'unknown';
640  }
641 }