0 ) { // dirty trick: author is child of authors, contributor is child of contributors while ($t = each($attr)) { // example: $thiswurfl["authors"]["author"]["name"]="Andrea Trasatti"; $thiswurfl[$name."s"][$name][$attr["name"]][$t[0]]=$t[1]; } } break; case "device": if ( ($attr["user_agent"] == "" || ! $attr["user_agent"]) && $attr["id"]!="generic" ) { die("No user agent and I am not generic!! id=".$attr["id"]." HELP"); } if ( sizeof($attr) > 0 ) { while ($t = each($attr)) { // example: $thiswurfl["devices"]["ericsson_generic"]["fall_back"]="generic"; $thiswurfl["devices"][$attr["id"]][$t[0]]=$t[1]; } } $curr_device=$attr["id"]; break; case "group": // this HAS NOT to be executed or we will define the id as string and then reuse it as array: ERROR //$thiswurfl["devices"][$curr_device][$attr["id"]]=$attr["id"]; $curr_group=$attr["id"]; break; case "capability": if ( $attr["value"] == 'true' ) { $value = true; } else if ( $attr["value"] == 'false' ) { $value = false; } else { $value = $attr["value"]; $intval = intval($value); if ( strcmp($value, $intval) == 0 ) { $value = $intval; } } $thiswurfl["devices"][$curr_device][$curr_group][$attr["name"]]=$value; break; case "devices": // This might look useless but it's good when you want to parse only the devices and skip the rest if ( !isset($thiswurfl["devices"]) ) $thiswurfl["devices"]=array(); break; case "wurfl_patch": // opening tag of the patch file case "wurfl": // opening tag of the WURFL, nothing to do break; case "default": // unknown events are not welcome die($name." is an unknown event
"); break; } } function endElement($parser, $name) { global $wurfl, $wurfl_patch, $curr_event, $curr_device, $curr_group, $wurfl_type; if($wurfl_type == "main"){ $thiswurfl = &$wurfl; }elseif($wurfl_type == "patch"){ $thiswurfl = &$wurfl_patch; }else{ die("Invalid wurfl_type."); } switch ($name) { case "group": break; case "device": break; case "ver": case "last_updated": case "official_url": case "statement": $thiswurfl[$name]=$curr_event; // referring to $GLOBALS to unset curr_event because unset will not destroy // a global variable unless called in this way unset($GLOBALS['curr_event']); break; default: break; } } function characterData($parser, $data) { global $curr_event; if (trim($data) != "" ) { $curr_event.=$data; //echo "data=".$data."
\n"; } } function emptyWurflDevTable($tablename){ // version 1.5.2, added KEY on actual device root for sorting $droptable = "DROP TABLE IF EXISTS ".$tablename; $createtable = "CREATE TABLE `".$tablename."` ( `deviceID` varchar(128) binary NOT NULL default '', `user_agent` varchar(255) default NULL, `fall_back` varchar(128) default NULL, `actual_device_root` tinyint(1) default '0', `capabilities` mediumtext, PRIMARY KEY (`deviceID`), KEY `fallback` (`fall_back`), KEY `useragent` (`user_agent`), KEY `dev_root` (`actual_device_root`) ) TYPE=".DB_TYPE; $emptytable = "DELETE FROM ".$tablename; if(DB_EMPTY_METHOD == "DROP_CREATE"){ if(!mysql_query($droptable))return(false); if(!mysql_query($createtable))return(false); }else{ if(!mysql_query($emptytable))return(false); } return(true); } function load_wurfl($filetype="main",$source="local") { global $wurfl, $wurfl_patch, $curr_event, $curr_device, $curr_group, $fp_cache, $check_patch_params, $checkpatch_result, $wurfl_type, $new_version; $wurfl_type = $filetype; if($wurfl_type == "main"){ $devtable = DB_DEVICE_TABLE.DB_TEMP_EXT; $prodtable = DB_DEVICE_TABLE; $wurflfile = WURFL_FILE; $thiswurfl = &$wurfl; }elseif($wurfl_type == "patch"){ $devtable = DB_PATCH_TABLE.DB_TEMP_EXT; $prodtable = DB_PATCH_TABLE; $wurflfile = WURFL_PATCH_FILE; $thiswurfl = &$wurfl_patch; } if(($source == "remote" || $source == "remote_cvs") && $wurfl_type == "main"){ if($source == "remote"){ $dl_url = WURFL_DL_URL; }elseif($source == "remote_cvs"){ $dl_url = WURFL_CVS_URL; } $newfile = DATADIR."dl_wurfl.xml"; echo "Downloading WURFL from $dl_url ...\n
"; flush(); if(!is_writable(DATADIR)){ toLog('update',"no write permissions for data directory",LOG_ERR); die("Fatal Error: The data directory is not writable. (".DATADIR.")

Please make the data directory writable by the user that runs the webserver process, in Linux this command would do the trick if you're not too concered about security:
chmod -R 777 ".DATADIR."
"); } @ini_set('user_agent', "PHP/Tera-WURFL_$version"); $dl_wurfl = file_get_contents($dl_url); file_put_contents($newfile,$dl_wurfl); $size = filesize($newfile); echo "done ($size bytes)
"; flush(); // ignore this error - I know I'm redefining a constant :P @define("WURFL_FILE",$newfile); $wurflfile = $newfile; } $thiswurfl = array(); $xml_parser = xml_parser_create(); xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "characterData"); if ( !file_exists($wurflfile) ) { toLog('parse', $wurflfile." does not exist",LOG_ERR); die($wurflfile." does not exist"); } if (!($fp = fopen($wurflfile, "r"))) { toLog('parse', "$wurflfile could not be opened for XML input",LOG_ERR); die("$wurflfile could not opened XML input"); } while ($data = fread($fp, 4096)) { if (!xml_parse($xml_parser, $data, feof($fp))) { $errmsg = sprintf("XML error: %s at line %d",xml_error_string(xml_get_error_code($xml_parser)),xml_get_current_line_number($xml_parser)); toLog('parse',$wurflfile." ".$errmsg); die($wurflfile." ".$errmsg); } } fclose($fp); xml_parser_free($xml_parser); $devices = &$thiswurfl["devices"]; // unset($thiswurfl['devices']); // die("
".var_export($thiswurfl,true)."
"); $new_version['ver']=$thiswurfl['ver']; $new_version['last_updated']=$thiswurfl['last_updated']; emptyWurflDevTable($devtable); $processedrows = count($devices); $queries = 0; $insertedrows = 0; $maxquerysize = 0; $insert_errors = array(); $insertcache = array(); $used_ids = array(); foreach($devices as $dev_id => $dev_data) { /* * This will detect duplicate device_ids in the WURFL * if they are different cases. This is important for * databases like MySQL where keys are sorted without regard * for case. * * EDIT: I have disabled this by using 'binary' keys in MySQL * which are case sensitive. * if(in_array(strtolower($dev_id),$used_ids)){ $insert_errors[] = "Duplicate ID omitted: \"$dev_id\""; continue; }else{ $used_ids[] = $dev_id; } */ // $wurfl_agents[$one['user_agent']] = $one['id']; // convert device root to tinyint format (0|1) for db $devroot = (isset($dev_data['actual_device_root']) && $dev_data['actual_device_root'])? 1: 0; if(strlen($dev_data['user_agent']) > 255){ $insert_errors[] = "Warning: user agent too long: \"".$dev['user_agent'].'"'; } if(DB_MULTI_INSERT){ $insertcache[] = sprintf("(%s,%s,%s,%s,%s)", sqlPrep($dev_id), sqlPrep($dev_data['user_agent']), sqlPrep($dev_data['fall_back']), sqlPrep($devroot), sqlPrep(serialize($dev_data)) ); if(count($insertcache) >= DB_MAX_INSERTS){ $query = "INSERT INTO ".$devtable." (deviceID, user_agent, fall_back, actual_device_root, capabilities) VALUES ".implode(",",$insertcache); mysql_query($query) or $insert_errors[] = "DB server reported error on id \"$dev_id\": ".mysql_error(); $insertedrows += mysql_affected_rows(); $insertcache = array(); $queries++; $maxquerysize = (strlen($query)>$maxquerysize)? strlen($query): $maxquerysize; } }else{ $query = sprintf("INSERT INTO ".$devtable." (deviceID, user_agent, fall_back, actual_device_root, capabilities) VALUES (%s,%s,%s,%s,%s)", sqlPrep($dev_id), sqlPrep($dev_data['user_agent']), sqlPrep($dev_data['fall_back']), sqlPrep($devroot), sqlPrep(serialize($dev_data)) ); mysql_query($query) or $insert_errors[]=mysql_error(); $insertedrows += mysql_affected_rows(); $queries++; $maxquerysize = (strlen($query)>$maxquerysize)? strlen($query): $maxquerysize; } } // some records are probably left in the insertcache if(DB_MULTI_INSERT && count($insertcache) > 0){ $query = "INSERT INTO ".$devtable." (deviceID, user_agent, fall_back, actual_device_root, capabilities) VALUES ".implode(",",$insertcache); mysql_query($query) or $insert_errors[]=mysql_error(); $insertedrows += mysql_affected_rows(); $queries++; $maxquerysize = (strlen($query)>$maxquerysize)? strlen($query): $maxquerysize; } // perform sanity checks if(count($insert_errors) > 0){ // problem with update - changes will not be applied echo "There were errors while updating the WURFL. No changes have been made to your database.

