[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 /**
18 * Module: Extension manager - Extension.xml push-parser
19 */
20 /**
21 * Parser for TYPO3's extension.xml file.
22 *
23 * Depends on PHP ext/xml which should be available
24 * with PHP 4+. This is the parser used in TYPO3
25 * Core <= 4.3 (without the "collect all data in one
26 * array" behaviour).
27 * Notice: ext/xml has proven to be buggy with entities.
28 * Use at least PHP 5.2.9+ and libxml2 2.7.3+!
29 * @since 2010-02-10
30 */
31 class ExtensionXmlPushParser extends AbstractExtensionXmlParser
32 {
33 /**
34 * Keeps current element to process.
35 *
36 * @var string
37 */
38 protected $element = null;
39
40 /**
41 * Class constructor.
42 */
43 public function __construct()
44 {
45 $this->requiredPhpExtensions = 'xml';
46 }
47
48 /**
49 * Create required parser
50 *
51 * @return void
52 */
53 protected function createParser()
54 {
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 {
68 $this->createParser();
69 if (!is_resource($this->objXml)) {
70 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640663);
71 }
72 // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
73 $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
74 // keep original character case of XML document
75 xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
76 xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
77 xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
78 xml_set_element_handler($this->objXml, 'startElement', 'endElement');
79 xml_set_character_data_handler($this->objXml, 'characterData');
80 if (!($fp = fopen($file, 'r'))) {
81 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640689);
82 }
83 while ($data = fread($fp, 4096)) {
84 if (!xml_parse($this->objXml, $data, feof($fp))) {
85 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), $file), 1342640703);
86 }
87 }
88 libxml_disable_entity_loader($previousValueOfEntityLoader);
89 xml_parser_free($this->objXml);
90 }
91
92 /**
93 * Method is invoked when parser accesses start tag of an element.
94 *
95 * @param resource $parser parser resource
96 * @param string $elementName element name at parser's current position
97 * @param array $attrs array of an element's attributes if available
98 * @return void
99 */
100 protected function startElement($parser, $elementName, $attrs)
101 {
102 switch ($elementName) {
103 case 'extension':
104 $this->extensionKey = $attrs['extensionkey'];
105 break;
106 case 'version':
107 $this->version = $attrs['version'];
108 break;
109 default:
110 $this->element = $elementName;
111 }
112 }
113
114 /**
115 * Method is invoked when parser accesses end tag of an element.
116 *
117 * @param resource $parser parser resource
118 * @param string $elementName: element name at parser's current position
119 * @return void
120 */
121 protected function endElement($parser, $elementName)
122 {
123 switch ($elementName) {
124 case 'extension':
125 $this->resetProperties(true);
126 break;
127 case 'version':
128 $this->notify();
129 $this->resetProperties();
130 break;
131 default:
132 $this->element = null;
133 }
134 }
135
136 /**
137 * Method is invoked when parser accesses any character other than elements.
138 *
139 * @param resource $parser parser resource
140 * @param string $data: an element's value
141 * @return void
142 */
143 protected function characterData($parser, $data)
144 {
145 if (isset($this->element)) {
146 switch ($this->element) {
147 case 'downloadcounter':
148 // downloadcounter could be a child node of
149 // extension or version
150 if ($this->version == null) {
151 $this->extensionDownloadCounter = $data;
152 } else {
153 $this->versionDownloadCounter = $data;
154 }
155 break;
156 case 'title':
157 $this->title = $data;
158 break;
159 case 'description':
160 $this->description = $data;
161 break;
162 case 'state':
163 $this->state = $data;
164 break;
165 case 'reviewstate':
166 $this->reviewstate = $data;
167 break;
168 case 'category':
169 $this->category = $data;
170 break;
171 case 'lastuploaddate':
172 $this->lastuploaddate = $data;
173 break;
174 case 'uploadcomment':
175 $this->uploadcomment = $data;
176 break;
177 case 'dependencies':
178 $this->dependencies = $this->convertDependencies($data);
179 break;
180 case 'authorname':
181 $this->authorname = $data;
182 break;
183 case 'authoremail':
184 $this->authoremail = $data;
185 break;
186 case 'authorcompany':
187 $this->authorcompany = $data;
188 break;
189 case 'ownerusername':
190 $this->ownerusername = $data;
191 break;
192 case 't3xfilemd5':
193 $this->t3xfilemd5 = $data;
194 break;
195 }
196 }
197 }
198 }