aboutsummaryrefslogtreecommitdiffstats
path: root/include/ws_core.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'include/ws_core.inc.php')
-rw-r--r--include/ws_core.inc.php399
1 files changed, 253 insertions, 146 deletions
diff --git a/include/ws_core.inc.php b/include/ws_core.inc.php
index 3e1db6647..a8c1b7f76 100644
--- a/include/ws_core.inc.php
+++ b/include/ws_core.inc.php
@@ -2,7 +2,7 @@
// +-----------------------------------------------------------------------+
// | Piwigo - a PHP based photo gallery |
// +-----------------------------------------------------------------------+
-// | Copyright(C) 2008-2013 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2008-2014 Piwigo Team http://piwigo.org |
// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
// +-----------------------------------------------------------------------+
@@ -35,12 +35,18 @@ define( 'WS_PARAM_ACCEPT_ARRAY', 0x010000 );
define( 'WS_PARAM_FORCE_ARRAY', 0x030000 );
define( 'WS_PARAM_OPTIONAL', 0x040000 );
+define( 'WS_TYPE_BOOL', 0x01 );
+define( 'WS_TYPE_INT', 0x02 );
+define( 'WS_TYPE_FLOAT', 0x04 );
+define( 'WS_TYPE_POSITIVE', 0x10 );
+define( 'WS_TYPE_NOTNULL', 0x20 );
+define( 'WS_TYPE_ID', WS_TYPE_INT | WS_TYPE_POSITIVE | WS_TYPE_NOTNULL);
+
define( 'WS_ERR_INVALID_METHOD', 501 );
define( 'WS_ERR_MISSING_PARAM', 1002 );
define( 'WS_ERR_INVALID_PARAM', 1003 );
define( 'WS_XML_ATTRIBUTES', 'attributes_xml_');
-define( 'WS_XML_CONTENT', 'content_xml_');
/**
* PwgError object can be returned from any web service function implementation.
@@ -98,7 +104,6 @@ class PwgNamedArray
class PwgNamedStruct
{
/*private*/ var $_content;
- /*private*/ var $_name;
/*private*/ var $_xmlAttributes;
/**
@@ -110,9 +115,8 @@ class PwgNamedStruct
* encoded as xml attributes (if null - automatically prefer xml attributes
* whenever possible)
*/
- function PwgNamedStruct($name, $content, $xmlAttributes=null, $xmlElements=null )
+ function PwgNamedStruct($content, $xmlAttributes=null, $xmlElements=null )
{
- $this->_name = $name;
$this->_content = $content;
if ( isset($xmlAttributes) )
{
@@ -139,28 +143,28 @@ class PwgNamedStruct
/**
* Abstract base class for request handlers.
*/
-class PwgRequestHandler
+abstract class PwgRequestHandler
{
/** Virtual abstract method. Decodes the request (GET or POST) handles the
* method invocation as well as response sending.
*/
- function handleRequest(&$server) { assert(false); }
+ abstract function handleRequest(&$service);
}
/**
*
* Base class for web service response encoder.
*/
-class PwgResponseEncoder
+abstract class PwgResponseEncoder
{
/** encodes the web service response to the appropriate output format
* @param response mixed the unencoded result of a service method call
*/
- function encodeResponse($response) { assert(false); }
+ abstract function encodeResponse($response);
/** default "Content-Type" http header for this kind of response format
*/
- function getContentType() { assert(false); }
+ abstract function getContentType();
/**
* returns true if the parameter is a 'struct' (php array type whose keys are
@@ -182,115 +186,42 @@ class PwgResponseEncoder
* removes all XML formatting from $response (named array, named structs, etc)
* usually called by every response encoder, except rest xml.
*/
- static function flattenResponse(&$response)
+ static function flattenResponse(&$value)
{
- PwgResponseEncoder::_mergeAttributesAndContent($response);
- PwgResponseEncoder::_removeNamedArray($response);
- PwgResponseEncoder::_removeNamedStruct($response);
- if (is_array($response))
- { // need to call 2 times (first time might add new arrays)
- array_walk_recursive($response, array('PwgResponseEncoder', '_remove_named_callback') );
- array_walk_recursive($response, array('PwgResponseEncoder', '_remove_named_callback') );
- }
-//print_r($response);
- PwgResponseEncoder::_mergeAttributesAndContent($response);
- }
-
- private static function _remove_named_callback(&$value, $key)
- {
- do
- {
- $changed = 0;
- $changed += PwgResponseEncoder::_removeNamedArray($value);
- $changed += PwgResponseEncoder::_removeNamedStruct($value);
- // print_r('walk '.$key."<br>\n");
- }
- while ($changed);
+ self::flatten($value);
}
- private static function _mergeAttributesAndContent(&$value)
+ private static function flatten(&$value)
{
- if ( !is_array($value) )
- return;
-/* $first_key = '';
- if (count($value)) { $ak = array_keys($value); $first_key = $ak[0]; }
-
- print_r( '_mergeAttributesAndContent is_struct='.PwgResponseEncoder::is_struct($value)
- .' count='.count($value)
- .' first_key='.$first_key
- ."<br>\n"
- );*/
- $ret = 0;
- if (PwgResponseEncoder::is_struct($value))
+ if (is_object($value))
{
- if ( isset($value[WS_XML_ATTRIBUTES]) )
+ $class = strtolower( @get_class($value) );
+ if ($class == 'pwgnamedarray')
{
- $value = array_merge( $value, $value[WS_XML_ATTRIBUTES] );
- unset( $value[WS_XML_ATTRIBUTES] );
- $ret=1;
+ $value = $value->_content;
}
- if ( isset($value[WS_XML_CONTENT]) )
+ if ($class == 'pwgnamedstruct')
{
- $cont_processed = 0;
- if ( count($value)==1 )
- {
- $value = $value[WS_XML_CONTENT];
- $cont_processed=1;
- }
- else
- {
- if (PwgResponseEncoder::is_struct($value[WS_XML_CONTENT]))
- {
- $value = array_merge( $value, $value[WS_XML_CONTENT] );
- unset( $value[WS_XML_CONTENT] );
- $cont_processed=1;
- }
- }
- $ret += $cont_processed;
- if (!$cont_processed)
- {
- $value['_content'] = $value[WS_XML_CONTENT];
- unset( $value[WS_XML_CONTENT] );
- $ret++;
- }
+ $value = $value->_content;
}
}
- foreach ($value as $key=>$v)
+ if (!is_array($value))
+ return;
+
+ if (self::is_struct($value))
{
- if ( PwgResponseEncoder::_mergeAttributesAndContent($v) )
+ if ( isset($value[WS_XML_ATTRIBUTES]) )
{
- $value[$key]=$v;
- $ret++;
+ $value = array_merge( $value, $value[WS_XML_ATTRIBUTES] );
+ unset( $value[WS_XML_ATTRIBUTES] );
}
}
- return $ret;
- }
-
- private static function _removeNamedArray(&$value)
- {
- if ( strtolower( @get_class($value) ) =='pwgnamedarray')
- {
- $value = $value->_content;
- return 1;
- }
- return 0;
- }
- private static function _removeNamedStruct(&$value)
- {
- if ( strtolower( @get_class($value) ) =='pwgnamedstruct')
+ foreach ($value as $key=>&$v)
{
- if ( isset($value->_content['']) )
- {
- $unknown = $value->_content[''];
- unset( $value->_content[''] );
- $value->_content[$value->_name] = $unknown;
- }
- $value = $value->_content;
- return 1;
+ self::flatten($v);
}
- return 0;
}
}
@@ -349,12 +280,16 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
return;
}
- $this->addMethod('reflection.getMethodList',
- array('PwgServer', 'ws_getMethodList'),
- null, '' );
- $this->addMethod('reflection.getMethodDetails',
+ // add reflection methods
+ $this->addMethod(
+ 'reflection.getMethodList',
+ array('PwgServer', 'ws_getMethodList')
+ );
+ $this->addMethod(
+ 'reflection.getMethodDetails',
array('PwgServer', 'ws_getMethodDetails'),
- array('methodName'),'');
+ array('methodName')
+ );
trigger_action('ws_add_methods', array(&$this) );
uksort( $this->_methods, 'strnatcmp' );
@@ -378,16 +313,22 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
* Registers a web service method.
* @param methodName string - the name of the method as seen externally
* @param callback mixed - php method to be invoked internally
- * @param params array - map of allowed parameter names with optional default
- * values and parameter flags. Example of $params:
- * array( 'param1' => array('default'=>523, 'flags'=>WS_PARAM_FORCE_ARRAY) ) .
- * Possible parameter flags are:
- * WS_PARAM_ALLOW_ARRAY - this parameter can be an array
- * WS_PARAM_FORCE_ARRAY - if this parameter is scalar, force it to an array
- * before invoking the method
+ * @param params array - map of allowed parameter names with options
+ * @option mixed default (optional)
+ * @option int flags (optional)
+ * possible values: WS_PARAM_ALLOW_ARRAY, WS_PARAM_FORCE_ARRAY, WS_PARAM_OPTIONAL
+ * @option int type (optional)
+ * possible values: WS_TYPE_BOOL, WS_TYPE_INT, WS_TYPE_FLOAT, WS_TYPE_ID
+ * WS_TYPE_POSITIVE, WS_TYPE_NOTNULL
+ * @option int|float maxValue (optional)
* @param description string - a description of the method.
+ * @param include_file string - a file to be included befaore the callback is executed
+ * @param options array
+ * @option bool hidden (optional) - if true, this method won't be visible by reflection.getMethodList
+ * @option bool admin_only (optional)
+ * @option bool post_only (optional)
*/
- function addMethod($methodName, $callback, $params=array(), $description, $include_file='')
+ function addMethod($methodName, $callback, $params=array(), $description='', $include_file='', $options=array())
{
if (!is_array($params))
{
@@ -399,25 +340,27 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
$params = array_flip($params);
}
- foreach( $params as $param=>$options)
+ foreach( $params as $param=>$data)
{
- if ( !is_array($options) )
+ if ( !is_array($data) )
{
- $params[$param] = array('flags'=>0);
+ $params[$param] = array('flags'=>0,'type'=>0);
}
else
{
- $flags = isset($options['flags']) ? $options['flags'] : 0;
- if ( array_key_exists('default', $options) )
+ if ( !isset($data['flags']) )
+ {
+ $data['flags'] = 0;
+ }
+ if ( array_key_exists('default', $data) )
{
- $flags |= WS_PARAM_OPTIONAL;
+ $data['flags'] |= WS_PARAM_OPTIONAL;
}
- if ( $flags & WS_PARAM_FORCE_ARRAY )
+ if ( !isset($data['type']) )
{
- $flags |= WS_PARAM_ACCEPT_ARRAY;
+ $data['type'] = 0;
}
- $options['flags'] = $flags;
- $params[$param] = $options;
+ $params[$param] = $data;
}
}
@@ -426,6 +369,7 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
'description' => $description,
'signature' => $params,
'include' => $include_file,
+ 'options' => $options,
);
}
@@ -445,13 +389,22 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
$signature = @$this->_methods[$methodName]['signature'];
return isset($signature) ? $signature : array();
}
+
+ /**
+ * @since 2.6
+ */
+ function getMethodOptions($methodName)
+ {
+ $options = @$this->_methods[$methodName]['options'];
+ return isset($options) ? $options : array();
+ }
- /*static*/ function isPost()
+ static function isPost()
{
return isset($HTTP_RAW_POST_DATA) or !empty($_POST);
}
- /*static*/ function makeArrayParam(&$param)
+ static function makeArrayParam(&$param)
{
if ( $param==null )
{
@@ -459,12 +412,100 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
}
else
{
- if (! is_array($param) )
+ if ( !is_array($param) )
{
$param = array($param);
}
}
}
+
+ static function checkType(&$param, $type, $name)
+ {
+ $opts = array();
+ $msg = '';
+ if ( self::hasFlag($type, WS_TYPE_POSITIVE | WS_TYPE_NOTNULL) )
+ {
+ $opts['options']['min_range'] = 1;
+ $msg = ' positive and not null';
+ }
+ else if ( self::hasFlag($type, WS_TYPE_POSITIVE) )
+ {
+ $opts['options']['min_range'] = 0;
+ $msg = ' positive';
+ }
+
+ if ( is_array($param) )
+ {
+ if ( self::hasFlag($type, WS_TYPE_BOOL) )
+ {
+ foreach ($param as &$value)
+ {
+ if ( ($value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) === null )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must only contain booleans' );
+ }
+ }
+ unset($value);
+ }
+ else if ( self::hasFlag($type, WS_TYPE_INT) )
+ {
+ foreach ($param as &$value)
+ {
+ if ( ($value = filter_var($value, FILTER_VALIDATE_INT, $opts)) === false )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must only contain'.$msg.' integers' );
+ }
+ }
+ unset($value);
+ }
+ else if ( self::hasFlag($type, WS_TYPE_FLOAT) )
+ {
+ foreach ($param as &$value)
+ {
+ if (
+ ($value = filter_var($value, FILTER_VALIDATE_FLOAT)) === false
+ or ( isset($opts['options']['min_range']) and $value < $opts['options']['min_range'] )
+ ) {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must only contain'.$msg.' floats' );
+ }
+ }
+ unset($value);
+ }
+ }
+ else if ( $param !== '' )
+ {
+ if ( self::hasFlag($type, WS_TYPE_BOOL) )
+ {
+ if ( ($param = filter_var($param, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) === null )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must be a boolean' );
+ }
+ }
+ else if ( self::hasFlag($type, WS_TYPE_INT) )
+ {
+ if ( ($param = filter_var($param, FILTER_VALIDATE_INT, $opts)) === false )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must be an'.$msg.' integer' );
+ }
+ }
+ else if ( self::hasFlag($type, WS_TYPE_FLOAT) )
+ {
+ if (
+ ($param = filter_var($param, FILTER_VALIDATE_FLOAT)) === false
+ or ( isset($opts['options']['min_range']) and $param < $opts['options']['min_range'] )
+ ) {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must be a'.$msg.' float' );
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static function hasFlag($val, $flag)
+ {
+ return ($val & $flag) == $flag;
+ }
/**
* Invokes a registered method. Returns the return of the method (or
@@ -476,54 +517,87 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
{
$method = @$this->_methods[$methodName];
- if ( $method==null )
+ if ( $method == null )
{
return new PwgError(WS_ERR_INVALID_METHOD, 'Method name is not valid');
}
+
+ if ( isset($method['options']['post_only']) and $method['options']['post_only'] and !self::isPost() )
+ {
+ return new PwgError(405, 'This method requires HTTP POST');
+ }
+
+ if ( isset($method['options']['admin_only']) and $method['options']['admin_only'] and !is_admin() )
+ {
+ return new PwgError(401, 'Access denied');
+ }
- // parameter check and data coercion !
+ // parameter check and data correction
$signature = $method['signature'];
$missing_params = array();
- foreach($signature as $name=>$options)
+
+ foreach ($signature as $name => $options)
{
$flags = $options['flags'];
+
+ // parameter not provided in the request
if ( !array_key_exists($name, $params) )
- {// parameter not provided in the request
- if ( !($flags&WS_PARAM_OPTIONAL) )
+ {
+ if ( !self::hasFlag($flags, WS_PARAM_OPTIONAL) )
{
$missing_params[] = $name;
}
- else if ( array_key_exists('default',$options) )
+ else if ( array_key_exists('default', $options) )
{
$params[$name] = $options['default'];
- if ( ($flags&WS_PARAM_FORCE_ARRAY) )
+ if ( self::hasFlag($flags, WS_PARAM_FORCE_ARRAY) )
{
- $this->makeArrayParam( $params[$name] );
+ self::makeArrayParam($params[$name]);
}
}
}
+ // parameter provided but empty
+ else if ( $params[$name]==='' and !self::hasFlag($flags, WS_PARAM_OPTIONAL) )
+ {
+ $missing_params[] = $name;
+ }
+ // parameter provided - do some basic checks
else
- {// parameter provided - do some basic checks
+ {
$the_param = $params[$name];
- if ( is_array($the_param) and ($flags&WS_PARAM_ACCEPT_ARRAY)==0 )
+
+ if ( is_array($the_param) and !self::hasFlag($flags, WS_PARAM_ACCEPT_ARRAY) )
{
return new PwgError(WS_ERR_INVALID_PARAM, $name.' must be scalar' );
}
- if ( ($flags&WS_PARAM_FORCE_ARRAY) )
+
+ if ( self::hasFlag($flags, WS_PARAM_FORCE_ARRAY) )
+ {
+ self::makeArrayParam($the_param);
+ }
+
+ if ( $options['type'] > 0 )
{
- $this->makeArrayParam( $the_param );
+ if ( ($ret = self::checkType($the_param, $options['type'], $name)) !== null )
+ {
+ return $ret;
+ }
}
+
if ( isset($options['maxValue']) and $the_param>$options['maxValue'])
{
$the_param = $options['maxValue'];
}
+
$params[$name] = $the_param;
}
}
+
if (count($missing_params))
{
return new PwgError(WS_ERR_MISSING_PARAM, 'Missing parameters: '.implode(',',$missing_params));
}
+
$result = trigger_event('ws_invoke_allowed', true, $methodName, $params);
if ( strtolower( @get_class($result) )!='pwgerror')
{
@@ -533,6 +607,7 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
}
$result = call_user_func_array($method['callback'], array($params, &$this) );
}
+
return $result;
}
@@ -541,7 +616,9 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
*/
static function ws_getMethodList($params, &$service)
{
- return array('methods' => new PwgNamedArray( array_keys($service->_methods),'method' ) );
+ $methods = array_filter($service->_methods,
+ create_function('$m', 'return empty($m["options"]["hidden"]) || !$m["options"]["hidden"];'));
+ return array('methods' => new PwgNamedArray( array_keys($methods),'method' ) );
}
/**
@@ -550,32 +627,62 @@ Request format: ".@$this->_requestFormat." Response format: ".@$this->_responseF
static function ws_getMethodDetails($params, &$service)
{
$methodName = $params['methodName'];
+
if (!$service->hasMethod($methodName))
{
- return new PwgError(WS_ERR_INVALID_PARAM,
- 'Requested method does not exist');
+ return new PwgError(WS_ERR_INVALID_PARAM, 'Requested method does not exist');
}
+
$res = array(
'name' => $methodName,
'description' => $service->getMethodDescription($methodName),
'params' => array(),
+ 'options' => $service->getMethodOptions($methodName),
);
- $signature = $service->getMethodSignature($methodName);
- foreach ($signature as $name => $options)
+
+ foreach ($service->getMethodSignature($methodName) as $name => $options)
{
$param_data = array(
'name' => $name,
- 'optional' => ($options['flags']&WS_PARAM_OPTIONAL)?true:false,
- 'acceptArray' => ($options['flags']&WS_PARAM_ACCEPT_ARRAY)?true:false,
+ 'optional' => self::hasFlag($options['flags'], WS_PARAM_OPTIONAL),
+ 'acceptArray' => self::hasFlag($options['flags'], WS_PARAM_ACCEPT_ARRAY),
+ 'type' => 'mixed',
);
+
if (isset($options['default']))
{
$param_data['defaultValue'] = $options['default'];
}
+ if (isset($options['maxValue']))
+ {
+ $param_data['maxValue'] = $options['maxValue'];
+ }
if (isset($options['info']))
{
$param_data['info'] = $options['info'];
}
+
+ if ( self::hasFlag($options['type'], WS_TYPE_BOOL) )
+ {
+ $param_data['type'] = 'bool';
+ }
+ else if ( self::hasFlag($options['type'], WS_TYPE_INT) )
+ {
+ $param_data['type'] = 'int';
+ }
+ else if ( self::hasFlag($options['type'], WS_TYPE_FLOAT) )
+ {
+ $param_data['type'] = 'float';
+ }
+ if ( self::hasFlag($options['type'], WS_TYPE_POSITIVE) )
+ {
+ $param_data['type'].= ' positive';
+ }
+ if ( self::hasFlag($options['type'], WS_TYPE_NOTNULL) )
+ {
+ $param_data['type'].= ' notnull';
+ }
+
$res['params'][] = $param_data;
}
return $res;