"; foreach($insert_errors as $error){ toLog("load_wurfl","error inserting device: ".$error,LOG_ERR); echo "Error inserting device: ".$error."
"; } }else{ // everything seems to be fine - go ahead and overwrite the production device table (or patch table) replace_table($prodtable, $devtable); // now our cache is assumed to be tainted since the underlying data may have changed // I guess it makes sense to clear it even if it's disabled - why not? clear_cache(); if($wurfl_type == "main"){ toLog("load_wurfl","the $wurfl_type database was successfully updated from: $source (".$new_version['ver'].", ".$new_version['last_updated'].")",LOG_WARNING); }else{ toLog("load_wurfl","the $wurfl_type database was successfully updated from: $source",LOG_WARNING); } } return(array("total" => $processedrows, "inserted" => $insertedrows, "errors" => $insert_errors, "queries" => $queries, "maxquerysize" => $maxquerysize, "new_version" => $new_version)); } function replace_table($to_be_replaced, $replacement){ @mysql_query("SELECT COUNT(deviceID) AS num FROM ".$replacement) or die("ERROR: table not found (".$replacement."): ".mysql_error()); mysql_query("DROP TABLE IF EXISTS ".$to_be_replaced); mysql_query("RENAME TABLE `".$replacement."` TO `".$to_be_replaced."`") or die("ERROR: could not rename table - make sure the database user has ALTER permissions! Error message: ".mysql_error()); return(true); } function clear_cache(){ mysql_query("DROP TABLE IF EXISTS ".DB_CACHE_TABLE); $createtable = "CREATE TABLE `".DB_CACHE_TABLE."` ( `user_agent` varchar(255) binary NOT NULL default '', `cache_data` mediumtext NOT NULL, PRIMARY KEY (`user_agent`) ) TYPE=".DB_TYPE; mysql_query($createtable) or die("ERROR: could not create cache table (".DB_CACHE_TABLE."): ".mysql_error()); return(true); } function apply_patch(){ emptyWurflDevTable(DB_HYBRID_TABLE); $queries = 1; $merge_errors = array(); // find total number of patch records $res = mysql_query("SELECT COUNT(deviceID) AS num FROM ".DB_PATCH_TABLE); $processedrows = mysql_result($res,0,'num'); $queries++; // fill the hybrid table with the stock WURFL first $fillhybrid = "INSERT INTO ".DB_HYBRID_TABLE." SELECT * FROM ".DB_DEVICE_TABLE; mysql_query($fillhybrid); $queries++; // insert all the patch devices that DON'T already exist in the WURFL into the hybrid table mysql_query("INSERT INTO ".DB_HYBRID_TABLE." SELECT p.* FROM ".DB_PATCH_TABLE." AS p LEFT JOIN ".DB_HYBRID_TABLE." AS d ON p.deviceID = d.deviceID WHERE d.deviceID IS NULL"); $queries++; $newdevs = mysql_affected_rows(); // get all the devices that DO exist in the main WURFL so we can merge them in the hybrid table $patchres = mysql_query("SELECT p.* FROM ".DB_PATCH_TABLE." AS p LEFT JOIN ".DB_DEVICE_TABLE." AS d ON p.deviceID = d.deviceID WHERE d.deviceID IS NOT NULL"); $queries++; $mergeddevs = mysql_num_rows($patchres); while($new = mysql_fetch_assoc($patchres)){ // grab the original record to merge with the new one $origres = mysql_query("SELECT * FROM ".DB_HYBRID_TABLE." WHERE deviceID=".sqlPrep($new['deviceID'])); $queries++; $orig = mysql_fetch_assoc($origres); $origcap = unserialize($orig['capabilities']); $newcap = unserialize($new['capabilities']); $merged = $new; $mergedcap = $origcap; foreach($newcap as $key => $val) { if ( is_array($val) ) { // TODO: Make sure this works correctly, I'm suspicious because of LOG_NOTICE errors // $mergedcap[$key] = @array_merge($mergedcap[$key], $val); // Thanks to Mait Vilbiks for noticing that PHP5 requires 2 arrays for array_merge() // changed by kamermans 19Feb2007 (tentative release 1.5) $mergedcap[$key] = @array_merge((array)$mergedcap[$key], $val); } else { $mergedcap[$key] = $val; } } $merged['capabilities'] = serialize($mergedcap); // now we should have a merged record - update it $setstringarr = array(); foreach($merged as $key => $val){ $setstringarr[] = $key."=".sqlPrep($val); } $setstring = implode(", ",$setstringarr); mysql_query("UPDATE ".DB_HYBRID_TABLE." SET ".$setstring." WHERE deviceID=".sqlPrep($merged['deviceID'])); $queries++; } return(array("total" => $processedrows, "new" => $newdevs, "merged" => $mergeddevs, "errors" => $merge_errors, "queries" => $queries)); } function sqlPrep($value){ if (get_magic_quotes_gpc()) $value = stripslashes($value); if($value == '') $value = 'NULL'; else if (!is_numeric($value) || $value[0] == '0') $value = "'" . compatibleEscape($value) . "'"; //Quote if not integer return $value; } function compatibleEscape($value){ if (version_compare(phpversion(), "4.3.0", ">=")) { // you're on 4.3.0 or later return(mysql_real_escape_string($value)); } else { // you're not return(mysql_escape_string($value)); } } // PHP4 does not have file_put_contents() so I emulate it if it's not defined if(!function_exists("file_put_contents")){ function file_put_contents($n, $d, $flag = false) { $mode = @($flag == FILE_APPEND || strtoupper($flag) == 'FILE_APPEND') ? 'a' : 'w'; $f = @fopen($n, $mode); if ($f === false) { return 0; } else { if (is_array($d)) $d = implode($d); $bytes_written = fwrite($f, $d); fclose($f); return $bytes_written; } } } function toLog($func, $text, $requestedLogLevel=LOG_NOTICE){ if ( !defined('LOG_LEVEL') || LOG_LEVEL == 0 || ($requestedLogLevel-1) >= LOG_LEVEL ) { return; } if ( $requestedLogLevel == LOG_ERR ) { $warn_banner = 'ERROR: '; } else if ( $requestedLogLevel == LOG_WARNING ) { $warn_banner = 'WARNING: '; } else { $warn_banner = ''; } // Thanks laacz $_textToLog = date('r')." [".php_uname('n')." ".getmypid()."]"."[$func] ".$warn_banner . $text; $_logFP = fopen(WURFL_LOG_FILE, "a+"); fputs($_logFP, $_textToLog."\n"); fclose($_logFP); return(true); } ?>