Fixed bug #1985: XSS vulnerability in wizard classes
[Packages/TYPO3.CMS.git] / typo3 / wizard_tsconfig.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2010 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Wizard for inserting TSconfig in form fields. (page,user or TS)
29 *
30 * $Id$
31 * Revised for TYPO3 3.6 November/2003 by Kasper Skaarhoj
32 * XHTML compliant
33 *
34 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 */
36 /**
37 * [CLASS/FUNCTION INDEX of SCRIPT]
38 *
39 *
40 *
41 * 98: class ext_TSparser extends t3lib_tsparser_ext
42 * 106: function makeHtmlspecialchars($P)
43 *
44 *
45 * 127: class SC_wizard_tsconfig
46 * 149: function init()
47 * 202: function setValue(field,value)
48 * 232: function mixerField(cmd,objString)
49 * 258: function str_replace(match,replace,string)
50 * 280: function jump(show,objString)
51 * 295: function main()
52 * 320: function printContent()
53 * 333: function browseTSprop($mode,$show)
54 *
55 * SECTION: Module functions
56 * 419: function getObjTree()
57 * 449: function setObj(&$objTree,$strArr,$params)
58 * 469: function revertFromSpecialChars($str)
59 * 482: function doLink($params)
60 * 495: function removePointerObjects($objArray)
61 * 514: function linkToObj($str,$uid,$objString='')
62 * 527: function printTable($table,$objString,$objTree)
63 * 608: function linkProperty($str,$propertyName,$prefix,$datatype)
64 *
65 * TOTAL FUNCTIONS: 17
66 * (This index is automatically created/updated by the extension "extdeveval")
67 *
68 */
69
70
71
72 $BACK_PATH='';
73 require ('init.php');
74 require ('template.php');
75 $LANG->includeLLFile('EXT:lang/locallang_wizards.xml');
76
77
78
79
80
81
82
83 /**
84 * TypoScript parser extension class.
85 *
86 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
87 * @package TYPO3
88 * @subpackage core
89 */
90 class ext_TSparser extends t3lib_tsparser_ext {
91
92 /**
93 * Pass through of incoming value for link.
94 *
95 * @param array P array
96 * @return string The "_LINK" key value, straight away.
97 */
98 function makeHtmlspecialchars($P) {
99 return $P['_LINK'];
100 }
101 }
102
103
104
105
106
107
108
109
110
111
112 /**
113 * Script Class for rendering the TSconfig/TypoScript property browser.
114 *
115 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
116 * @package TYPO3
117 * @subpackage core
118 */
119 class SC_wizard_tsconfig {
120
121 // Internal, dynamic:
122 /**
123 * document template object
124 *
125 * @var mediumDoc
126 */
127 var $doc;
128 var $content; // Content accumulation for the module.
129
130 // Internal, static: GPvars
131 var $P; // Wizard parameters, coming from TCEforms linking to the wizard.
132 var $mode; // "page", "tsref" or "beuser"
133 var $show; // Pointing to an entry in static_tsconfig_help to show.
134 var $objString; // Object path - for display.
135 var $onlyProperty; // If set, the "mixed-field" is not shown and you can select only one property at a time.
136
137
138
139
140
141 /**
142 * Initialization of the class
143 *
144 * @return void
145 */
146 function init() {
147 global $LANG,$BACK_PATH;
148
149 // Check if the tsconfig_help extension is loaded - which is mandatory for this wizard to work.
150 t3lib_extMgm::isLoaded('tsconfig_help',1);
151
152 // Init GPvars:
153 $this->P = t3lib_div::_GP('P');
154 $this->mode = t3lib_div::_GP('mode');
155 $this->show = t3lib_div::_GP('show');
156 $this->objString = t3lib_div::_GP('objString');
157 $this->onlyProperty = t3lib_div::_GP('onlyProperty');
158 // Preparing some JavaScript code:
159 if (!$this->areFieldChangeFunctionsValid()) {
160 $this->P['fieldChangeFunc']=array();
161 }
162 unset($this->P['fieldChangeFunc']['alert']);
163 $update='';
164 foreach($this->P['fieldChangeFunc'] as $k=>$v) {
165 $update.= '
166 window.opener.'.$v;
167 }
168
169 // Init the document table object:
170 $this->doc = t3lib_div::makeInstance('template');
171 $this->doc->backPath = $BACK_PATH;
172 $this->doc->form='<form action="" name="editform">';
173
174 // Adding Styles (should go into stylesheet?)
175 $this->doc->inDocStylesArray[] = '
176 A:link {text-decoration: bold; color: '.$this->doc->hoverColor.';}
177 A:visited {text-decoration: bold; color: '.$this->doc->hoverColor.';}
178 A:active {text-decoration: bold; color: '.$this->doc->hoverColor.';}
179 A:hover {color: '.$this->doc->bgColor2.'}
180 ';
181
182 $this->doc->JScode.=$this->doc->wrapScriptTags('
183 function checkReference_name() { // Checks if the input field containing the name exists in the document
184 if (window.opener && window.opener.document && window.opener.document.'.$this->P['formName'].' && window.opener.document.'.$this->P['formName'].'["'.$this->P['itemName'].'"] ) {
185 return window.opener.document.'.$this->P['formName'].'["'.$this->P['itemName'].'"];
186 }
187 }
188 function checkReference_value() { // Checks if the input field containing the value exists in the document
189 if (window.opener && window.opener.document && window.opener.document.'.$this->P['formName'].' && window.opener.document.'.$this->P['formName'].'["'.$this->P['itemValue'].'"] ) {
190 return window.opener.document.'.$this->P['formName'].'["'.$this->P['itemValue'].'"];
191 }
192 }
193
194 /**
195 * [Describe function...]
196 *
197 * @param [type] $field,value: ...
198 * @return [type] ...
199 */
200 function setValue(field,value) {
201 var nameField = checkReference_name();
202 var valueField = checkReference_value();
203 if (nameField) {
204 if (valueField) { // This applies to the TS Object Browser module
205 nameField.value=field;
206 valueField.value=value;
207 } else { // This applies to the Info/Modify module and the Page TSconfig field
208 if (value) {
209 nameField.value=field+"="+value+"\n"+nameField.value;
210 } else {
211 nameField.value=field+"\n"+nameField.value;
212 }
213 }
214 '.$update.'
215 window.opener.focus();
216 }
217 close();
218 }
219 function getValue() { // This is never used. Remove it?
220 var field = checkReference_name();
221 if (field) {
222 return field.value;
223 } else {
224 close();
225 }
226 }
227
228 /**
229 * [Describe function...]
230 *
231 * @param [type] $cmd,objString: ...
232 * @return [type] ...
233 */
234 function mixerField(cmd,objString) {
235 var temp;
236 switch(cmd) {
237 case "Indent":
238 temp = str_replace("\n","\n ","\n"+document.editform.mixer.value);
239 document.editform.mixer.value = temp.substr(1);
240 break;
241 case "Outdent":
242 temp = str_replace("\n ","\n","\n"+document.editform.mixer.value);
243 document.editform.mixer.value = temp.substr(1);
244 break;
245 case "Transfer":
246 setValue(document.editform.mixer.value);
247 break;
248 case "Wrap":
249 document.editform.mixer.value=objString+" {\n"+document.editform.mixer.value+"\n}";
250 break;
251 }
252 }
253
254 /**
255 * [Describe function...]
256 *
257 * @param [type] $match,replace,string: ...
258 * @return [type] ...
259 */
260 function str_replace(match,replace,string) {
261 var input = ""+string;
262 var matchStr = ""+match;
263 if (!matchStr) {return string;}
264 var output = "";
265 var pointer=0;
266 var pos = input.indexOf(matchStr);
267 while (pos!=-1) {
268 output+=""+input.substr(pointer, pos-pointer)+replace;
269 pointer=pos+matchStr.length;
270 pos = input.indexOf(match,pos+1);
271 }
272 output+=""+input.substr(pointer);
273 return output;
274 }
275
276 /**
277 * [Describe function...]
278 *
279 * @param [type] $show,objString: ...
280 * @return [type] ...
281 */
282 function jump(show,objString) {
283 window.location.href = "'.t3lib_div::linkThisScript(array('show'=>'','objString'=>'')).'&show="+show+"&objString="+objString;
284 }
285 ');
286
287
288 // Start the page:
289 $this->content.=$this->doc->startPage($LANG->getLL('tsprop'));
290 }
291
292 /**
293 * Main function, rendering the content of the TypoScript property browser, including links to online resources
294 *
295 * @return void
296 */
297 function main() {
298 global $LANG;
299
300 // Adding module content:
301 $this->content.=$this->doc->section($LANG->getLL('tsprop'),$this->browseTSprop($this->mode,$this->show),0,1);
302
303 // Adding link to TSref:
304 if ($this->mode=='tsref') {
305 $this->content.=$this->doc->section($LANG->getLL('tsprop_TSref'),'
306 <a href="'.htmlspecialchars('http://typo3.org/documentation/document-library/references/doc_core_tsref/current/view/').'" target="_blank">'.$LANG->getLL('tsprop_TSref',1).'</a>
307 ',0,1);
308 }
309 // Adding link to admin guides etc:
310 if ($this->mode=='page' || $this->mode=='beuser') {
311 $this->content.=$this->doc->section($LANG->getLL('tsprop_tsconfig'),'
312 <a href="'.htmlspecialchars('http://typo3.org/documentation/document-library/references/doc_core_tsconfig/current/view/').'" target="_blank">'.$LANG->getLL('tsprop_tsconfig',1).'</a>
313 ',0,1);
314 }
315 }
316
317 /**
318 * Outputting the accumulated content to screen
319 *
320 * @return void
321 */
322 function printContent() {
323 $this->content.= $this->doc->endPage();
324 $this->content = $this->doc->insertStylesAndJS($this->content);
325 echo $this->content;
326 }
327
328 /**
329 * Create the content of the module:
330 *
331 * @param string Object string
332 * @param integer Pointing to an entry in static_tsconfig_help to show.
333 * @return string HTML
334 */
335 function browseTSprop($mode,$show) {
336 global $LANG;
337
338 // Get object tree:
339 $objTree = $this->getObjTree();
340
341 // Show single element, if show is set.
342 $out='';
343 if ($show) {
344 // Get the entry data:
345 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'static_tsconfig_help', 'uid='.intval($show));
346 $rec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
347 $table = unserialize($rec['appdata']);
348 $obj_string = strtr($this->objString,'()','[]'); // Title:
349
350 // Title and description:
351 $out.='<a href="'.htmlspecialchars(t3lib_div::linkThisScript(array('show'=>''))).'" class="typo3-goBack">'.
352 t3lib_iconWorks::getSpriteIcon('actions-view-go-back') .
353 htmlspecialchars($obj_string).
354 '</a><br />';
355 if ($rec['title']) $out.= '<strong>'.htmlspecialchars($rec['title']).': </strong>';
356 if ($rec['description']) $out.= nl2br(htmlspecialchars(trim($rec['description']))).'<br />';
357
358 // Printing the content:
359 $out.= '<br />'.$this->printTable($table, $obj_string, $objTree[$mode.'.']);
360 $out.='<hr />';
361
362 // Printing the "mixer-field":
363 if (!$this->onlyProperty) {
364 $links=array();
365 $links[]='<a href="#" onclick="mixerField(\'Indent\');return false;">'.$LANG->getLL('tsprop_mixer_indent',1).'</a>';
366 $links[]='<a href="#" onclick="mixerField(\'Outdent\');return false;">'.$LANG->getLL('tsprop_mixer_outdent',1).'</a>';
367 $links[]='<a href="#" onclick="mixerField(\'Wrap\',unescape(\''.rawurlencode($obj_string).'\'));return false;">'.$LANG->getLL('tsprop_mixer_wrap',1).'</a>';
368 $links[]='<a href="#" onclick="mixerField(\'Transfer\');return false;">'.$LANG->getLL('tsprop_mixer_transfer',1).'</a>';
369 $out.='<textarea rows="5" name="mixer" wrap="off"'.$this->doc->formWidthText(48,'','off').' class="fixed-font enable-tab"></textarea>';
370 $out.='<br /><strong>'.implode('&nbsp; | &nbsp;',$links).'</strong>';
371 $out.='<hr />';
372 }
373 }
374
375
376 // SECTION: Showing property tree:
377 $tmpl = t3lib_div::makeInstance('ext_TSparser');
378 $tmpl->tt_track = 0; // Do not log time-performance information
379 $tmpl->fixedLgd=0;
380 $tmpl->linkObjects=0;
381 $tmpl->bType='';
382 $tmpl->ext_expandAllNotes=1;
383 $tmpl->ext_noPMicons=1;
384 $tmpl->ext_noSpecialCharsOnLabels=1;
385
386 if (is_array($objTree[$mode.'.'])) {
387 $out.='
388
389
390 <!--
391 TSconfig, object tree:
392 -->
393 <table border="0" cellpadding="0" cellspacing="0" class="t3-tree t3-tree-config" id="typo3-objtree">
394 <tr class="t3-row-header"><td>TSref</td></tr>
395 <tr>
396 <td nowrap="nowrap">'.$tmpl->ext_getObjTree($this->removePointerObjects($objTree[$mode.'.']),'','','','','1').'</td>
397 </tr>
398 </table>';
399 }
400
401 return $out;
402 }
403
404
405
406
407
408
409
410 /***************************
411 *
412 * Module functions
413 *
414 ***************************/
415
416 /**
417 * Create object tree from static_tsconfig_help table
418 *
419 * @return array Object tree.
420 * @access private
421 */
422 function getObjTree() {
423 $objTree=array();
424
425 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,obj_string,title', 'static_tsconfig_help', '');
426 while($rec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
427 $rec['obj_string'] = $this->revertFromSpecialChars($rec['obj_string']);
428 $p = explode(';',$rec['obj_string']);
429 foreach ($p as $v) {
430 $p2 = t3lib_div::trimExplode(':',$v,1);
431 $subp=t3lib_div::trimExplode('/',$p2[1],1);
432 foreach ($subp as $v2) {
433 $this->setObj($objTree,explode('.',$p2[0].'.'.$v2),array($rec,$v2));
434 }
435 }
436 }
437 return $objTree;
438 }
439
440 /**
441 * Sets the information from a static_tsconfig_help record in the object array.
442 * Makes recursive calls.
443 *
444 * @param array Object tree array, passed by value!
445 * @param array Array of elements from object path (?)
446 * @param array Array with record and something else (?)
447 * @return void
448 * @access private
449 * @see getObjTree()
450 */
451 function setObj(&$objTree,$strArr,$params) {
452 $key = current($strArr);
453 reset($strArr);
454 if (count($strArr)>1) {
455 array_shift($strArr);
456 if (!isset($objTree[$key.'.'])) $objTree[$key.'.']=array();
457 $this->setObj($objTree[$key.'.'],$strArr,$params);
458 } else {
459 $objTree[$key]=$params;
460 $objTree[$key]['_LINK']=$this->doLink($params);
461 }
462 }
463
464 /**
465 * Converts &gt; and &lt; to > and <
466 *
467 * @param string Input string
468 * @return string Output string
469 * @access private
470 */
471 function revertFromSpecialChars($str) {
472 $str = str_replace('&gt;','>',$str);
473 $str = str_replace('&lt;','<',$str);
474 return $str;
475 }
476
477 /**
478 * Creates a link based on input params array:
479 *
480 * @param array Parameters
481 * @return string The link.
482 * @access private
483 */
484 function doLink($params) {
485 $title = trim($params[0]['title'])?trim($params[0]['title']):'[GO]';
486 $str = $this->linkToObj($title,$params[0]['uid'],$params[1]);
487 return $str;
488 }
489
490 /**
491 * Remove pointer strings from an array
492 *
493 * @param array Input array
494 * @return array Modified input array
495 * @access private
496 */
497 function removePointerObjects($objArray) {
498 foreach ($objArray as $k => $value) {
499 if (substr(trim($k),0,2)=="->" && trim($k)!='->.') {
500 $objArray['->.'][substr(trim($k),2)]=$objArray[$k];
501 unset($objArray[$k]);
502 }
503 }
504 return $objArray;
505 }
506
507 /**
508 * Linking string to object by UID
509 *
510 * @param string String to link
511 * @param integer UID of a static_tsconfig_help record.
512 * @param string Title string for that record!
513 * @return string Linked string
514 */
515 function linkToObj($str,$uid,$objString='') {
516 $aOnClick='jump(\''.rawurlencode($uid).'\',\''.rawurlencode($objString).'\');return false;';
517 return '<a href="#" onclick="'.htmlspecialchars($aOnClick).'">'.htmlspecialchars($str).'</a>';
518 }
519
520 /**
521 * Creates a table of properties:
522 *
523 * @param array Array with properties for the current object path
524 * @param string Object path
525 * @param array Object tree
526 * @return string HTML content.
527 */
528 function printTable($table,$objString,$objTree) {
529 if (is_array($table['rows'])) {
530
531 // Initialize:
532 $lines=array();
533
534 // Adding header:
535 $lines[]='
536 <tr class="t3-row-header">
537 <td>Property:</td>
538 <td>Data type:</td>
539 <td>Description:</td>
540 <td>Default:</td>
541 </tr>';
542
543 // Traverse the content of "rows":
544 foreach($table['rows'] as $i => $row) {
545
546 // Linking:
547 $lP=t3lib_div::trimExplode(LF,$row['property'],1);
548 $lP2=array();
549 foreach ($lP as $k => $lStr) {
550 $lP2[$k] = $this->linkProperty($lStr,$lStr,$objString,$row['datatype']);
551 }
552 $linkedProperties=implode('<hr />',$lP2);
553
554 // Data type:
555 $dataType = $row['datatype'];
556
557 // Generally "->[something]"
558 $reg=array();
559 preg_match('/->[[:alnum:]_]*/',$dataType,$reg);
560 if ($reg[0] && is_array($objTree[$reg[0]])) {
561 $dataType = str_replace($reg[0],'<a href="'.htmlspecialchars(t3lib_div::linkThisScript(array('show'=>$objTree[$reg[0]][0]['uid'],'objString'=>$objString.'.'.$lP[0]))).'">'.htmlspecialchars($reg[0]).'</a>',$dataType);
562 }
563
564 // stdWrap
565 if (!strstr($dataType,'->stdWrap') && strstr(strip_tags($dataType),'stdWrap')) {
566 // Potential problem can be that "stdWrap" is substituted inside another A-tag. So maybe we should even check if there is already a <A>-tag present and if so, not make a substitution?
567 $dataType = str_replace('stdWrap','<a href="'.htmlspecialchars(t3lib_div::linkThisScript(array('show'=>$objTree['->stdWrap'][0]['uid'],'objString'=>$objString.'.'.$lP[0]))).'">stdWrap</a>',$dataType);
568 }
569
570
571 $lines[]='
572 <tr class="t3-row ' . ($i % 2 ? 't3-row-even' : 't3-row-odd') . '">
573 <td valign="top" class="bgColor4-20" nowrap="nowrap"><strong>'.$linkedProperties.'</strong></td>
574 <td valign="top">'.nl2br($dataType.'&nbsp;').'</td>
575 <td valign="top">'.nl2br($row['description']).'</td>
576 <td valign="top">'.nl2br($row['default']).'</td>
577 </tr>';
578 }
579 // Return it all:
580 return '
581
582
583
584 <!--
585 TSconfig, attribute selector:
586 -->
587 <table border="0" cellpadding="0" cellspacing="1" width="98%" class="t3-table" id="typo3-attributes">
588 '.implode('',$lines).'
589 </table>';
590 }
591 }
592
593 /**
594 * Creates a link on a property.
595 *
596 * @param string String to link
597 * @param string Property value.
598 * @param string Object path prefix to value
599 * @param string Data type
600 * @return string Linked $str
601 */
602 function linkProperty($str,$propertyName,$prefix,$datatype) {
603 $out='';
604
605 // Setting preset value:
606 if (strstr($datatype,'boolean')) {
607 $propertyVal='1'; // preset "1" to boolean values.
608 }
609
610 // Adding mixer features; The plus icon:
611 if(!$this->onlyProperty) {
612 $aOnClick = 'document.editform.mixer.value=unescape(\' '.rawurlencode($propertyName.'='.$propertyVal).'\')+\'\n\'+document.editform.mixer.value; return false;';
613 $out.= '<a href="#" onclick="'.htmlspecialchars($aOnClick).'">'.
614 t3lib_iconWorks::getSpriteIcon('actions-edit-add', array('title' => $GLOBALS['LANG']->getLL('tsprop_addToList', TRUE))) .
615 '</a>';
616 $propertyName = $prefix.'.'.$propertyName;
617 }
618
619 // Wrap string:
620 $aOnClick = 'setValue(unescape(\''.rawurlencode($propertyName).'\'),unescape(\''.rawurlencode($propertyVal).'\')); return false;';
621 $out.= '<a href="#" onclick="'.htmlspecialchars($aOnClick).'">'.$str.'</a>';
622
623 // Return link:
624 return $out;
625 }
626
627 /**
628 * Determines whether submitted field change functions are valid
629 * and are coming from the system and not from an external abuse.
630 *
631 * @return boolean Whether the submitted field change functions are valid
632 */
633 protected function areFieldChangeFunctionsValid() {
634 return (
635 isset($this->P['fieldChangeFunc']) && is_array($this->P['fieldChangeFunc']) && isset($this->P['fieldChangeFuncHash'])
636 && $this->P['fieldChangeFuncHash'] == t3lib_div::hmac(serialize($this->P['fieldChangeFunc']))
637 );
638 }
639 }
640
641
642 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/wizard_tsconfig.php']) {
643 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/wizard_tsconfig.php']);
644 }
645
646
647
648 // Make instance:
649 $SOBE = t3lib_div::makeInstance('SC_wizard_tsconfig');
650 $SOBE->init();
651 $SOBE->main();
652 $SOBE->printContent();
653
654 ?>