created > time() - 60*60*24)) { // Found a fresh entry in the cache $browserinfo = unserialize($cache->data); } else { // Note the 'backwards' use of LIKE - the useragent column contains // the wildcarded pattern to match against our full-length string // The ORDER BY chooses the most-specific matching pattern $browserinfo = db_fetch_object(db_query_range( "SELECT * from {browscap} WHERE '%s' LIKE useragent ORDER BY LENGTH(useragent) DESC", $useragent, 0, 1)); // A couple of fieldnames not in our database, provided for // compatibility with PHP's get_browser() $browserinfo->browser_name_pattern = strtr($browserinfo->useragent, '%_', '*?'); $browserinfo->tables = $browserinfo->htmltables; cache_set($cacheid, serialize($browserinfo)); } if ($return_array) { return get_object_vars($browserinfo); } else { return $browserinfo; } } /** * Implementation of hook_cron(). */ function browscap_cron() { // Has it been a week since the last (attempt to) import? $last_imported = variable_get('browscap_imported', 0); if (($last_imported + 60*60*24*7) < time()) { _browscap_import(); variable_set('browscap_imported', time()); } } // A numeric interpretation of browscap.csv's TRUE/FALSE/default fields function _browscap_boolean($value) { switch ($value) { case 'TRUE': case 'true': return 1; case 'FALSE': case 'false': case 'default': default: return 0; } } /** * If there's a new version of browscap.csv, fetch it and update the * database. */ function _browscap_import() { // Politely check the version for updates before fetching the file $versionpage = drupal_http_request('http://www.garykeith.com/browsers/version.asp'); if ($versionpage->error) { watchdog('browscap', "Couldn't check version: ".$versionpage->error); return; } $browscapversion = trim($versionpage->data); $oldversion = variable_get('browscap_version', 'Never fetched'); if ($browscapversion == $oldversion) { // No update, nothing to do here watchdog('browscap', 'No new version of browscap to import'); return; } // Fetch the new version, and dump it in the temp directory $server = $_SERVER['SERVER_NAME']; $path = variable_get('file_directory_temp', '/tmp'); $browscapfile = "$path/browscap_$server.csv"; // If we can, download the zipped version and extract the file if (is_callable('zip_open')) { $browscapzipfile = "$path/csv_browscap_$server.zip"; $browscapzip = drupal_http_request('http://www.garykeith.com/browsers/stream.asp?CSV_BrowsCapZIP'); if ($browscapzip->error or !trim($browscapzip->data)) { watchdog('browscap', "Couldn't retrieve updated browscap: ".$browscapzip->error); return; } $browscapzipfp = fopen($browscapzipfile, "w"); fwrite($browscapzipfp, $browscapzip->data); fclose($browscapzipfp); $zip = zip_open($browscapzipfile); if ($zip) { while ($zip_entry = zip_read($zip)) { if (zip_entry_name($zip_entry) == 'browscap.csv') { if (zip_entry_open($zip, $zip_entry, 'rb')) { $browscapfp = fopen($browscapfile, 'w'); fwrite($browscapfp, zip_entry_read($zip_entry, zip_entry_filesize($zip_entry))); fclose($browscapfp); break; } } } zip_close($zip); } unlink($browscapzipfile); // Can't handle zip, get the unzipped version } else { $browscap = drupal_http_request('http://www.garykeith.com/browsers/stream.asp?BrowsCapCSV'); if ($browscap->error or !trim($browscap->data)) { watchdog('browscap', "Couldn't retrieve updated browscap: ".$browscap->error); return; } $browscapfp = fopen($browscapfile, "w"); fwrite($browscapfp, $browscap->data); fclose($browscapfp); } // Parse the .csv file, importing each row (except the first two) // into the {browscap} table $browscapfp = fopen($browscapfile, 'r'); if ($browscapfp) { // Ignore the first two rows (column headers & version info) fgetcsv($browscapfp, 1000); fgetcsv($browscapfp, 1000); while (($browserinfo = fgetcsv($browscapfp, 1000)) != FALSE) { // Strip brackets $useragent = substr($browserinfo[0], 1, -1); // Replace wildcards with SQL equivalents $useragent = strtr($useragent, '*?', '%_'); $parent = $browserinfo[1]; $browser = $browserinfo[2]; $version = $browserinfo[3]; $majorversion = $browserinfo[4]; $minorversion = $browserinfo[5]; $platform = $browserinfo[6]; $authenticodeupdate = $browserinfo[7]; if ($browserinfo[8] == 'default') { $cssversion = -1; } else { $cssversion = $browserinfo[8]; } $frames = _browscap_boolean($browserinfo[9]); $iframes = _browscap_boolean($browserinfo[10]); $htmltables = _browscap_boolean($browserinfo[11]); $cookies = _browscap_boolean($browserinfo[12]); $backgroundsounds = _browscap_boolean($browserinfo[13]); $vbscript = _browscap_boolean($browserinfo[14]); $javascript = _browscap_boolean($browserinfo[15]); $javaapplets = _browscap_boolean($browserinfo[16]); $activexcontrols = _browscap_boolean($browserinfo[17]); $cdf = _browscap_boolean($browserinfo[18]); $aol = _browscap_boolean($browserinfo[19]); $beta = _browscap_boolean($browserinfo[20]); $win16 = _browscap_boolean($browserinfo[21]); $crawler = _browscap_boolean($browserinfo[22]); $stripper = _browscap_boolean($browserinfo[23]); $wap = _browscap_boolean($browserinfo[24]); $netclr = _browscap_boolean($browserinfo[25]); db_query('REPLACE {browscap} (useragent,parent,browser,version,'. 'majorversion,minorversion,platform,authenticodeupdate,'. 'cssversion,frames,iframes,htmltables,cookies,backgroundsounds,'. 'vbscript,javascript,javaapplets,activexcontrols,cdf,aol,'. 'beta,win16,crawler,stripper,wap,netclr) '. "VALUES('%s','%s','%s','%s','%s','%s','%s','%s',%d,%d,%d,%d,%d,". '%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)', $useragent, $parent, $browser, $version, $majorversion, $minorversion, $platform, $authenticodeupdate, $cssversion, $frames, $iframes, $htmltables, $cookies, $backgroundsounds, $vbscript, $javascript, $javaapplets, $activexcontrols, $cdf, $aol, $beta, $win16, $crawler, $stripper, $wap, $netclr); } fclose($browscapfp); unlink($browscapfile); // Phase 2 - for every entry which isn't a top-level entry, plug // in the data from its parent $query = 'SELECT useragent,parent from {browscap} WHERE useragent <> parent'; $result = db_query($query); while ($destrow = db_fetch_object($result)) { $query = "SELECT * FROM {browscap} WHERE useragent='%s'"; $srcrow = db_fetch_object(db_query($query, $destrow->parent)); db_query("UPDATE {browscap} SET browser='%s',version='%s',". "majorversion='%s',minorversion='%s',platform='%s',". "authenticodeupdate='%s',cssversion=%d,frames=%d,iframes=%d,". "htmltables=%d,cookies=%d,backgroundsounds=%d,vbscript=%d,". "javascript=%d,javaapplets=%d,activexcontrols=%d,cdf=%d,". "aol=%d,beta=%d,win16=%d,crawler=%d,stripper=%d,wap=%d,netclr=%d ". "WHERE useragent='%s'", $srcrow->browser,$srcrow->version, $srcrow->majorversion,$srcrow->minorversion,$srcrow->platform, $srcrow->authenticodeupdate,$srcrow->cssversion,$srcrow->frames,$srcrow->iframes, $srcrow->htmltables,$srcrow->cookies,$srcrow->backgroundsounds,$srcrow->vbscript, $srcrow->javascript,$srcrow->javaapplets,$srcrow->activexcontrols,$srcrow->cdf, $srcrow->aol,$srcrow->beta,$srcrow->win16,$srcrow->crawler,$srcrow->stripper,$srcrow->wap,$srcrow->netclr, $destrow->useragent); } // All done updating the browscap info - invalidate cached data // from the last version, and record the version we're currently // using cache_clear_all('browscap:', TRUE); variable_set('browscap_version', $browscapversion); watchdog('browscap', "New version of browscap imported: $browscapversion"); } } /** * Implementation of hook_exit(). * * Keep tabs on browsers that visit */ function browscap_exit() { // No point if statistics aren't enabled if (!module_exist('statistics')) { return; } // If monitoring is enabled, record the browser if (variable_get('browscap_monitor', FALSE)) { $browser = browscap_get_browser(); $browserstring = substr(trim($browser->parent), 0, 255); if ($browserstring == '' or $browserstring == 'Default Browser') { $browserstring = trim($_SERVER['HTTP_USER_AGENT']); } db_query("UPDATE {browscap_statistics} SET counter = counter + 1, is_crawler=%d ". "WHERE parent='%s'", $browser->crawler, $browserstring); // If we affected 0 rows, this is the first time we've seen this browser if (!db_affected_rows()) { // We must create a new row to store counters for the new browser. db_query('INSERT INTO {browscap_statistics} (parent,counter,is_crawler) '. "VALUES('%s', 1, %d)", $browserstring, $browser->crawler); } } } /* * Undo a recorded browser visit by request * * This function serves the statistics_filter module, enabling it * to ignore visits from specified roles. */ function browscap_unmonitor() { // No point if statistics aren't enabled if (!module_exist('statistics')) { return; } // If monitoring is enabled, unrecord the browser if (variable_get('browscap_monitor', FALSE)) { $browser = browscap_get_browser(); $browserstring = trim($browser->parent); if ($browserstring == '' or $browserstring == 'Default Browser') { $browserstring = trim($_SERVER['HTTP_USER_AGENT']); } db_query("UPDATE {browscap_statistics} SET counter = counter - 1, is_crawler=%d ". "WHERE parent='%s'", $browser->crawler, $browserstring); } } /** * Implementation of hook_perm(). * * @return array */ function browscap_perm() { return array('administer browscap'); } /** * Turn monitor on or off * * @return array */ function browscap_settings() { // Restrict administration of this module if (user_access('administer browscap') === false) { return drupal_access_denied(); } $form['browscap_monitor'] = array( '#type' => 'checkbox', '#prefix' => t('
Browscap data current as of %fileversion
', array('%fileversion' => variable_get('browscap_version', 'Never fetched'))), '#title' => t('Monitor browsers'), '#default_value' => variable_get('browscap_monitor', FALSE), '#description' => t('Monitor all user agents visiting the site.') ); return $form; } /** * Implementation of hook_menu(). * * @return array */ function browscap_menu($may_cache) { $items = array(); if ($may_cache && variable_get('browscap_monitor', FALSE)) { $access = user_access('access statistics'); $items[] = array('path' => 'admin/logs/useragents', 'title' => t('user agents'), 'callback' => 'browscap_top_useragents', 'access' => $access, 'weight' => 5); $items[] = array('path' => 'admin/logs/useragents/browsers', 'title' => t('browsers'), 'access' => $access); $items[] = array('path' => 'admin/logs/useragents/crawlers', 'title' => t('crawlers'), 'access' => $access); } return $items; } /** * Menu callback; presents the user agents monitoring page. * * @param $view * - "browsers": Only display "real" browsers * - "crawlers": Only display search engine crawlers * - "all": Display all user agents. */ function browscap_top_useragents($view = 'all') { if ($view == 'all') { $result = db_query('SELECT SUM(counter) FROM {browscap_statistics}'); $total = db_result($result); if (!$total) $total = 1; $query = "SELECT parent,counter,(100*counter)/$total as percent,is_crawler FROM {browscap_statistics}"; $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics}'; $title = t('Top user agents'); $header = array( array('data' => t('User agent'), 'field' => 'parent'), array('data' => t('Count'), 'field' => 'counter', 'sort' => 'desc'), array('data' => t('Percent'), 'field' => 'percent'), array('data' => t('Crawler?'), 'field' => 'is_crawler') ); } elseif ($view == 'browsers') { $result = db_query('SELECT SUM(counter) FROM {browscap_statistics} WHERE is_crawler=0'); $total = db_result($result); if (!$total) $total = 1; $query = "SELECT parent,counter,(100*counter)/$total as percent FROM {browscap_statistics} WHERE is_crawler=0"; $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics} WHERE is_crawler=0'; $title = t('Top browsers'); $header = array( array('data' => t('Browser'), 'field' => 'parent'), array('data' => t('Count'), 'field' => 'counter', 'sort' => 'desc'), array('data' => t('Percent'), 'field' => 'percent') ); } else { $result = db_query('SELECT SUM(counter) FROM {browscap_statistics} WHERE is_crawler=1'); $total = db_result($result); if (!$total) $total = 1; $query = "SELECT parent,counter,(100*counter)/$total as percent FROM {browscap_statistics} WHERE is_crawler=1"; $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics} WHERE is_crawler=1'; $title = t('Top crawlers'); $header = array( array('data' => t('Crawler'), 'field' => 'parent'), array('data' => t('Count'), 'field' => 'counter', 'sort' => 'desc'), array('data' => t('Percent'), 'field' => 'percent') ); } $query .= tablesort_sql($header); $result = pager_query($query, 50, 0, $query_cnt); while ($useragent = db_fetch_object($result)) { if ($view == 'all') { if ($useragent->is_crawler) { $is_crawler = 'Yes'; } else { $is_crawler = 'No'; } $rows[] = array($useragent->parent, $useragent->counter, $useragent->percent, $is_crawler); } else { $rows[] = array($useragent->parent, $useragent->counter, $useragent->percent); } } if ($pager = theme('pager', NULL, 50, 0)) { $rows[] = array(array('data' => $pager, 'colspan' => 2)); } $output .= theme('table', $header, $rows); print theme('page', $output, $title); } ?>