From ddce56184c26419f13a3564f0a3112874b9c4ea4 Mon Sep 17 00:00:00 2001 From: Ralf Lang Date: Mon, 6 Apr 2026 20:11:33 +0200 Subject: [PATCH 1/4] test: Formally update test suite --- .horde.yml | 1 - phpunit.xml.dist | 28 +++++++++++---------- test/{ => Horde/Rpc}/rpc-test.php | 0 test/StrLogger.php | 31 ----------------------- test/{ => Unit}/ActiveSyncTest.php | 37 ++++++++++++++++++++------- test/Unit/StrLogger.php | 40 ++++++++++++++++++++++++++++++ test/bootstrap.php | 14 ----------- 7 files changed, 83 insertions(+), 68 deletions(-) rename test/{ => Horde/Rpc}/rpc-test.php (100%) delete mode 100644 test/StrLogger.php rename test/{ => Unit}/ActiveSyncTest.php (53%) create mode 100644 test/Unit/StrLogger.php delete mode 100644 test/bootstrap.php diff --git a/.horde.yml b/.horde.yml index fff1c64..8eaeb93 100644 --- a/.horde.yml +++ b/.horde.yml @@ -59,4 +59,3 @@ dependencies: dev: composer: horde/activesync: ^3 - horde/test: ^3 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index bb497e2..682ccde 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,26 +1,28 @@ + defaultTestSuite="unit"> - - test + + test/Unit + + + test/Integration + + + test/Horde - + - src + lib - + diff --git a/test/rpc-test.php b/test/Horde/Rpc/rpc-test.php similarity index 100% rename from test/rpc-test.php rename to test/Horde/Rpc/rpc-test.php diff --git a/test/StrLogger.php b/test/StrLogger.php deleted file mode 100644 index b5378cb..0000000 --- a/test/StrLogger.php +++ /dev/null @@ -1,31 +0,0 @@ -logs[] = [ - 'msg' => $msg, - 'level' => $level, - ]; - } - - public function err($msg) - { - $this->log($msg, 'ERROR'); - } - - public function debug($msg) - { - $this->log($msg, 'DEBUG'); - } - - public function notice($msg) - { - $this->log($msg, 'NOTICE'); - } -} diff --git a/test/ActiveSyncTest.php b/test/Unit/ActiveSyncTest.php similarity index 53% rename from test/ActiveSyncTest.php rename to test/Unit/ActiveSyncTest.php index 52a7548..1618569 100644 --- a/test/ActiveSyncTest.php +++ b/test/Unit/ActiveSyncTest.php @@ -1,14 +1,25 @@ createMock(Horde_ActiveSync::class); + $activeSync = $this->createStub(Horde_ActiveSync::class); $activeSync->method('getGetVars')->willReturn([ 'Cmd' => 'OPTIONS', 'DeviceId' => 'test', @@ -29,7 +41,7 @@ public function testNoPwInLogmessages() throw new Horde_Exception('test'); }); - $request = $this->createMock(Horde_Controller_Request_Http::class); + $request = $this->createStub(Horde_Controller_Request_Http::class); $request->method('getMethod')->willReturn('POST'); $request->method('getServerVars')->willReturn([ 'QUERY_STRING' => 'test', @@ -43,14 +55,21 @@ public function testNoPwInLogmessages() 'logger' => $logger, ]); - $this->pretendAuth('user', 'password', $request); + // Suppress exit() since the code calls exit after error handling + $this->expectOutputString(''); + + try { + $this->activeSyncRpc->getResponse($request); + } catch (\Throwable $e) { + // Catch any exception that might be thrown instead of exit + } foreach ($logger->logs as $log) { - $this->assertFalse(strpos($log['msg'], 'password')); + $this->assertStringNotContainsString('password', $log['msg']); } } - protected function pretendAuth($user, $pw, $request) + protected function pretendAuth(string $user, string $pw, Horde_Controller_Request_Http $request): void { $this->activeSyncRpc->getResponse($request); } diff --git a/test/Unit/StrLogger.php b/test/Unit/StrLogger.php new file mode 100644 index 0000000..5991666 --- /dev/null +++ b/test/Unit/StrLogger.php @@ -0,0 +1,40 @@ +logs[] = [ + 'msg' => $msg, + 'level' => $level, + ]; + } + + public function err(string $msg): void + { + $this->log($msg, 'ERROR'); + } + + public function debug(string $msg): void + { + $this->log($msg, 'DEBUG'); + } + + public function notice(string $msg): void + { + $this->log($msg, 'NOTICE'); + } +} diff --git a/test/bootstrap.php b/test/bootstrap.php deleted file mode 100644 index c92c4e9..0000000 --- a/test/bootstrap.php +++ /dev/null @@ -1,14 +0,0 @@ - Date: Mon, 6 Apr 2026 20:12:34 +0200 Subject: [PATCH 2/4] chore: gitignore entries --- .gitignore | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.gitignore b/.gitignore index 00d4aca..d56def6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,23 @@ run-tests.log .phpunit.cache/ composer.lock + +# Added by horde-components QC --fix-qc-issues +# Build artifacts directory +/build/ +# PHPStorm IDE settings +/.idea/ +# VSCode IDE settings +/.vscode/ +# Claude Code CLI cache and state +/.claude/ +# Cline extension data +/.cline/ +# PHP CS Fixer cache file +/.php-cs-fixer.cache +# PHPUnit result cache +/.phpunit.result.cache +# PHPStan local configuration +/phpstan.neon +# PHPStan cache directory +/.phpstan.cache/ From 646078554a01050b60ddc0a28548366f814a61e5 Mon Sep 17 00:00:00 2001 From: Ralf Lang Date: Mon, 6 Apr 2026 20:13:00 +0200 Subject: [PATCH 3/4] style: php-cs-fixer --- doc/Horde/Rpc/examples/soap.php | 8 +- doc/Horde/Rpc/examples/xmlrpc.php | 8 +- lib/Horde/Rpc.php | 27 +++-- lib/Horde/Rpc/ActiveSync.php | 171 ++++++++++++++------------- lib/Horde/Rpc/Exception.php | 7 +- lib/Horde/Rpc/Jsonrpc.php | 43 +++---- lib/Horde/Rpc/Phpgw.php | 29 ++--- lib/Horde/Rpc/Soap.php | 58 +++++---- lib/Horde/Rpc/Syncml.php | 26 ++-- lib/Horde/Rpc/Syncml/Wbxml.php | 5 +- lib/Horde/Rpc/Translation.php | 3 +- lib/Horde/Rpc/Webdav.php | 9 +- lib/Horde/Rpc/Xmlrpc.php | 29 ++--- test/Horde/Rpc/rpc-test.php | 189 +++++++++++++++++------------- test/Unit/ActiveSyncTest.php | 3 +- 15 files changed, 339 insertions(+), 276 deletions(-) diff --git a/doc/Horde/Rpc/examples/soap.php b/doc/Horde/Rpc/examples/soap.php index 655990f..9ee0ca5 100644 --- a/doc/Horde/Rpc/examples/soap.php +++ b/doc/Horde/Rpc/examples/soap.php @@ -1,4 +1,5 @@ '', 'password' => '', 'namespace' => 'urn:horde', @@ -23,7 +24,7 @@ 'uri' => 'urn:horde', 'exceptions' => true, 'trace' => true, -); +]; $soap = new SoapClient(null, $rpc_options); $result = Horde_Rpc::request( @@ -31,5 +32,6 @@ $GLOBALS['rpc_endpoint'], $GLOBALS['rpc_method'], $soap, - array()); + [] +); var_dump($result); diff --git a/doc/Horde/Rpc/examples/xmlrpc.php b/doc/Horde/Rpc/examples/xmlrpc.php index 2336b47..e6a0d93 100644 --- a/doc/Horde/Rpc/examples/xmlrpc.php +++ b/doc/Horde/Rpc/examples/xmlrpc.php @@ -1,4 +1,5 @@ '', 'request.password' => '', -); +]; $http_client = new Horde_Http_Client($rpc_options); $result = Horde_Rpc::request( @@ -23,6 +24,7 @@ $GLOBALS['rpc_endpoint'], $GLOBALS['rpc_method'], $GLOBALS['http_client'], - array()); + [] +); var_dump($result); diff --git a/lib/Horde/Rpc.php b/lib/Horde/Rpc.php index e7f3df9..067627e 100644 --- a/lib/Horde/Rpc.php +++ b/lib/Horde/Rpc.php @@ -1,6 +1,7 @@ _logger = $params['logger']; unset($params['logger']); } else { - $this->_logger = new Horde_Support_Stub; + $this->_logger = new Horde_Support_Stub(); } $this->_params = $params; @@ -125,12 +126,12 @@ public function authorize() $hash = str_replace('Basic ', '', $serverVars['Authorization']); $hash = base64_decode($hash); if (strpos($hash, ':') !== false) { - list($user, $pass) = explode(':', $hash, 2); + [$user, $pass] = explode(':', $hash, 2); } } if (!isset($user) - || !$auth->authenticate($user, array('password' => $pass))) { + || !$auth->authenticate($user, ['password' => $pass])) { if ($this->_requestMissingAuthorization) { header('WWW-Authenticate: Basic realm="Horde RPC"'); } @@ -222,13 +223,17 @@ public function sendOutput($output) * @return mixed The returned result from the method * @throws Horde_Rpc_Exception */ - public static function request($driver, $url, $method, $client, - $params = null) - { + public static function request( + $driver, + $url, + $method, + $client, + $params = null + ) { $driver = Horde_String::ucfirst(basename($driver)); $class = 'Horde_Rpc_' . $driver; if (class_exists($class)) { - return call_user_func(array($class, 'request'), $url, $method, $client, $params); + return call_user_func([$class, 'request'], $url, $method, $client, $params); } else { throw new Horde_Rpc_Exception('Class definition of ' . $class . ' not found.'); } diff --git a/lib/Horde/Rpc/ActiveSync.php b/lib/Horde/Rpc/ActiveSync.php index b7f18f8..bfce783 100644 --- a/lib/Horde/Rpc/ActiveSync.php +++ b/lib/Horde/Rpc/ActiveSync.php @@ -1,6 +1,7 @@ getServerVars(); $this->_get = $params['server']->getGetVars(); - if ($request->getMethod() == 'POST' && - (((empty($this->_get['Cmd']) || empty($this->_get['DeviceId']) || - empty($this->_get['DeviceType'])) && empty($serverVars['QUERY_STRING'])) && - stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false)) { + if ($request->getMethod() == 'POST' + && (((empty($this->_get['Cmd']) || empty($this->_get['DeviceId']) + || empty($this->_get['DeviceType'])) && empty($serverVars['QUERY_STRING'])) + && stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false)) { $this->_logger->err('Missing required parameters.'); throw new Horde_Rpc_Exception('Your device requested the ActiveSync URL wihtout required parameters.'); @@ -91,24 +92,39 @@ public function getResponse($request) ob_start(null, 1048576); $serverVars = $this->_request->getServerVars(); switch ($serverVars['REQUEST_METHOD']) { - case 'OPTIONS': - case 'GET': - if ($serverVars['REQUEST_METHOD'] == 'GET' && - (!empty($this->_get['Cmd']) && $this->_get['Cmd'] != 'OPTIONS') && - stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false) { + case 'OPTIONS': + case 'GET': + if ($serverVars['REQUEST_METHOD'] == 'GET' + && (!empty($this->_get['Cmd']) && $this->_get['Cmd'] != 'OPTIONS') + && stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false) { + + $this->_logger->debug('Accessing ActiveSync endpoint from browser or missing required data.'); + throw new Horde_Rpc_Exception( + Horde_Rpc_Translation::t('Trying to access the ActiveSync endpoint from a browser. Not Supported.') + ); + } + if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') !== false) { + try { + $result = $this->_server->handleRequest('Autodiscover', null); + if (!$result) { + $this->_logger->err('Unknown error during Autodiscover.'); + throw new Horde_Exception('Unknown Error'); + } + $this->_contentType = $result; + } catch (Horde_Exception_AuthenticationFailure $e) { + $this->_sendAuthenticationFailedHeaders($e); + exit; + } catch (Horde_Exception $e) { + $this->_handleError($e); + } + break; + } - $this->_logger->debug('Accessing ActiveSync endpoint from browser or missing required data.'); - throw new Horde_Rpc_Exception( - Horde_Rpc_Translation::t('Trying to access the ActiveSync endpoint from a browser. Not Supported.')); - } - if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') !== false) { + $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for OPTIONS'); try { - $result = $this->_server->handleRequest('Autodiscover', null); - if (!$result) { - $this->_logger->err('Unknown error during Autodiscover.'); + if (!$this->_server->handleRequest('Options', null)) { throw new Horde_Exception('Unknown Error'); } - $this->_contentType = $result; } catch (Horde_Exception_AuthenticationFailure $e) { $this->_sendAuthenticationFailedHeaders($e); exit; @@ -116,58 +132,49 @@ public function getResponse($request) $this->_handleError($e); } break; - } - $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for OPTIONS'); - try { - if (!$this->_server->handleRequest('Options', null)) { - throw new Horde_Exception('Unknown Error'); + case 'POST': + // Autodiscover Request + if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') !== false) { + $this->_get['Cmd'] = 'Autodiscover'; + $this->_get['DeviceId'] = null; } - } catch (Horde_Exception_AuthenticationFailure $e) { - $this->_sendAuthenticationFailedHeaders($e); - exit; - } catch (Horde_Exception $e) { - $this->_handleError($e); - } - break; - case 'POST': - // Autodiscover Request - if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') !== false) { - $this->_get['Cmd'] = 'Autodiscover'; - $this->_get['DeviceId'] = null; - } + $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']); - $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']); - - try { - $ret = $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']); - if ($ret === false) { - throw new Horde_Rpc_Exception(sprintf( - 'Received FALSE while handling %s command.', $this->_get['Cmd'])); - } elseif ($ret !== true) { - $this->_contentType = $ret; + try { + $ret = $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']); + if ($ret === false) { + throw new Horde_Rpc_Exception(sprintf( + 'Received FALSE while handling %s command.', + $this->_get['Cmd'] + )); + } elseif ($ret !== true) { + $this->_contentType = $ret; + } + } catch (Horde_ActiveSync_Exception_InvalidRequest $e) { + $this->_logger->err(sprintf( + 'Returning HTTP 400 while handling %s command. Error is: %s', + $this->_get['Cmd'], + $e->getMessage() + )); + $this->_handleError($e); + header('HTTP/1.1 400 Invalid Request'); + exit; + } catch (Horde_Exception_AuthenticationFailure $e) { + $this->_sendAuthenticationFailedHeaders($e); + exit; + } catch (Horde_Exception $e) { + $this->_logger->err(sprintf( + 'Returning HTTP 500 while handling %s command. Error is: %s', + $this->_get['Cmd'], + $e->getMessage() + )); + $this->_handleError($e); + header('HTTP/1.1 500'); + exit; } - } catch (Horde_ActiveSync_Exception_InvalidRequest $e) { - $this->_logger->err(sprintf( - 'Returning HTTP 400 while handling %s command. Error is: %s', - $this->_get['Cmd'], $e->getMessage())); - $this->_handleError($e); - header('HTTP/1.1 400 Invalid Request'); - exit; - } catch (Horde_Exception_AuthenticationFailure $e) { - $this->_sendAuthenticationFailedHeaders($e); - exit; - } catch (Horde_Exception $e) { - $this->_logger->err(sprintf( - 'Returning HTTP 500 while handling %s command. Error is: %s', - $this->_get['Cmd'], - $e->getMessage())); - $this->_handleError($e); - header('HTTP/1.1 500'); - exit; - } - break; + break; } } @@ -226,7 +233,7 @@ protected function _handleError($e) $this->_logger->err('Error in communicating with ActiveSync server: ' . $m); $b = new Horde_Support_Backtrace($e); - $this->_logger->err((string)$b); + $this->_logger->err((string) $b); $this->_logger->err('Buffer contents: ' . $buffer); } @@ -239,19 +246,19 @@ protected function _handleError($e) protected function _sendAuthenticationFailedHeaders($e) { switch ($e->getCode()) { - case constant('Horde_ActiveSync_Status::SERVER_ERROR_RETRY'): - $this->_logger->warn('Authentication server unavailable, sending 503 response.'); - header('HTTP/1.1 503 Unavailable'); - break; - case Horde_ActiveSync_Status::SYNC_NOT_ALLOWED: - case Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER: - $this->_logger->notice('Sending HTTP 403 Forbidden header response.'); - header('HTTP/1.1 403 Forbidden'); - break; - default: - $this->_logger->notice('Sending HTTP 401 Unauthorized header response.'); - header('HTTP/1.1 401 Unauthorized'); - header('WWW-Authenticate: Basic realm="Horde ActiveSync"'); + case constant('Horde_ActiveSync_Status::SERVER_ERROR_RETRY'): + $this->_logger->warn('Authentication server unavailable, sending 503 response.'); + header('HTTP/1.1 503 Unavailable'); + break; + case Horde_ActiveSync_Status::SYNC_NOT_ALLOWED: + case Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER: + $this->_logger->notice('Sending HTTP 403 Forbidden header response.'); + header('HTTP/1.1 403 Forbidden'); + break; + default: + $this->_logger->notice('Sending HTTP 401 Unauthorized header response.'); + header('HTTP/1.1 401 Unauthorized'); + header('WWW-Authenticate: Basic realm="Horde ActiveSync"'); } } } diff --git a/lib/Horde/Rpc/Exception.php b/lib/Horde/Rpc/Exception.php index a907a1e..3cf9b14 100644 --- a/lib/Horde/Rpc/Exception.php +++ b/lib/Horde/Rpc/Exception.php @@ -1,8 +1,9 @@ params)) { - $params = array(); + $params = []; } else { $params = $this->_objectsToArrays($request->params); if (!is_array($params)) { @@ -79,7 +80,7 @@ function getResponse($request) } // Return result. - $response = array('version' => '1.1', 'result' => $result); + $response = ['version' => '1.1', 'result' => $result]; if (isset($request->id)) { $response['id'] = $request->id; } @@ -97,7 +98,7 @@ function getResponse($request) * @return PEAR_Error An error object suitable for a JSON-RPC 1.1 * conform error result. */ - function _raiseError($error, $request) + public function _raiseError($error, $request) { $code = $userinfo = null; if ($error instanceof Exception) { @@ -111,13 +112,13 @@ function _raiseError($error, $request) $userinfo = $error->getUserInfo(); $error = $error->getMessage(); } - $error = array('name' => 'JSONRPCError', - 'code' => $code ? $code : 999, - 'message' => (string)$error); + $error = ['name' => 'JSONRPCError', + 'code' => $code ? $code : 999, + 'message' => (string) $error]; if ($userinfo) { $error['error'] = $userinfo; } - $response = array('version' => '1.1', 'error' => $error); + $response = ['version' => '1.1', 'error' => $error]; if (isset($request->id)) { $response['id'] = $request->id; } @@ -138,34 +139,34 @@ function _raiseError($error, $request) * for the method call. * @param mixed $unused This param is only here to be * compatible with Horde_Rpc, since that - * has $driver as the first param. + * has $driver as the first param. * @return mixed The returned result from the method. * @throws Horde_Rpc_Exception */ public static function request($url, $method, $client, $params = null, $unused = null) { - $headers = array( + $headers = [ 'User-Agent' => 'Horde RPC client', 'Accept' => 'application/json', - 'Content-Type' => 'application/json'); + 'Content-Type' => 'application/json']; - $data = array('version' => '1.1', 'method' => $method); + $data = ['version' => '1.1', 'method' => $method]; if (!empty($params)) { $data['params'] = $params; } $data = Horde_Serialize::serialize($data, Horde_Serialize::JSON); try { - $result = $client->post((string)$url, $data, $headers); + $result = $client->post((string) $url, $data, $headers); } catch (Horde_Http_Exception $e) { throw new Horde_Rpc_Exception($e->getMessage()); } if ($result->code == 500) { $response = Horde_Serialize::unserialize($result->getBody(), Horde_Serialize::JSON); - if (is_a($response, 'stdClass') && - isset($response->error) && - is_a($response->error, 'stdClass') && - isset($response->error->name) && - $response->error->name == 'JSONRPCError') { + if (is_a($response, 'stdClass') + && isset($response->error) + && is_a($response->error, 'stdClass') + && isset($response->error->name) + && $response->error->name == 'JSONRPCError') { throw new Horde_Rpc_Exception($response->error->message); /* @todo: Include more information if we have an Exception that can handle this. return PEAR::raiseError($response->error->message, @@ -190,7 +191,7 @@ public static function request($url, $method, $client, $params = null, $unused = * @return mixed stdClass objects are returned as asscociative arrays, * scalars as-is, and arrays with their elements converted. */ - function _objectsToArrays($data) + public function _objectsToArrays($data) { if (is_a($data, 'stdClass')) { $data = get_object_vars($data); diff --git a/lib/Horde/Rpc/Phpgw.php b/lib/Horde/Rpc/Phpgw.php index 1fa2ce9..9e53bb9 100644 --- a/lib/Horde/Rpc/Phpgw.php +++ b/lib/Horde/Rpc/Phpgw.php @@ -1,4 +1,5 @@ _server, $method, array('Horde_Rpc_Phpgw', '_dispatcher')); + xmlrpc_server_register_method($this->_server, $method, ['Horde_Rpc_Phpgw', '_dispatcher']); } } /** * Authorization is done by xmlrpc method system.login. */ - function authorize() + public function authorize() { return true; } @@ -53,7 +54,7 @@ function authorize() * * @return string The XML encoded response from the server. */ - function getResponse($request) + public function getResponse($request) { $response = null; return xmlrpc_server_call_method($this->_server, $request, $response); @@ -71,7 +72,7 @@ function getResponse($request) * * @return mixed The result of the called registry method. */ - function _dispatcher($method, $params, $data) + public function _dispatcher($method, $params, $data) { global $registry; $method = str_replace('.', '/', 'phpgw.' . $method); @@ -83,14 +84,14 @@ function _dispatcher($method, $params, $data) // Try to resume a session if (isset($params[0]['kp3']) && $params[0]["kp3"] == session_name() && session_id() != $params[0]["sessionid"]) { - Horde::log("manually reload session ".$params[0]["sessionid"], 'NOTICE'); + Horde::log("manually reload session " . $params[0]["sessionid"], 'NOTICE'); session_regenerate_id(); session_unset(); session_id($params[0]["sessionid"]); } // Be authenticated or call system.login. - $authenticated = $registry->isAuthenticated() || $method== "phpgw/system/login"; + $authenticated = $registry->isAuthenticated() || $method == "phpgw/system/login"; if ($authenticated) { Horde::log("rpc call $method allowed", 'NOTICE'); @@ -115,18 +116,18 @@ function _dispatcher($method, $params, $data) * parameters for the method call. * @param mixed $unused This param is only here to be * compatible with Horde_Rpc, since that - * has $driver as the first param. + * has $driver as the first param. * @return mixed The returned result from the method. * @throws Horde_Rpc_Exception */ public static function request($url, $method, $client, $params = null, $unused = null) { $options['method'] = 'POST'; - $headers = array( + $headers = [ 'User-Agent' => 'Horde RPC client', - 'Content-Type', 'text/xml'); + 'Content-Type', 'text/xml']; try { - $result = $client->post((string)$url, xmlrpc_encode_request($method, $params), $headers); + $result = $client->post((string) $url, xmlrpc_encode_request($method, $params), $headers); } catch (Horde_Http_Exception $e) { throw new Horde_Rpc_Exception($result); } @@ -138,8 +139,8 @@ public static function request($url, $method, $client, $params = null, $unused = $response = @xmlrpc_decode(substr($result->getBody(), strpos($result->getBody(), '_serviceName = $params['serviceName']; } - $this->_server = new SoapServer(null, array('uri' => (string)Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/rpc.php', true, -1))); + $this->_server = new SoapServer(null, ['uri' => (string) Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/rpc.php', true, -1)]); $this->_server->addFunction(SOAP_FUNCTIONS_ALL); $this->_server->setClass('Horde_Rpc_Soap_Caller', $params); } @@ -72,7 +73,7 @@ public function __construct($request, $params = array()) * * @return string The XML encoded response from the server. */ - function getResponse($request) + public function getResponse($request) { if ($request == 'disco' || $request == 'wsdl') { /*@TODO Replace with subcalls for disco and wsdl generation from the old SOAP driver. */ @@ -85,14 +86,21 @@ function getResponse($request) ob_start(); $this->_server->handle($request); Horde::log( - sprintf('SOAP call: %s(%s) by %s serviced in %d seconds, sent %d bytes in response', - $GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'], - implode(', ', array_map(function ($a) { return is_array($a) ? 'Array' : $a; }, - $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'])), - $GLOBALS['registry']->getAuth(), - time() - $beginTime, - ob_get_length()), - 'INFO'); + sprintf( + 'SOAP call: %s(%s) by %s serviced in %d seconds, sent %d bytes in response', + $GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'], + implode(', ', array_map( + function ($a) { + return is_array($a) ? 'Array' : $a; + }, + $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'] + )), + $GLOBALS['registry']->getAuth(), + time() - $beginTime, + ob_get_length() + ), + 'INFO' + ); return ob_get_clean(); } @@ -108,7 +116,7 @@ function getResponse($request) * @param SoapClient $soap A configured SoapClient object. * @param mixed $unused This param is only here to be * compatible with Horde_Rpc, since that - * has $driver as the first param. + * has $driver as the first param. * @return mixed The returned result from the method * @throws Horde_Rpc_Exception */ @@ -123,18 +131,18 @@ public static function request($url, $method, $params, $soap, $unused = null) } -class Horde_Rpc_Soap_Caller { - +class Horde_Rpc_Soap_Caller +{ /** * List of method names to allow. * * @var array */ - protected $_allowedMethods = array(); + protected $_allowedMethods = []; /** */ - public function __construct($params = array()) + public function __construct($params = []) { if (!empty($params['allowedMethods'])) { $this->_allowedMethods = $params['allowedMethods']; @@ -164,14 +172,14 @@ public function __call($method, $params) { $method = str_replace('.', '/', $method); - if (!empty($this->_params['allowedMethods']) && - !in_array($method, $this->_params['allowedMethods'])) { + if (!empty($this->_params['allowedMethods']) + && !in_array($method, $this->_params['allowedMethods'])) { return sprintf(Horde_Rpc_Translation::t("Method \"%s\" is not defined"), $method); } $GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'] = $method; - $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'] = - !empty($params) ? $params : array(); + $GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'] + = !empty($params) ? $params : []; if (!$GLOBALS['registry']->hasMethod($method)) { return sprintf(Horde_Rpc_Translation::t("Method \"%s\" is not defined"), $method); diff --git a/lib/Horde/Rpc/Syncml.php b/lib/Horde/Rpc/Syncml.php index 226c94a..321dd53 100644 --- a/lib/Horde/Rpc/Syncml.php +++ b/lib/Horde/Rpc/Syncml.php @@ -1,9 +1,10 @@ Horde::getTempDir().'/sync', + 'debug_dir' => Horde::getTempDir() . '/sync', /* Log all (wb)xml packets received or sent to debug_dir. */ 'debug_files' => true, /* Log everything. */ - 'log_level' => 'DEBUG'); + 'log_level' => 'DEBUG']; /* Create the backend. */ $GLOBALS['backend'] = Horde_SyncMl_Backend::factory('Horde', $backendparms); @@ -47,9 +48,14 @@ function getResponse($request) /* Handle request. */ $h = new Horde_SyncMl_ContentHandler(); $response = $h->process( - $request, $this->getResponseContentType(), - Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/rpc.php', - true, -1)); + $request, + $this->getResponseContentType(), + Horde::url( + $GLOBALS['registry']->get('webroot', 'horde') . '/rpc.php', + true, + -1 + ) + ); /* Close the backend. */ $GLOBALS['backend']->close(); @@ -62,7 +68,7 @@ function getResponse($request) * * @return string The MIME Content-Type of the RPC response. */ - function getResponseContentType() + public function getResponseContentType() { return 'application/vnd.syncml+xml'; } diff --git a/lib/Horde/Rpc/Syncml/Wbxml.php b/lib/Horde/Rpc/Syncml/Wbxml.php index f39c036..f5a048b 100644 --- a/lib/Horde/Rpc/Syncml/Wbxml.php +++ b/lib/Horde/Rpc/Syncml/Wbxml.php @@ -1,9 +1,10 @@ _server = $GLOBALS['injector']->getInstance('Horde_Dav_Server'); @@ -69,7 +70,5 @@ public function getResponse($request) /** * Implemented in Sabre\DAV\Server. */ - public function sendOutput($output) - { - } + public function sendOutput($output) {} } diff --git a/lib/Horde/Rpc/Xmlrpc.php b/lib/Horde/Rpc/Xmlrpc.php index ac33bc9..fbc14f0 100644 --- a/lib/Horde/Rpc/Xmlrpc.php +++ b/lib/Horde/Rpc/Xmlrpc.php @@ -1,9 +1,10 @@ _server = xmlrpc_server_create(); foreach ($GLOBALS['registry']->listMethods() as $method) { - xmlrpc_server_register_method($this->_server, str_replace('/', '.', $method), array('Horde_Rpc_Xmlrpc', '_dispatcher')); + xmlrpc_server_register_method($this->_server, str_replace('/', '.', $method), ['Horde_Rpc_Xmlrpc', '_dispatcher']); } } @@ -43,7 +44,7 @@ public function __construct($request, $params = array()) * * @return string The XML encoded response from the server. */ - function getResponse($request) + public function getResponse($request) { $response = null; return xmlrpc_server_call_method($this->_server, $request, $response); @@ -61,7 +62,7 @@ function getResponse($request) * * @return mixed The result of the called registry method. */ - function _dispatcher($method, $params, $data) + public function _dispatcher($method, $params, $data) { global $registry; @@ -73,8 +74,8 @@ function _dispatcher($method, $params, $data) try { $result = $registry->call($method, $params); } catch (Horde_Exception $e) { - $result = array('faultCode' => (int)$e->getCode(), - 'faultString' => $e->getMessage()); + $result = ['faultCode' => (int) $e->getCode(), + 'faultString' => $e->getMessage()]; } return $result; @@ -93,18 +94,18 @@ function _dispatcher($method, $params, $data) * parameters for the method call. * @param mixed $unused This param is only here to be * compatible with Horde_Rpc, since that - * has $driver as the first param. + * has $driver as the first param. * * @return mixed The returned result from the method. * @throws Horde_Rpc_Exception */ public static function request($url, $method, $client, $params = null, $unused = null) { - $headers = array( + $headers = [ 'User-Agent' => 'Horde RPC client', - 'Content-Type' => 'text/xml'); + 'Content-Type' => 'text/xml']; try { - $result = $client->post((string)$url, xmlrpc_encode_request($method, $params), $headers); + $result = $client->post((string) $url, xmlrpc_encode_request($method, $params), $headers); } catch (Horde_Http_Exception $e) { throw new Horde_Rpc_Exception($e); } @@ -116,8 +117,8 @@ public static function request($url, $method, $client, $params = null, $unused = $response = @xmlrpc_decode(substr($result->getBody(), strpos($result->getBody(), ' true)); +Horde_Registry::appInit('horde', ['cli' => true]); $conf['server']['name'] = 'localhost'; $conf['server']['port'] = 80; @@ -16,91 +17,119 @@ array_shift($argv); $testno = array_shift($argv); -$rpc_params = array( +$rpc_params = [ 'request.username' => @array_shift($argv), - 'request.password' => @array_shift($argv) -); -$language = isset($GLOBALS['language']) ? - $GLOBALS['language'] : - (isset($_SERVER['LANG']) ? $_SERVER['LANG'] : ''); + 'request.password' => @array_shift($argv), +]; +$language = $GLOBALS['language'] + ?? ($_SERVER['LANG'] ?? ''); if (!empty($language)) { - $rpc_params['request.headers'] = array('Accept-Language' => $language); + $rpc_params['request.headers'] = ['Accept-Language' => $language]; } -$http = $GLOBALS['injector']-> - getInstance('Horde_Core_Facotory_HttpClient')-> - create($rpc_params); +$http = $GLOBALS['injector'] + ->getInstance('Horde_Core_Facotory_HttpClient') + ->create($rpc_params); try { switch ($testno) { - case 0: - $response = Horde_Rpc_Xmlrpc::request(Horde::url('rpc.php', true, -1), - 'system.listMethods', $http); - break; - - case 1: - $response = Horde_Rpc_Xmlrpc::request(Horde::url('rpc.php', true, -1), - 'system.describeMethods', $http, - array('tasks.list')); - break; - - case 2: - $response = Horde_Rpc_Xmlrpc::request(Horde::url('rpc.php', true, -1), - 'tasks.listTasks', $http, array(0)); - break; - - case 3: - $response = Horde_Rpc_Xmlrpc::request('http://dev.horde.org/horde/rpc.php', - 'system.listMethods', $http); - break; - - case 4: - // @TODO: Need to instantiate a soap client. - $rpc_options = array( - 'login' => $rpc_params['username'], - 'password' => $rpc_params['password'], - 'namespace' => 'urn:horde', - 'timeout' => 5, - 'allowRedirects' => true, - 'maxRedirects' => 3, - 'location' => Horde::url(rpc.php, true, -1), - 'uri' => 'urn:horde', - 'exceptions' => true, - 'trace' => true, - ); - $soap = new SOAP_Client(null, $rpc_options); - $response = Horde_Rpc_Soap::request(Horde::url('rpc.php', true, -1), - 'tasks.listTasks', $soap, array()); - break; - - case 5: - - $rpc_options = array( - 'login' => $rpc_params['username'], - 'password' => $rpc_params['password'], - 'namespace' => 'urn:horde', - 'timeout' => 5, - 'allowRedirects' => true, - 'maxRedirects' => 3, - 'location' => Horde::url(rpc.php, true, -1), - 'uri' => 'urn:horde', - 'exceptions' => true, - 'trace' => true, - ); - $soap = new SOAP_Client(null, $rpc_options); - $response = Horde_Rpc_Soap::request(Horde::url('rpc.php', true, -1), - array_shift($argv), $soap, $argv); - - break; - - case 6: - $response = Horde_Rpc_Xmlrpc::request(Horde::url('rpc.php', true, -1), - array_shift($argv), $http, $argv); - break; - - case 7: - $response = Horde_Rpc_Jsonrpc::request(Horde::url('rpc.php', true, -1), - array_shift($argv), $http, $argv); - break; + case 0: + $response = Horde_Rpc_Xmlrpc::request( + Horde::url('rpc.php', true, -1), + 'system.listMethods', + $http + ); + break; + + case 1: + $response = Horde_Rpc_Xmlrpc::request( + Horde::url('rpc.php', true, -1), + 'system.describeMethods', + $http, + ['tasks.list'] + ); + break; + + case 2: + $response = Horde_Rpc_Xmlrpc::request( + Horde::url('rpc.php', true, -1), + 'tasks.listTasks', + $http, + [0] + ); + break; + + case 3: + $response = Horde_Rpc_Xmlrpc::request( + 'http://dev.horde.org/horde/rpc.php', + 'system.listMethods', + $http + ); + break; + + case 4: + // @TODO: Need to instantiate a soap client. + $rpc_options = [ + 'login' => $rpc_params['username'], + 'password' => $rpc_params['password'], + 'namespace' => 'urn:horde', + 'timeout' => 5, + 'allowRedirects' => true, + 'maxRedirects' => 3, + 'location' => Horde::url(rpc . php, true, -1), + 'uri' => 'urn:horde', + 'exceptions' => true, + 'trace' => true, + ]; + $soap = new SOAP_Client(null, $rpc_options); + $response = Horde_Rpc_Soap::request( + Horde::url('rpc.php', true, -1), + 'tasks.listTasks', + $soap, + [] + ); + break; + + case 5: + + $rpc_options = [ + 'login' => $rpc_params['username'], + 'password' => $rpc_params['password'], + 'namespace' => 'urn:horde', + 'timeout' => 5, + 'allowRedirects' => true, + 'maxRedirects' => 3, + 'location' => Horde::url(rpc . php, true, -1), + 'uri' => 'urn:horde', + 'exceptions' => true, + 'trace' => true, + ]; + $soap = new SOAP_Client(null, $rpc_options); + $response = Horde_Rpc_Soap::request( + Horde::url('rpc.php', true, -1), + array_shift($argv), + $soap, + $argv + ); + + break; + + case 6: + $response = Horde_Rpc_Xmlrpc::request( + Horde::url('rpc.php', true, -1), + array_shift($argv), + $http, + $argv + ); + break; + + case 7: + $response = Horde_Rpc_Jsonrpc::request( + Horde::url('rpc.php', true, -1), + array_shift($argv), + $http, + $argv + ); + break; } echo "===value======\n"; diff --git a/test/Unit/ActiveSyncTest.php b/test/Unit/ActiveSyncTest.php index 1618569..1d5c8cc 100644 --- a/test/Unit/ActiveSyncTest.php +++ b/test/Unit/ActiveSyncTest.php @@ -18,6 +18,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; +use Throwable; #[CoversClass(Horde_Rpc_ActiveSync::class)] class ActiveSyncTest extends TestCase @@ -60,7 +61,7 @@ public function testNoPwInLogmessages(): void try { $this->activeSyncRpc->getResponse($request); - } catch (\Throwable $e) { + } catch (Throwable $e) { // Catch any exception that might be thrown instead of exit } From d3dc4fd767561f643d54c2f60401ca14c7df2af7 Mon Sep 17 00:00:00 2001 From: Ralf Lang Date: Mon, 6 Apr 2026 20:15:01 +0200 Subject: [PATCH 4/4] chore(hordeymlfile): Add keywords --- .horde.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.horde.yml b/.horde.yml index 8eaeb93..d8b0a8a 100644 --- a/.horde.yml +++ b/.horde.yml @@ -36,7 +36,7 @@ license: uri: http://www.horde.org/licenses/lgpl21 dependencies: required: - php: ^7.4 || ^8 + php: ^8 composer: horde/core: ^3 horde/dav: ^2 @@ -59,3 +59,9 @@ dependencies: dev: composer: horde/activesync: ^3 + horde/http: ^3 +keywords: + - soap + - jsonrpc + - activesync +vendor: horde