TeraWurfl
[ class tree: TeraWurfl ] [ index: TeraWurfl ] [ all elements ]

Source for file TeraWurflWebservice.php

Documentation is available at TeraWurflWebservice.php

  1. <?php
  2. /**
  3.  * Tera_WURFL - PHP MySQL driven WURFL
  4.  * 
  5.  * Tera-WURFL was written by Steve Kamerman, and is based on the
  6.  * Java WURFL Evolution package by Luca Passani and WURFL PHP Tools by Andrea Trassati.
  7.  * This version uses a MySQL database to store the entire WURFL file, multiple patch
  8.  * files, and a persistent caching mechanism to provide extreme performance increases.
  9.  * 
  10.  * @package TeraWurfl
  11.  * @author Steve Kamerman <stevekamerman AT gmail.com>
  12.  * @version Stable 2.1.3 $Date: 2010/09/18 15:43:21
  13.  * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1
  14.  */
  15. /**
  16.  * The server-side Tera-WURFL webservice provider.  Normally used with webservice.php
  17.  * @package TeraWurfl
  18.  *
  19.  */
  20.     
  21.     /**
  22.      * Allow clients to query the webservice only from the listed networks. Setting this
  23.      * variable to false disables the filter and allows connections from ANY client IP.
  24.      * To allow only certain networks, put them in CIDR notation in an array.  For example,
  25.      * to allow only the range 172.16.10.0/24 and the single IP 192.168.2.17 you would use
  26.      * this as the setting:
  27.      * 
  28.      * <code>
  29.      * public static $ALLOWED_CLIENT_IPS = array('172.16.10.0/24','192.168.2.17/32');
  30.      * </code>
  31.      * 
  32.      * NOTE: 127.0.0.1/32 is automatically allowed, however, some clients may use a different
  33.      * loopback address like 127.1.1.1.  In this case, add 127.0.0.0/8 to your list.
  34.      * 
  35.      * Unauthorized attempts to use this webservice are logged to the Tera-WURFL log file
  36.      * with a severity of LOG_WARNING.
  37.      * 
  38.      * @var Mixed 
  39.      */
  40.     public static $ALLOWED_CLIENT_IPS false;
  41.     
  42.     public static $FORMAT_XML 'xml';
  43.     public static $FORMAT_JSON 'json';
  44.     
  45.     /**
  46.      * Log all errors from the webservice
  47.      * @var Boolean Enable
  48.      */
  49.     public $enable_error_log = true;
  50.     /**
  51.      * Filename of error log
  52.      * @var String 
  53.      */
  54.     public $error_log_filename = 'webservice_error.log';
  55.     /**
  56.      * The directory where the error log is stored.  Set to null to use the Tera-WURFL data/ directory
  57.      * @var String 
  58.      */
  59.     public $error_log_path = null;
  60.     /**
  61.      * Log all access of the webservice
  62.      * @var Boolean Enable
  63.      */
  64.     public $enable_access_log = false;
  65.     /**
  66.      * Filename of access log
  67.      * @var String 
  68.      */
  69.     public $access_log_filename = 'webservice_access.log';
  70.     /**
  71.      * The directory where the access log is stored.  Set to null to use the Tera-WURFL data/ directory
  72.      * @var String 
  73.      */
  74.     public $access_log_path = null;
  75.     
  76.     protected $format;
  77.     protected $xml;
  78.     protected $json;
  79.     protected $out_cap = array();
  80.     protected $search_results = array();
  81.     protected $out_errors = array();
  82.     protected $userAgent;
  83.     protected $wurflObj;
  84.     protected $flatCapabilities = array();
  85.     
  86.     public function __construct($userAgent,$searchPhrase,$data_format='xml',$teraWurflInstance=null){
  87.         set_exception_handler(array($this,'__handleExceptions'));
  88.         require_once realpath(dirname(__FILE__).'/TeraWurfl.php');
  89.         $this->format = $data_format;
  90.         $this->userAgent = $userAgent;
  91.         if(!is_null($teraWurflInstance)){
  92.             $this->wurflObj =$teraWurflInstance;
  93.         }else{
  94.             $this->wurflObj = new TeraWurfl();
  95.         }
  96.         if(!$this->isClientAllowed()){
  97.             $this->logError("Denied webservice access to client {$_SERVER['REMOTE_ADDR']}",LOG_WARNING);
  98.             echo "access is denied from ".$_SERVER['REMOTE_ADDR'];
  99.             exit(0);
  100.         }
  101.         if($this->enable_access_log$this->logAccess();
  102.         $this->wurflObj->getDeviceCapabilitiesFromAgent($this->userAgent);
  103.         $this->flattenCapabilities();
  104.         $this->search($searchPhrase);
  105.         switch($this->format){
  106.             case self::$FORMAT_JSON:
  107.                 $this->generateJSON();
  108.                 break;
  109.             default:
  110.             case self::$FORMAT_XML:
  111.                 $this->generateXML();
  112.                 break;
  113.         }
  114.     }
  115.     /**
  116.      * Get the response that would normally be sent to the client.
  117.      * @return String Response
  118.      */
  119.     public function getResponse(){
  120.         switch($this->format){
  121.             case self::$FORMAT_JSON:
  122.                 return $this->json;
  123.                 break;
  124.             default:
  125.             case self::$FORMAT_XML:
  126.                 return $this->xml;
  127.                 break;
  128.         }
  129.     }
  130.     /**
  131.      * Send the HTTP Headers for the return data
  132.      * @return void 
  133.      */
  134.     public function sendHTTPHeaders(){
  135.         header("Cache-Control: no-cache, must-revalidate")// HTTP/1.1
  136.         header("Expires: Mon, 26 Jul 1997 05:00:00 GMT")// Date in the past
  137.         switch($this->format){
  138.             case self::$FORMAT_JSON:
  139.                 header("Content-Type: application/json");
  140.                 break;
  141.             default:
  142.             case self::$FORMAT_XML:
  143.                 header("Content-Type: text/xml");
  144.                 break;
  145.         }
  146.     }
  147.     /**
  148.      * Send the complete response to the client, including the HTTP Headers and the response.
  149.      * @return void 
  150.      */
  151.     public function sendResponse(){
  152.         $this->sendHTTPHeaders();
  153.         echo $this->getResponse();
  154.     }
  155.     /**
  156.      * See if a given ip ($ip) is in a given CIDR network ($cidr_range)
  157.      * @param String CIDR Network (e.g. "192.168.2.0/24")
  158.      * @param String IP Address
  159.      * @return Bool IP Address is in CIDR Network
  160.      */
  161.     public static function ipInCIDRNetwork($cidr_network,$ip){
  162.         // Thanks Bill Grady for posting a *working* IP in CIDR network function!
  163.         // Source: http://billgrady.com/wp/2009/05/21/ip-matching-with-cidr-notation-in-php/
  164.         // Get the base and the bits from the CIDR
  165.         list($base$bitsexplode('/'$cidr_network);
  166.         if($bits || $bits 32){
  167.             throw new Exception("Error: Invalid CIDR mask specified.");
  168.         }
  169.         // Now split it up into it's classes
  170.         list($a$b$c$dexplode('.'$base);
  171.         // Now do some bit shifting/switching to convert to ints
  172.         $i    ($a << 24($b << 16$c << $d;
  173.         $mask $bits == 0(~<< (32 $bits));
  174.         // Here's our lowest int
  175.         $low $i $mask;
  176.         // Here's our highest int
  177.         $high $i (~$mask 0xFFFFFFFF);
  178.         // Now split the ip we're checking against up into classes
  179.         list($a$b$c$dexplode('.'$ip);
  180.         // Now convert the ip we're checking against to an int
  181.         $check ($a << 24($b << 16$c << $d;
  182.         // If the ip is within the range, including highest/lowest values,
  183.         // then it's witin the CIDR range
  184.         if ($check >= $low && $check <= $highreturn true;
  185.         return false;
  186.     }
  187.     /**
  188.      * Is the connecting client allowed to use this webservice
  189.      * @return Bool 
  190.      */
  191.     protected function isClientAllowed(){
  192.         if(!self::$ALLOWED_CLIENT_IPS || $_SERVER['REMOTE_ADDR'== '127.0.0.1'return true;
  193.         $ip $_SERVER['REMOTE_ADDR'];
  194.         foreach(self::$ALLOWED_CLIENT_IPS as $cidr_range){
  195.             if(self::ipInCIDRNetwork($cidr_range,$ip)) return true;
  196.         }
  197.         return false;
  198.     }
  199.     /**
  200.      * Converts PHP variables to an XML friendly string
  201.      * @param Mixed Value
  202.      * @return String Value
  203.      */
  204.     protected function exportValue($in){
  205.         if(is_bool($in))return var_export($in,true);
  206.         if(is_null($in|| !isset($in))return '';
  207.         return $in;
  208.     }
  209.     /**
  210.      * Add an error to the errors array that will be sent in the response
  211.      * @param String Capability name that is in error
  212.      * @param String Description of the error
  213.      * @return void 
  214.      */
  215.     protected function addError($name,$desc){
  216.         if($this->enable_error_log$this->logError("Client ".$_SERVER['REMOTE_ADDR']." requested an invalid capability: $name",LOG_WARNING);
  217.         $this->out_errors[array('name'=>$name,'desc'=>$desc);
  218.     }
  219.     /**
  220.      * Search through all the capabilities and place the requested ones in search_results to
  221.      * be sent in the response.
  222.      * @param String Search phrase (e.g. "is_wireless_device|streaming|tera_wurfl")
  223.      * @return void 
  224.      */
  225.     protected function search($searchPhrase){
  226.         if (!empty($searchPhrase)){
  227.             $capabilities explode('|',$_REQUEST['search']);
  228.             foreach($capabilities as $cap){
  229.                 $cap strtolower($cap);
  230.                 $cap preg_replace('/[^a-z0-9_\- ]/','',$cap);
  231.                 // Individual Capability
  232.                 if(array_key_exists($cap,$this->flatCapabilities)){
  233.                     $this->search_results[$cap$this->flatCapabilities[$cap];
  234.                     continue;
  235.                 }
  236.                 // Group
  237.                 if(array_key_exists($cap,$this->wurflObj->capabilities&& is_array($this->wurflObj->capabilities[$cap])){
  238.                     foreach($this->wurflObj->capabilities[$capas $group_cap => $value){
  239.                         $this->search_results[$group_cap$value;
  240.                     }
  241.                     continue;
  242.                 }
  243.                 $this->addError($cap,"The group or capability is not valid.");
  244.                 $this->search_results[$capnull;
  245.             }
  246.         }else{
  247.             $this->search_results = $this->flatCapabilities;
  248.         }
  249.     }
  250.     /**
  251.      * Flatten the multi-tiered capabilities array into a list of capabilities.
  252.      * @return void 
  253.      */
  254.     protected function flattenCapabilities(){
  255.         $this->flatCapabilities = array();
  256.         foreach($this->wurflObj->capabilities as $key => $value){
  257.             if(is_array($value)){
  258.                 foreach($value as $subkey => $subvalue){
  259.                     $this->flatCapabilities[$subkey$subvalue;
  260.                 }
  261.             }else{
  262.                 $this->flatCapabilities[$key$value;
  263.             }
  264.         }
  265.     }
  266.     /**
  267.      * Generate the XML response
  268.      * @return void 
  269.      */
  270.     protected function generateXML(){
  271.         $this->xml = '<?xml version="1.0" encoding="iso-8859-1"?>'."\n";
  272.         $this->xml .= "<TeraWURFLQuery>\n";
  273.         $this->xml .= sprintf("\t".'<device apiVersion="%s" mtime="%s" useragent="%s" id="%s">'."\n",
  274.             $this->wurflObj->release_version,
  275.             $this->wurflObj->getSetting(TeraWurfl::$SETTING_LOADED_DATE),
  276.             str_replace('&','&amp;',$this->wurflObj->capabilities['user_agent']),
  277.             $this->wurflObj->capabilities['id']
  278.         );
  279.         foreach$this->search_results as $cap_name => $value){
  280.             $value $this->exportValue($value);
  281.             $value str_replace('&','&amp;',$value);
  282.             $this->xml .= "\t\t<capability name=\"$cap_name\" value=\"$value\"/>\n";
  283.         }
  284.         $this->xml .= "\t</device>\n";
  285.         $this->xml .= $this->generateXMLErrors();
  286.         $this->xml .= "</TeraWURFLQuery>";
  287.     }
  288.     /**
  289.      * Generate JSON response
  290.      * @return void 
  291.      */
  292.     protected function generateJSON(){
  293.         $data array(
  294.             'apiVersion'    => $this->wurflObj->release_version,
  295.             'mtime'            => $this->wurflObj->getSetting(TeraWurfl::$SETTING_LOADED_DATE),
  296.             'useragent'        => $this->wurflObj->capabilities['user_agent'],
  297.             'id'            => $this->wurflObj->capabilities['id'],
  298.             'capabilities'    => $this->search_results,
  299.             'errors'        => $this->out_errors,
  300.         );
  301.         $this->json = json_encode($data);
  302.         unset($data);
  303.     }
  304.     /**
  305.      * Generate the errors section of the XML response
  306.      * @return String XML errors section
  307.      */
  308.     protected function generateXMLErrors(){
  309.         $xml '';
  310.         if(count($this->out_errors)==0){
  311.             $xml .= "\t<errors/>\n";
  312.         }else{
  313.             $xml .= "\t<errors>\n";
  314.             foreach($this->out_errors as $error){
  315.                 $xml .= "\t\t<error name=\"{$error['name']}\" description=\"{$error['desc']}\"/>\n";
  316.             }
  317.             $xml .= "\t</errors>\n";
  318.         }
  319.         return $xml;
  320.     }
  321.     /**
  322.      * Log this access with the IP of the requestor and the user agent
  323.      */
  324.     protected function logAccess(){
  325.         $_textToLog sprintf('%s [%s %s][%s] %s',
  326.             date('r'),
  327.             php_uname('n'),
  328.             getmypid(),
  329.             $_SERVER['REMOTE_ADDR'],
  330.             $this->userAgent
  331.         )."\n";
  332.         $path is_null($this->access_log_path)dirname(__FILE__).'/'.TeraWurflConfig::$DATADIR$this->access_log_path.'/';
  333.         $logfile $path.$this->access_log_filename;
  334.         @file_put_contents($logfile,$_textToLog,FILE_APPEND);
  335.     }
  336. /**
  337.      * Log an error in the TeraWurflWebservice log file
  338.      * @param String The error message text
  339. `     * @param Int The log level / severity of the error
  340.      * @param String The function or code that was being run when the error occured
  341.      * @return void 
  342.      */
  343.     protected function logError($text$requestedLogLevel=LOG_NOTICE$func="TeraWurflWebservice"){
  344.         if($requestedLogLevel == LOG_ERR$this->errors[$text;
  345.         if (TeraWurflConfig::$LOG_LEVEL == || ($requestedLogLevel-1>= TeraWurflConfig::$LOG_LEVEL {
  346.             return;
  347.         }
  348.         if $requestedLogLevel == LOG_ERR {
  349.             $warn_banner 'ERROR: ';
  350.         else if $requestedLogLevel == LOG_WARNING {
  351.             $warn_banner 'WARNING: ';
  352.         else {
  353.             $warn_banner '';
  354.         }
  355.         $_textToLog date('r')." [".php_uname('n')." ".getmypid()."]"."[$func".$warn_banner $text "\n";
  356.         $path is_null($this->access_log_path)dirname(__FILE__).'/'.TeraWurflConfig::$DATADIR$this->access_log_path.'/';
  357.         $logfile $path.$this->error_log_filename;
  358.         @file_put_contents($logfile,$_textToLog,FILE_APPEND);
  359.     }
  360.     public function __handleExceptions(Exception $exception){
  361.         $this->logError($exception->getMessage(),LOG_ERR);
  362.     }
  363. }

Documentation generated on Sun, 19 Sep 2010 00:16:04 +0000 by phpDocumentor 1.4.3