[SECURITY] Fix several XSS issues
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Classes / User.php
1 <?php
2 namespace TYPO3\CMS\Rtehtmlarea;
3 /**
4 * This file is part of the TYPO3 CMS project.
5 *
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
9 *
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
12 *
13 * The TYPO3 project - inspiring people to share!
14 */
15
16 use TYPO3\CMS\Backend\Utility\BackendUtility;
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * User defined content for htmlArea RTE
21 *
22 * @author Kasper Skårhøj <kasper@typo3.com>
23 * @author Stanislas Rolland <typo3(arobas)sjbr.ca>
24 */
25 class User {
26
27 /**
28 * @todo Define visibility
29 */
30 public $content;
31
32 /**
33 * @todo Define visibility
34 */
35 public $modData;
36
37 /**
38 * @todo Define visibility
39 */
40 public $siteUrl;
41
42 /**
43 * document template object
44 *
45 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
46 * @todo Define visibility
47 */
48 public $doc;
49
50 /**
51 * @todo Define visibility
52 */
53 public $editorNo;
54
55 /**
56 * Initialize language files
57 */
58 public function __construct() {
59 $GLOBALS['LANG']->includeLLFile('EXT:rtehtmlarea/mod5/locallang.xlf');
60 $GLOBALS['LANG']->includeLLFile('EXT:rtehtmlarea/htmlarea/locallang_dialogs.xlf');
61 }
62
63 /**
64 * @return [type] ...
65 * @todo Define visibility
66 */
67 public function init() {
68 $this->editorNo = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('editorNo');
69 $this->siteUrl = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
70 $this->doc = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Template\\DocumentTemplate');
71 $this->doc->backPath = $GLOBALS['BACK_PATH'];
72 $this->doc->bodyTagAdditions = 'onload="Init();"';
73 $this->doc->form = '
74 <form action="" id="process" name="process" method="post">
75 <input type="hidden" name="processContent" value="" />
76 <input type="hidden" name="returnUrl" value="' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REQUEST_URI')) . '" />
77 ';
78 $JScode = '
79 var plugin = window.parent.RTEarea[' . GeneralUtility::quoteJSvalue($this->editorNo) . '].editor.getPlugin("UserElements");
80 var HTMLArea = window.parent.HTMLArea;
81 var editor = plugin.editor;
82
83 function Init() {
84 };
85 function insertHTML(content,noHide) {
86 plugin.restoreSelection();
87 editor.getSelection().insertHtml(content);
88 if(!noHide) plugin.close();
89 };
90 function wrapHTML(wrap1,wrap2,noHide) {
91 plugin.restoreSelection();
92 if(!editor.getSelection().isEmpty()) {
93 editor.getSelection().surroundHtml(wrap1,wrap2);
94 } else {
95 alert(' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->getLL('noTextSelection')) . ');
96 }
97 if(!noHide) plugin.close();
98 };
99 function processSelection(script) {
100 plugin.restoreSelection();
101 document.process.action = script;
102 document.process.processContent.value = editor.getSelection().getHtml();
103 document.process.submit();
104 };
105 function jumpToUrl(URL) {
106 var RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode(\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RTEtsConfigParams')) . '";
107 var editorNo = "&editorNo=' . rawurlencode($this->editorNo) . '";
108 theLocation = URL+RTEtsConfigParams+editorNo;
109 window.location.href = theLocation;
110 }
111 ';
112
113 // unset the default jumpToUrl() function
114 unset($this->doc->JScodeArray['jumpToUrl']);
115
116 $this->doc->JScode = $this->doc->wrapScriptTags($JScode);
117 $this->modData = $GLOBALS['BE_USER']->getModuleData('user.php', 'ses');
118 if (\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('OC_key')) {
119 $parts = explode('|', \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('OC_key'));
120 $this->modData['openKeys'][$parts[1]] = $parts[0] == 'O' ? 1 : 0;
121 $GLOBALS['BE_USER']->pushModuleData('user.php', $this->modData);
122 }
123 }
124
125 /**
126 * [Describe function...]
127 *
128 * @return [type] ...
129 * @todo Define visibility
130 */
131 public function main() {
132 $this->content = '';
133 $this->content .= $this->main_user($this->modData['openKeys']);
134 }
135
136 /**
137 * [Describe function...]
138 *
139 * @return [type] ...
140 * @todo Define visibility
141 */
142 public function printContent() {
143 echo $this->content;
144 }
145
146 /********************************
147 *
148 * Other functions
149 *
150 *********************************/
151 /**
152 * @param [type] $imgInfo: ...
153 * @param [type] $maxW: ...
154 * @param [type] $maxH: ...
155 * @return [type] ...
156 * @todo Define visibility
157 */
158 public function calcWH($imgInfo, $maxW = 380, $maxH = 500) {
159 $IW = $imgInfo[0];
160 $IH = $imgInfo[1];
161 if ($IW > $maxW) {
162 $IH = ceil($IH / $IW * $maxW);
163 $IW = $maxW;
164 }
165 if ($IH > $maxH) {
166 $IW = ceil($IW / $IH * $maxH);
167 $IH = $maxH;
168 }
169 $imgInfo[3] = 'width="' . $IW . '" height="' . $IH . '"';
170 return $imgInfo;
171 }
172
173 /**
174 * Rich Text Editor (RTE) user element selector
175 *
176 * @param [type] $openKeys: ...
177 * @return [type] ...
178 * @todo Define visibility
179 */
180 public function main_user($openKeys) {
181 // Starting content:
182 $content = $this->doc->startPage($GLOBALS['LANG']->getLL('Insert Custom Element', TRUE));
183 $RTEtsConfigParts = explode(':', \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RTEtsConfigParams'));
184 $RTEsetup = $GLOBALS['BE_USER']->getTSConfig('RTE', \TYPO3\CMS\Backend\Utility\BackendUtility::getPagesTSconfig($RTEtsConfigParts[5]));
185 $thisConfig = \TYPO3\CMS\Backend\Utility\BackendUtility::RTEsetup($RTEsetup['properties'], $RTEtsConfigParts[0], $RTEtsConfigParts[2], $RTEtsConfigParts[4]);
186 if (is_array($thisConfig['userElements.'])) {
187 $categories = array();
188 foreach ($thisConfig['userElements.'] as $k => $value) {
189 $ki = (int)$k;
190 $v = $thisConfig['userElements.'][$ki . '.'];
191 if (substr($k, -1) == '.' && is_array($v)) {
192 $subcats = array();
193 $openK = $ki;
194 if ($openKeys[$openK]) {
195 $mArray = '';
196 if ($v['load'] === 'images_from_folder') {
197 $mArray = array();
198 if ($v['path'] && @is_dir((PATH_site . $v['path']))) {
199 $files = \TYPO3\CMS\Core\Utility\GeneralUtility::getFilesInDir(PATH_site . $v['path'], 'gif,jpg,jpeg,png', 0, '');
200 if (is_array($files)) {
201 $c = 0;
202 foreach ($files as $filename) {
203 $iInfo = @getimagesize((PATH_site . $v['path'] . $filename));
204 $iInfo = $this->calcWH($iInfo, 50, 100);
205 $ks = (string) (100 + $c);
206 $mArray[$ks] = $filename;
207 $mArray[$ks . '.'] = array(
208 'content' => '<img src="' . $this->siteUrl . $v['path'] . $filename . '" />',
209 '_icon' => '<img src="' . $this->siteUrl . $v['path'] . $filename . '" ' . $iInfo[3] . ' />',
210 'description' => $GLOBALS['LANG']->getLL('filesize') . ': ' . str_replace('&nbsp;', ' ', \TYPO3\CMS\Core\Utility\GeneralUtility::formatSize(@filesize((PATH_site . $v['path'] . $filename)))) . ', ' . $GLOBALS['LANG']->getLL('pixels', 1) . ': ' . $iInfo[0] . 'x' . $iInfo[1]
211 );
212 $c++;
213 }
214 }
215 }
216 }
217 if (is_array($mArray)) {
218 if ($v['merge']) {
219 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($mArray, $v);
220 $v = $mArray;
221 } else {
222 $v = $mArray;
223 }
224 }
225 foreach ($v as $k2 => $dummyValue) {
226 $k2i = (int)$k2;
227 if (substr($k2, -1) == '.' && is_array($v[$k2i . '.'])) {
228 $title = trim($v[$k2i]);
229 if (!$title) {
230 $title = '[' . $GLOBALS['LANG']->getLL('noTitle', TRUE) . ']';
231 } else {
232 $title = $GLOBALS['LANG']->sL($title, TRUE);
233 }
234 $description = $GLOBALS['LANG']->sL($v[($k2i . '.')]['description'], TRUE) . '<br />';
235 if (!$v[($k2i . '.')]['dontInsertSiteUrl']) {
236 $v[$k2i . '.']['content'] = str_replace('###_URL###', $this->siteUrl, $v[$k2i . '.']['content']);
237 }
238 $logo = $v[$k2i . '.']['_icon'] ?: '';
239 $onClickEvent = '';
240 switch ((string) $v[($k2i . '.')]['mode']) {
241 case 'wrap':
242 $wrap = explode('|', $v[$k2i . '.']['content']);
243 $onClickEvent = 'wrapHTML(' . GeneralUtility::quoteJSvalue($wrap[0]) . ',' . GeneralUtility::quoteJSvalue($wrap[1]) . ',false);';
244 break;
245 case 'processor':
246 $script = trim($v[$k2i . '.']['submitToScript']);
247 if (substr($script, 0, 4) != 'http') {
248 $script = $this->siteUrl . $script;
249 }
250 if ($script) {
251 $onClickEvent = 'processSelection(' . GeneralUtility::quoteJSvalue($script) . ');';
252 }
253 break;
254 case 'insert':
255
256 default:
257 $onClickEvent = 'insertHTML(' . GeneralUtility::quoteJSvalue($v[($k2i . '.')]['content']) . ');';
258 }
259 $A = array('<a href="#" onClick="' . $onClickEvent . 'return false;">', '</a>');
260 $subcats[$k2i] = '<tr>
261 <td><img src="clear.gif" width="18" height="1" /></td>
262 <td class="bgColor4" valign="top">' . $A[0] . $logo . $A[1] . '</td>
263 <td class="bgColor4" valign="top">' . $A[0] . '<strong>' . $title . '</strong><br />' . $description . $A[1] . '</td>
264 </tr>';
265 }
266 }
267 ksort($subcats);
268 }
269 $categories[$ki] = implode('', $subcats);
270 }
271 }
272 ksort($categories);
273 // Render menu of the items:
274 $lines = array();
275 foreach ($categories as $k => $v) {
276 $title = trim($thisConfig['userElements.'][$k]);
277 $openK = $k;
278 if (!$title) {
279 $title = '[' . $GLOBALS['LANG']->getLL('noTitle', TRUE) . ']';
280 } else {
281 $title = $GLOBALS['LANG']->sL($title, TRUE);
282 }
283 $lines[] = '<tr><td colspan="3" class="bgColor5"><a href="#" title="' . $GLOBALS['LANG']->getLL('expand', TRUE) . '" onClick="jumpToUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('rtehtmlarea_wizard_user', array('OC_key' => ($openKeys[$openK] ? 'C|' : 'O|') . $openK))) . ');return false;"><img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($GLOBALS['BACK_PATH'], ('gfx/ol/' . ($openKeys[$openK] ? 'minus' : 'plus') . 'bullet.gif'), 'width="18" height="16"') . ' title="' . $GLOBALS['LANG']->getLL('expand', TRUE) . '" /><strong>' . $title . '</strong></a></td></tr>';
284 $lines[] = $v;
285 }
286 $content .= '<table border="0" cellpadding="1" cellspacing="1">' . implode('', $lines) . '</table>';
287 }
288 $content .= $this->doc->endPage();
289 return $content;
290 }
291
292 }