[SECURITY] XML entity expansion
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / Parser / ExtensionXmlPushParser.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 /**
17 * Module: Extension manager - Extension.xml push-parser
18 */
19 /**
20 * Parser for TYPO3's extension.xml file.
21 *
22 * Depends on PHP ext/xml which should be available
23 * with PHP 4+. This is the parser used in TYPO3
24 * Core <= 4.3 (without the "collect all data in one
25 * array" behaviour).
26 * Notice: ext/xml has proven to be buggy with entities.
27 * Use at least PHP 5.2.9+ and libxml2 2.7.3+!
28 *
29 * @author Marcus Krause <marcus#exp2010@t3sec.info>
30 * @author Steffen Kamper <info@sk-typo3.de>
31 * @since 2010-02-10
32 */
33 class ExtensionXmlPushParser extends AbstractExtensionXmlParser {
34
35 /**
36 * Keeps current element to process.
37 *
38 * @var string
39 */
40 protected $element = NULL;
41
42 /**
43 * Class constructor.
44 */
45 public function __construct() {
46 $this->requiredPhpExtensions = 'xml';
47 }
48
49 /**
50 * Create required parser
51 *
52 * @return void
53 */
54 protected function createParser() {
55 $this->objXml = xml_parser_create();
56 xml_set_object($this->objXml, $this);
57 }
58
59 /**
60 * Method parses an extensions.xml file.
61 *
62 * @param string $file GZIP stream resource
63 * @return void
64 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parse errors
65 */
66 public function parseXml($file) {
67 $this->createParser();
68 if (!is_resource($this->objXml)) {
69 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640663);
70 }
71 // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
72 $previousValueOfEntityLoader = libxml_disable_entity_loader(TRUE);
73 // keep original character case of XML document
74 xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, FALSE);
75 xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, FALSE);
76 xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
77 xml_set_element_handler($this->objXml, 'startElement', 'endElement');
78 xml_set_character_data_handler($this->objXml, 'characterData');
79 if (!($fp = fopen($file, 'r'))) {
80 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', htmlspecialchars($file)), 1342640689);
81 }
82 while ($data = fread($fp, 4096)) {
83 if (!xml_parse($this->objXml, $data, feof($fp))) {
84 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), htmlspecialchars($file)), 1342640703);
85 }
86 }
87 libxml_disable_entity_loader($previousValueOfEntityLoader);
88 xml_parser_free($this->objXml);
89 }
90
91 /**
92 * Method is invoked when parser accesses start tag of an element.
93 *
94 * @param resource $parser parser resource
95 * @param string $elementName element name at parser's current position
96 * @param array $attrs array of an element's attributes if available
97 * @return void
98 */
99 protected function startElement($parser, $elementName, $attrs) {
100 switch ($elementName) {
101 case 'extension':
102 $this->extensionKey = $attrs['extensionkey'];
103 break;
104 case 'version':
105 $this->version = $attrs['version'];
106 break;
107 default:
108 $this->element = $elementName;
109 }
110 }
111
112 /**
113 * Method is invoked when parser accesses end tag of an element.
114 *
115 * @param resource $parser parser resource
116 * @param string $elementName: element name at parser's current position
117 * @return void
118 */
119 protected function endElement($parser, $elementName) {
120 switch ($elementName) {
121 case 'extension':
122 $this->resetProperties(TRUE);
123 break;
124 case 'version':
125 $this->notify();
126 $this->resetProperties();
127 break;
128 default:
129 $this->element = NULL;
130 }
131 }
132
133 /**
134 * Method is invoked when parser accesses any character other than elements.
135 *
136 * @param resource $parser parser resource
137 * @param string $data: an element's value
138 * @return void
139 */
140 protected function characterData($parser, $data) {
141 if (isset($this->element)) {
142 switch ($this->element) {
143 case 'downloadcounter':
144 // downloadcounter could be a child node of
145 // extension or version
146 if ($this->version == NULL) {
147 $this->extensionDownloadCounter = $data;
148 } else {
149 $this->versionDownloadCounter = $data;
150 }
151 break;
152 case 'title':
153 $this->title = $data;
154 break;
155 case 'description':
156 $this->description = $data;
157 break;
158 case 'state':
159 $this->state = $data;
160 break;
161 case 'reviewstate':
162 $this->reviewstate = $data;
163 break;
164 case 'category':
165 $this->category = $data;
166 break;
167 case 'lastuploaddate':
168 $this->lastuploaddate = $data;
169 break;
170 case 'uploadcomment':
171 $this->uploadcomment = $data;
172 break;
173 case 'dependencies':
174 $this->dependencies = $this->convertDependencies($data);
175 break;
176 case 'authorname':
177 $this->authorname = $data;
178 break;
179 case 'authoremail':
180 $this->authoremail = $data;
181 break;
182 case 'authorcompany':
183 $this->authorcompany = $data;
184 break;
185 case 'ownerusername':
186 $this->ownerusername = $data;
187 break;
188 case 't3xfilemd5':
189 $this->t3xfilemd5 = $data;
190 break;
191 }
192 }
193 }
194 }