diff --git a/.horde.yml b/.horde.yml index 9fbeb6f..0dcca76 100644 --- a/.horde.yml +++ b/.horde.yml @@ -35,7 +35,6 @@ dependencies: php: ^7.4 || ^8 composer: horde/exception: ^3 - horde/listheaders: ^2 horde/mail: ^3 horde/stream: ^2 horde/stream_filter: ^3 diff --git a/composer.json b/composer.json index f0ec72d..d81e901 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,5 @@ { + "minimum-stability": "dev", "name": "horde/mime", "description": "MIME library", "type": "library", @@ -21,7 +22,6 @@ "require": { "php": "^7.4 || ^8", "horde/exception": "^3 || dev-FRAMEWORK_6_0", - "horde/listheaders": "^2 || dev-FRAMEWORK_6_0", "horde/mail": "^3 || dev-FRAMEWORK_6_0", "horde/stream": "^2 || dev-FRAMEWORK_6_0", "horde/stream_filter": "^3 || dev-FRAMEWORK_6_0", diff --git a/lib/Horde/Mime/Headers.php b/lib/Horde/Mime/Headers.php index 72455a5..05cc907 100644 --- a/lib/Horde/Mime/Headers.php +++ b/lib/Horde/Mime/Headers.php @@ -54,6 +54,13 @@ class Horde_Mime_Headers implements ArrayAccess, IteratorAggregate, Serializable */ protected $_headers; + /** + * The EOL string to use for output. + * + * @var string + */ + protected $_eol = "\n"; + /** * Constructor. */ @@ -512,50 +519,85 @@ public function getIterator() return new ArrayIterator($this->_headers); } - /* Deprecated functions */ + /* Header value access */ - /** - * Handle deprecated methods. - */ - public function __call($name, $arguments) - { - $d = new Horde_Mime_Headers_Deprecated($this); - return call_user_func_array([$d, $name], $arguments); - } + /* Constants for getValue(). */ + public const VALUE_STRING = 1; + public const VALUE_BASE = 2; + public const VALUE_PARAMS = 3; /** - * Handle deprecated static methods. + * Get a header value in a specific format. + * + * @param string $header Header name. + * @param int $type VALUE_STRING (full value), VALUE_BASE (base + * value), or VALUE_PARAMS (parameters only). + * + * @return mixed Header value, or null if not found. */ - public static function __callStatic($name, $arguments) + public function getValue($header, $type = self::VALUE_STRING) { - $d = new Horde_Mime_Headers_Deprecated(new Horde_Mime_Headers()); - return call_user_func_array([$d, $name], $arguments); - } + if (!($ob = $this[$header])) { + return null; + } - /** - * @deprecated - */ - protected $_eol = "\n"; + switch ($type) { + case self::VALUE_BASE: + $tmp = $ob->value; + break; + + case self::VALUE_PARAMS: + return array_change_key_case($ob->params, CASE_LOWER); + + case self::VALUE_STRING: + $tmp = $ob->full_value; + break; + } + + return (is_array($tmp) && (count($tmp) === 1)) + ? reset($tmp) + : $tmp; + } /** - * @deprecated + * Replace a header value. + * + * @param string $header Header name. + * @param string $value Header value. + * @param array $opts Additional options passed to addHeader(). */ - public function setEOL($eol) + public function replaceHeader($header, $value, array $opts = []) { - $this->_eol = $eol; + $this->removeHeader($header); + $this->addHeader($header, $value, $opts); } /** - * @deprecated + * Return the list of single-value header fields. + * + * @param bool $list Include list header fields? + * + * @return array List of single-value header field names (lowercase). */ - public function getEOL() + public function singleFields($list = true) { - return $this->_eol; - } + $fields = [ + 'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to', + 'message-id', 'in-reply-to', 'references', 'subject', + 'content-md5', 'mime-version', 'content-type', + 'content-transfer-encoding', 'content-id', 'content-description', + 'content-base', 'content-disposition', 'content-duration', + 'content-location', 'content-features', 'content-language', + 'content-alternative', 'importance', 'x-priority', + ]; - /* Constants for getValue(). @deprecated */ - public const VALUE_STRING = 1; - public const VALUE_BASE = 2; - public const VALUE_PARAMS = 3; + $list_fields = [ + 'list-help', 'list-unsubscribe', 'list-subscribe', 'list-owner', + 'list-post', 'list-archive', 'list-id', + ]; + return $list + ? array_merge($fields, $list_fields) + : $fields; + } } diff --git a/lib/Horde/Mime/Headers/Deprecated.php b/lib/Horde/Mime/Headers/Deprecated.php deleted file mode 100644 index fb06c6f..0000000 --- a/lib/Horde/Mime/Headers/Deprecated.php +++ /dev/null @@ -1,268 +0,0 @@ - - * @deprecated - * @category Horde - * @copyright 2014-2016 Horde LLC - * @internal - * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 - * @package Mime - * @since 2.5.0 - */ -class Horde_Mime_Headers_Deprecated -{ - /** - * Base headers object. - * - * @var Horde_Mime_Headers - */ - private $_headers; - - /** - */ - public function __construct(Horde_Mime_Headers $headers) - { - $this->_headers = $headers; - } - - /** - */ - public function addMessageIdHeader() - { - $this->_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); - } - - /** - */ - public function addUserAgentHeader() - { - $this->_headers->addHeaderOb(Horde_Mime_Headers_UserAgent::create()); - } - - /** - */ - public function getUserAgent() - { - return strval(Horde_Mime_Headers_UserAgent::create()); - } - - /** - */ - public function setUserAgent($agent) - { - $this->_headers->addHeaderOb( - new Horde_Mime_Headers_UserAgent(null, $agent) - ); - } - - /** - */ - public function addReceivedHeader(array $opts = []) - { - $old_error = error_reporting(0); - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - /* This indicates the user is connecting through a proxy. */ - $remote_path = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); - $remote_addr = $remote_path[0]; - if (!empty($opts['dns'])) { - $remote = $remote_addr; - try { - if ($response = $opts['dns']->query($remote_addr, 'PTR')) { - foreach ($response->answer as $val) { - if (isset($val->ptrdname)) { - $remote = $val->ptrdname; - break; - } - } - } - } catch (NetDNS2Exception $e) { - } - } else { - $remote = gethostbyaddr($remote_addr); - } - } else { - $remote_addr = $_SERVER['REMOTE_ADDR']; - if (empty($_SERVER['REMOTE_HOST'])) { - if (!empty($opts['dns'])) { - $remote = $remote_addr; - try { - if ($response = $opts['dns']->query($remote_addr, 'PTR')) { - foreach ($response->answer as $val) { - if (isset($val->ptrdname)) { - $remote = $val->ptrdname; - break; - } - } - } - } catch (NetDNS2Exception $e) { - } - } else { - $remote = gethostbyaddr($remote_addr); - } - } else { - $remote = $_SERVER['REMOTE_HOST']; - } - } - error_reporting($old_error); - - if (!empty($_SERVER['REMOTE_IDENT'])) { - $remote_ident = $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' '; - } elseif ($remote != $_SERVER['REMOTE_ADDR']) { - $remote_ident = $remote . ' '; - } else { - $remote_ident = ''; - } - - if (!empty($opts['server'])) { - $server_name = $opts['server']; - } elseif (!empty($_SERVER['SERVER_NAME'])) { - $server_name = $_SERVER['SERVER_NAME']; - } elseif (!empty($_SERVER['HTTP_HOST'])) { - $server_name = $_SERVER['HTTP_HOST']; - } else { - $server_name = 'unknown'; - } - - $is_ssl = isset($_SERVER['HTTPS']) - && $_SERVER['HTTPS'] != 'off'; - - if ($remote == $remote_addr) { - $remote = '[' . $remote . ']'; - } - - $this->_headers->addHeaderOb(new Horde_Mime_Headers_Element_Multiple( - 'Received', - 'from ' . $remote . ' (' . $remote_ident - . '[' . $remote_addr . ']) ' - . 'by ' . $server_name . ' (Horde Framework) with HTTP' - . ($is_ssl ? 'S' : '') . '; ' . date('r') - )); - } - - /** - */ - public function getOb($field) - { - return ($h = $this->_headers[$field]) - ? $h->getAddressList(true) - : null; - } - - /** - */ - public function getValue($header, $type = Horde_Mime_Headers::VALUE_STRING) - { - if (!($ob = $this->_headers[$header])) { - return null; - } - - switch ($type) { - case Horde_Mime_Headers::VALUE_BASE: - $tmp = $ob->value; - break; - - case Horde_Mime_Headers::VALUE_PARAMS: - return array_change_key_case($ob->params, CASE_LOWER); - - case Horde_Mime_Headers::VALUE_STRING: - $tmp = $ob->full_value; - break; - } - - return (is_array($tmp) && (count($tmp) === 1)) - ? reset($tmp) - : $tmp; - } - - /** - */ - public function listHeaders() - { - $lhdrs = new Horde_ListHeaders(); - return $lhdrs->headers(); - } - - /** - */ - public function listHeadersExist() - { - $lhdrs = new Horde_ListHeaders(); - return $lhdrs->listHeadersExist($this->_headers); - } - - /** - */ - public function replaceHeader($header, $value, array $opts = []) - { - $this->_headers->removeHeader($header); - $this->_headers->addHeader($header, $value, $opts); - } - - /** - */ - public function getString($header) - { - return (($hdr = $this->_headers[$header]) === null) - ? null - : $this->_headers[$header]->name; - } - - /** - */ - public function addressFields() - { - return [ - 'from', 'to', 'cc', 'bcc', 'reply-to', 'resent-to', 'resent-cc', - 'resent-bcc', 'resent-from', 'sender', - ]; - } - - /** - */ - public function singleFields($list = true) - { - $fields = [ - 'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to', - 'message-id', 'in-reply-to', 'references', 'subject', - 'content-md5', 'mime-version', 'content-type', - 'content-transfer-encoding', 'content-id', 'content-description', - 'content-base', 'content-disposition', 'content-duration', - 'content-location', 'content-features', 'content-language', - 'content-alternative', 'importance', 'x-priority', - ]; - - $list_fields = [ - 'list-help', 'list-unsubscribe', 'list-subscribe', 'list-owner', - 'list-post', 'list-archive', 'list-id', - ]; - - return $list - ? array_merge($fields, $list_fields) - : $fields; - } - - /** - */ - public function mimeParamFields() - { - return ['content-type', 'content-disposition']; - } - -} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b521b83..8db664d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,26 +1,26 @@ + displayDetailsOnTestsThatTriggerDeprecations="true" + displayDetailsOnTestsThatTriggerWarnings="true" + displayDetailsOnTestsThatTriggerErrors="true" + displayDetailsOnTestsThatTriggerNotices="true"> test - - + - src + lib - + diff --git a/test/AllTests.php b/test/AllTests.php deleted file mode 100644 index 6d76d1d..0000000 --- a/test/AllTests.php +++ /dev/null @@ -1,8 +0,0 @@ -run(); diff --git a/test/Unnamespaced/ContentParam/DecodeTest.php b/test/Unnamespaced/ContentParam/DecodeTest.php index 6ebccab..6f77308 100644 --- a/test/Unnamespaced/ContentParam/DecodeTest.php +++ b/test/Unnamespaced/ContentParam/DecodeTest.php @@ -45,7 +45,7 @@ public function testDecode($string, $expected) ); } - public function decodeProvider() + public static function decodeProvider() { return [ [ diff --git a/test/Unnamespaced/ContentParamTest.php b/test/Unnamespaced/ContentParamTest.php index 905b36c..998f77d 100644 --- a/test/Unnamespaced/ContentParamTest.php +++ b/test/Unnamespaced/ContentParamTest.php @@ -46,7 +46,7 @@ public function testEncode($params, $opts, $expected) ); } - public function encodeProvider() + public static function encodeProvider() { return [ [ @@ -140,7 +140,7 @@ public function testDecode($in, $val_expected, $params_expected) ); } - public function decodeProvider() + public static function decodeProvider() { return [ [ diff --git a/test/Unnamespaced/Filter/EncodingTest.php b/test/Unnamespaced/Filter/EncodingTest.php index 6ba49b6..c52831a 100644 --- a/test/Unnamespaced/Filter/EncodingTest.php +++ b/test/Unnamespaced/Filter/EncodingTest.php @@ -57,7 +57,7 @@ public function testBodyFilter($data, $result) ); } - public function bodyFilterProvider() + public static function bodyFilterProvider() { return [ [ diff --git a/test/Unnamespaced/Headers/ContentDispositionTest.php b/test/Unnamespaced/Headers/ContentDispositionTest.php index 2699abf..2b7362e 100644 --- a/test/Unnamespaced/Headers/ContentDispositionTest.php +++ b/test/Unnamespaced/Headers/ContentDispositionTest.php @@ -53,7 +53,7 @@ public function testParsingOfInput($input, $expected_val, $expected_params) ); } - public function parsingOfInputProvider() + public static function parsingOfInputProvider() { return [ [ @@ -123,7 +123,7 @@ public function testFullValue($value, $params, $expected) ); } - public function fullValueProvider() + public static function fullValueProvider() { return [ [ @@ -212,7 +212,7 @@ public function testIsDefault($value, $is_default) } } - public function isDefaultProvider() + public static function isDefaultProvider() { return [ [ diff --git a/test/Unnamespaced/Headers/ContentIdTest.php b/test/Unnamespaced/Headers/ContentIdTest.php index 2be8adf..bf11fe8 100644 --- a/test/Unnamespaced/Headers/ContentIdTest.php +++ b/test/Unnamespaced/Headers/ContentIdTest.php @@ -44,7 +44,7 @@ public function testValue($input, $expected_val) $this->assertFalse($ob->isDefault()); } - public function valueProvider() + public static function valueProvider() { return [ [ diff --git a/test/Unnamespaced/Headers/ContentLanguageTest.php b/test/Unnamespaced/Headers/ContentLanguageTest.php index 46bb6c1..868db41 100644 --- a/test/Unnamespaced/Headers/ContentLanguageTest.php +++ b/test/Unnamespaced/Headers/ContentLanguageTest.php @@ -47,7 +47,7 @@ public function testParsingOfInput($input, $expected_val, $expected_langs) ); } - public function parsingOfInputProvider() + public static function parsingOfInputProvider() { return [ [ diff --git a/test/Unnamespaced/Headers/ContentTransferEncodingTest.php b/test/Unnamespaced/Headers/ContentTransferEncodingTest.php index 72755cb..1a15c14 100644 --- a/test/Unnamespaced/Headers/ContentTransferEncodingTest.php +++ b/test/Unnamespaced/Headers/ContentTransferEncodingTest.php @@ -48,7 +48,7 @@ public function testValues($input, $expected_val, $is_default) } } - public function valuesProvider() + public static function valuesProvider() { return [ [ diff --git a/test/Unnamespaced/Headers/ContentTypeTest.php b/test/Unnamespaced/Headers/ContentTypeTest.php index 4dd1eab..6a5dd93 100644 --- a/test/Unnamespaced/Headers/ContentTypeTest.php +++ b/test/Unnamespaced/Headers/ContentTypeTest.php @@ -53,7 +53,7 @@ public function testParsingOfInput($input, $expected_val, $expected_params) ); } - public function parsingOfInputProvider() + public static function parsingOfInputProvider() { return [ [ @@ -179,7 +179,7 @@ public function testParsingContentTypeValue($value, $primary, $sub) ); } - public function parsingContentTypeValueProvider() + public static function parsingContentTypeValueProvider() { return [ [ @@ -220,7 +220,7 @@ public function testTypeCharsetProperty($value, $charset, $expected) ); } - public function typeCharsetPropertyProvider() + public static function typeCharsetPropertyProvider() { return [ [ @@ -256,7 +256,7 @@ public function testMultipartPartsHaveBoundary($value, $has_boundary) } } - public function multipartPartsHaveBoundary() + public static function multipartPartsHaveBoundary() { return [ [ @@ -289,7 +289,7 @@ public function testCharsetIsLowercase($charset, $expected) ); } - public function charsetIsLowercaseProvider() + public static function charsetIsLowercaseProvider() { return [ [ @@ -336,7 +336,7 @@ public function testIsDefault($value, $is_default) } } - public function isDefaultProvider() + public static function isDefaultProvider() { return [ [ diff --git a/test/Unnamespaced/HeadersTest.php b/test/Unnamespaced/HeadersTest.php index d0c2f75..90e5711 100644 --- a/test/Unnamespaced/HeadersTest.php +++ b/test/Unnamespaced/HeadersTest.php @@ -101,7 +101,7 @@ public function testSerialize($header, $value) ); } - public function serializeProvider() + public static function serializeProvider() { return [ [ @@ -142,7 +142,7 @@ public function testNormalHeaderDecode($header, $value, $decoded) ); } - public function normalHeaderDecodeProvider() + public static function normalHeaderDecodeProvider() { return [ [ @@ -195,7 +195,7 @@ public function testContentParamHeaderDecode( ); } - public function contentParamHeaderDecodeProvider() + public static function contentParamHeaderDecodeProvider() { return [ [ @@ -237,7 +237,7 @@ public function testHeaderAutoDetectCharset($header, $value, $decoded) ); } - public function headerAutoDetectCharsetProvider() + public static function headerAutoDetectCharsetProvider() { return [ [ @@ -282,7 +282,7 @@ public function testHeaderEncode( ); } - public function headerEncodeProvider() + public static function headerEncodeProvider() { return [ /* Single address header */ @@ -358,7 +358,7 @@ public function testMultivalueHeaders($header, $in, $expected) ); } - public function multivalueHeadersProvider() + public static function multivalueHeadersProvider() { $expected = 'recipient1@example.com, recipient2@example.com'; @@ -399,7 +399,7 @@ public function testAddHeaderWithGroup($header, $email) ); } - public function addHeaderWithGroupProvider() + public static function addHeaderWithGroupProvider() { return [ [ @@ -428,7 +428,7 @@ public function testUnencodedMimeHeader($header, $in, $decoded) ); } - public function unencodeMimeHeaderProvider() + public static function unencodeMimeHeaderProvider() { return [ [ @@ -467,7 +467,7 @@ public function testParseContentDispositionHeaderWithUtf8Data( ); } - public function parseContentDispositionHeaderWithUtf8DataProvider() + public static function parseContentDispositionHeaderWithUtf8DataProvider() { return [ [ @@ -570,7 +570,7 @@ public function testUndisclosedHeaderParsing($header, $value) ); } - public function undisclosedHeaderParsingProvider() + public static function undisclosedHeaderParsingProvider() { return [ ['To', 'undisclosed-recipients'], @@ -665,7 +665,7 @@ public function testMultiplePriorityHeaders($header, $data, $value) } - public function multiplePriorityHeadersProvider() + public static function multiplePriorityHeadersProvider() { return [ [ @@ -713,7 +713,7 @@ public function testAddHeaderOb($ob, $valid) } } - public function addHeaderObProvider() + public static function addHeaderObProvider() { return [ [ @@ -744,7 +744,7 @@ public function testHeaderGeneration($label, $data, $class) $this->assertInstanceOf($class, $ob); } - public function headerGenerationProvider() + public static function headerGenerationProvider() { return [ [ diff --git a/test/Unnamespaced/MdnTest.php b/test/Unnamespaced/MdnTest.php index c1b8aa8..8eec551 100644 --- a/test/Unnamespaced/MdnTest.php +++ b/test/Unnamespaced/MdnTest.php @@ -49,7 +49,7 @@ public function testGetMdnReturnAddr($email) ); } - public function getMdnReturnAddrProvider() + public static function getMdnReturnAddrProvider() { $email = 'foo1@example.com, Test '; @@ -77,7 +77,7 @@ public function testUserConfirmationNeeded($h, $expected) } } - public function userConfirmationNeededProvider() + public static function userConfirmationNeededProvider() { $out = []; @@ -90,12 +90,14 @@ public function userConfirmationNeededProvider() $h->addHeader('Return-Path', 'foo2@example.com'); $out[] = [clone $h, true]; - $h->replaceHeader('Return-Path', 'foo@example.com'); + $h->removeHeader('Return-Path'); + $h->addHeader('Return-Path', 'foo@example.com'); $h->addHeader(Horde_Mime_Mdn::MDN_HEADER, 'FOO@example.com'); $out[] = [clone $h, true]; - $h->replaceHeader(Horde_Mime_Mdn::MDN_HEADER, 'foo@EXAMPLE.com'); + $h->removeHeader(Horde_Mime_Mdn::MDN_HEADER); + $h->addHeader(Horde_Mime_Mdn::MDN_HEADER, 'foo@EXAMPLE.com'); $out[] = [clone $h, false]; return $out; diff --git a/test/Unnamespaced/MimeIdTest.php b/test/Unnamespaced/MimeIdTest.php index 4edc379..816d36b 100644 --- a/test/Unnamespaced/MimeIdTest.php +++ b/test/Unnamespaced/MimeIdTest.php @@ -53,7 +53,7 @@ public function testIdArithmetic($id, $action, $opts, $expected) ); } - public function idArithmeticProvider() + public static function idArithmeticProvider() { return [ [ @@ -145,7 +145,7 @@ public function testIsChild($base, $id, $expected) } } - public function isChildProvider() + public static function isChildProvider() { return [ ['1', '1.0', true], diff --git a/test/Unnamespaced/MimeTest.php b/test/Unnamespaced/MimeTest.php index 3c81dcf..973ebfd 100644 --- a/test/Unnamespaced/MimeTest.php +++ b/test/Unnamespaced/MimeTest.php @@ -40,7 +40,7 @@ public function testIs8bit($data, $expected) ); } - public function is8bitProvider() + public static function is8bitProvider() { return [ ['A', false], @@ -70,7 +70,7 @@ public function testDecode($data, $expected) ); } - public function decodeProvider() + public static function decodeProvider() { return [ [ @@ -123,7 +123,7 @@ public function testEncode($data, $charset, $expected) ); } - public function encodeProvider() + public static function encodeProvider() { return [ /* Adapted from Dovecot's diff --git a/test/Unnamespaced/PartTest.php b/test/Unnamespaced/PartTest.php index 159f0b5..b5666e8 100644 --- a/test/Unnamespaced/PartTest.php +++ b/test/Unnamespaced/PartTest.php @@ -263,7 +263,7 @@ public function testContentsTransferDecoding($data, $encoding, $text) ); } - public function contentsTransferDecodingProvider() + public static function contentsTransferDecodingProvider() { return [ [ @@ -306,7 +306,7 @@ public function testSetType($data, $type, $boundary) } } - public function setTypeProvider() + public static function setTypeProvider() { return [ [ @@ -400,7 +400,7 @@ public function testSetDisposition($disp) ); } - public function setDispositionProvider() + public static function setDispositionProvider() { return [ ['attachment'], @@ -701,7 +701,7 @@ public function testSetCharset($charset, $expected) ); } - public function setCharsetProvider() + public static function setCharsetProvider() { return [ ['utf-8', 'text/plain; charset=utf-8'], diff --git a/test/Unnamespaced/RelatedTest.php b/test/Unnamespaced/RelatedTest.php index ca3a5b1..66a207c 100644 --- a/test/Unnamespaced/RelatedTest.php +++ b/test/Unnamespaced/RelatedTest.php @@ -41,7 +41,7 @@ public function testStart($ob, $id) ); } - public function startProvider() + public static function startProvider() { return [ [ @@ -70,7 +70,7 @@ public function testSearch($ob, $search, $id) ); } - public function searchProvider() + public static function searchProvider() { return [ [ @@ -101,7 +101,7 @@ public function testIterator($ob, $ids) ); } - public function iteratorProvider() + public static function iteratorProvider() { return [ [ diff --git a/test/Unnamespaced/UudecodeTest.php b/test/Unnamespaced/UudecodeTest.php index 06b94cb..e09404d 100644 --- a/test/Unnamespaced/UudecodeTest.php +++ b/test/Unnamespaced/UudecodeTest.php @@ -54,7 +54,7 @@ public function testUudecode($data, $expected) } } - public function uudecodeProvider() + public static function uudecodeProvider() { return [ [ diff --git a/test/bootstrap.php b/test/bootstrap.php deleted file mode 100644 index c0c37a9..0000000 --- a/test/bootstrap.php +++ /dev/null @@ -1,14 +0,0 @@ -