OSDN Git Service

Security fix
[nucleus-jp/nucleus-jp-ancient.git] / utf8 / nucleus / libs / skinie.php
1 <?php
2 /*
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4  * Copyright (C) 2002-2007 The Nucleus Group
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * (see nucleus/documentation/index.html#license for more info)
11  */
12 /**
13  *      This class contains two classes that can be used for importing and
14  *      exporting Nucleus skins: SKINIMPORT and SKINEXPORT
15  *
16  * @license http://nucleuscms.org/license.txt GNU General Public License
17  * @copyright Copyright (C) 2002-2007 The Nucleus Group
18  * @version $Id: skinie.php,v 1.8 2007-03-22 03:30:14 kmorimatsu Exp $
19  * @version $NucleusJP: skinie.php,v 1.7 2007/02/04 06:28:46 kimitake Exp $
20  */
21
22 class SKINIMPORT {
23
24         // hardcoded value (see constructor). When 1, interesting info about the
25         // parsing process is sent to the output
26         var $debug;
27
28         // parser/file pointer
29         var $parser;
30         var $fp;
31
32         // which data has been read?
33         var $metaDataRead;
34         var $allRead;
35
36         // extracted data
37         var $skins;
38         var $templates;
39         var $info;
40
41         // to maintain track of where we are inside the XML file
42         var $inXml;
43         var $inData;
44         var $inMeta;
45         var $inSkin;
46         var $inTemplate;
47         var $currentName;
48         var $currentPartName;
49         var $cdata;
50
51
52
53         /**
54          * constructor initializes data structures
55          */
56         function SKINIMPORT() {
57                 // disable magic_quotes_runtime if it's turned on
58                 set_magic_quotes_runtime(0);
59
60                 // debugging mode?
61                 $this->debug = 0;
62
63                 $this->reset();
64
65         }
66
67         function reset() {
68                 if ($this->parser)
69                         xml_parser_free($this->parser);
70
71                 // XML file pointer
72                 $this->fp = 0;
73
74                 // which data has been read?
75                 $this->metaDataRead = 0;
76                 $this->allRead = 0;
77
78                 // to maintain track of where we are inside the XML file
79                 $this->inXml = 0;
80                 $this->inData = 0;
81                 $this->inMeta = 0;
82                 $this->inSkin = 0;
83                 $this->inTemplate = 0;
84                 $this->currentName = '';
85                 $this->currentPartName = '';
86
87                 // character data pile
88                 $this->cdata = '';
89
90                 // list of skinnames and templatenames (will be array of array)
91                 $this->skins = array();
92                 $this->templates = array();
93
94                 // extra info included in the XML files (e.g. installation notes)
95                 $this->info = '';
96
97                 // init XML parser
98                 $this->parser = xml_parser_create();
99                 xml_set_object($this->parser, $this);
100                 xml_set_element_handler($this->parser, 'startElement', 'endElement');
101                 xml_set_character_data_handler($this->parser, 'characterData');
102                 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
103
104         }
105
106         /**
107          * Reads an XML file into memory
108          *
109          * @param $filename
110          *              Which file to read
111          * @param $metaOnly
112          *              Set to 1 when only the metadata needs to be read (optional, default 0)
113          */
114         function readFile($filename, $metaOnly = 0) {
115                 // open file
116                 $this->fp = @fopen($filename, 'r');
117                 if (!$this->fp) return 'Failed to open file/URL';
118
119                 // here we go!
120                 $this->inXml = 1;
121
122                 $tempbuffer = null;
123
124                 while (!feof($this->fp)) {
125                         $tempbuffer .= fread($this->fp, 4096);
126                 }
127                 fclose($this->fp);
128
129 /*
130         [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates
131                                                    with CDATA sections. need to investigate consequences.
132                                                    see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
133
134                 // backwards compatibility with the non-wellformed skinbackup.xml files
135                 // generated by v2/v3 (when CDATA sections were present in skins)
136                 // split up those CDATA sections into multiple ones
137                 $tempbuffer = preg_replace_callback(
138                         "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
139                         create_function(
140                                 '$matches',
141                                 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
142                         ),
143                         $tempbuffer
144                 );
145 */
146                 $temp = tmpfile();
147                 fwrite($temp, $tempbuffer);
148                 rewind($temp);
149
150                 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
151                         $err = xml_parse( $this->parser, $buffer, feof($temp) );
152                         if (!$err && $this->debug)
153                                 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />';
154                 }
155
156                 // all done
157                 $this->inXml = 0;
158                 fclose($temp);
159         }
160
161         /**
162          * Returns the list of skin names
163          */
164         function getSkinNames() {
165                 return array_keys($this->skins);
166         }
167
168         /**
169          * Returns the list of template names
170          */
171         function getTemplateNames() {
172                 return array_keys($this->templates);
173         }
174
175         /**
176          * Returns the extra information included in the XML file
177          */
178         function getInfo() {
179                 return $this->info;
180         }
181
182         /**
183          * Writes the skins and templates to the database
184          *
185          * @param $allowOverwrite
186          *              set to 1 when allowed to overwrite existing skins with the same name
187          *              (default = 0)
188          */
189         function writeToDatabase($allowOverwrite = 0) {
190                 $existingSkins = $this->checkSkinNameClashes();
191                 $existingTemplates = $this->checkTemplateNameClashes();
192
193                 // if not allowed to overwrite, check if any nameclashes exists
194                 if (!$allowOverwrite) {
195                         if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0))
196                                 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite';
197                 }
198
199                 foreach ($this->skins as $skinName => $data) {
200                         // 1. if exists: delete all part data, update desc data
201                         //    if not exists: create desc
202                         if (in_array($skinName, $existingSkins)) {
203                                 $skinObj = SKIN::createFromName($skinName);
204
205                                 // delete all parts of the skin
206                                 $skinObj->deleteAllParts();
207
208                                 // update general info
209                                 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
210                         } else {
211                                 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
212                                 $skinObj = new SKIN($skinid);
213                         }
214
215                         // 2. add parts
216                         foreach ($data['parts'] as $partName => $partContent) {
217                                 $skinObj->update($partName, $partContent);
218                         }
219                 }
220
221                 foreach ($this->templates as $templateName => $data) {
222                         // 1. if exists: delete all part data, update desc data
223                         //    if not exists: create desc
224                         if (in_array($templateName, $existingTemplates)) {
225                                 $templateObj = TEMPLATE::createFromName($templateName);
226
227                                 // delete all parts of the template
228                                 $templateObj->deleteAllParts();
229
230                                 // update general info
231                                 $templateObj->updateGeneralInfo($templateName, $data['description']);
232                         } else {
233                                 $templateid = TEMPLATE::createNew($templateName, $data['description']);
234                                 $templateObj = new TEMPLATE($templateid);
235                         }
236
237                         // 2. add parts
238                         foreach ($data['parts'] as $partName => $partContent) {
239                                 $templateObj->update($partName, $partContent);
240                         }
241                 }
242
243
244         }
245
246         /**
247           * returns an array of all the skin nameclashes (empty array when no name clashes)
248           */
249         function checkSkinNameClashes() {
250                 $clashes = array();
251
252                 foreach ($this->skins as $skinName => $data) {
253                         if (SKIN::exists($skinName))
254                                 array_push($clashes, $skinName);
255                 }
256
257                 return $clashes;
258         }
259
260         /**
261           * returns an array of all the template nameclashes
262           * (empty array when no name clashes)
263           */
264         function checkTemplateNameClashes() {
265                 $clashes = array();
266
267                 foreach ($this->templates as $templateName => $data) {
268                         if (TEMPLATE::exists($templateName))
269                                 array_push($clashes, $templateName);
270                 }
271
272                 return $clashes;
273         }
274
275         /**
276          * Called by XML parser for each new start element encountered
277          */
278         function startElement($parser, $name, $attrs) {
279                 if ($this->debug) echo 'START: ', htmlspecialchars($name), '<br />';
280
281                 switch ($name) {
282                         case 'nucleusskin':
283                                 $this->inData = 1;
284                                 break;
285                         case 'meta':
286                                 $this->inMeta = 1;
287                                 break;
288                         case 'info':
289                                 // no action needed
290                                 break;
291                         case 'skin':
292                                 if (!$this->inMeta) {
293                                         $this->inSkin = 1;
294                                         $this->currentName = $attrs['name'];
295                                         $this->skins[$this->currentName]['type'] = $attrs['type'];
296                                         $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
297                                         $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
298                                         $this->skins[$this->currentName]['parts'] = array();
299                                 } else {
300                                         $this->skins[$attrs['name']] = array();
301                                         $this->skins[$attrs['name']]['parts'] = array();
302                                 }
303                                 break;
304                         case 'template':
305                                 if (!$this->inMeta) {
306                                         $this->inTemplate = 1;
307                                         $this->currentName = $attrs['name'];
308                                         $this->templates[$this->currentName]['parts'] = array();
309                                 } else {
310                                         $this->templates[$attrs['name']] = array();
311                                         $this->templates[$attrs['name']]['parts'] = array();
312                                 }
313                                 break;
314                         case 'description':
315                                 // no action needed
316                                 break;
317                         case 'part':
318                                 $this->currentPartName = $attrs['name'];
319                                 break;
320                         default:
321                                 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name) , '<br />';
322                                 break;
323                 }
324
325                 // character data never contains other tags
326                 $this->clearCharacterData();
327
328         }
329
330         /**
331           * Called by the XML parser for each closing tag encountered
332           */
333         function endElement($parser, $name) {
334                 if ($this->debug) echo 'END: ', htmlspecialchars($name), '<br />';
335
336                 switch ($name) {
337                         case 'nucleusskin':
338                                 $this->inData = 0;
339                                 $this->allRead = 1;
340                                 break;
341                         case 'meta':
342                                 $this->inMeta = 0;
343                                 $this->metaDataRead = 1;
344                                 break;
345                         case 'info':
346                                 $this->info = $this->getCharacterData();
347                         case 'skin':
348                                 if (!$this->inMeta) $this->inSkin = 0;
349                                 break;
350                         case 'template':
351                                 if (!$this->inMeta) $this->inTemplate = 0;
352                                 break;
353                         case 'description':
354                                 if ($this->inSkin) {
355                                         $this->skins[$this->currentName]['description'] = $this->getCharacterData();
356                                 } else {
357                                         $this->templates[$this->currentName]['description'] = $this->getCharacterData();
358                                 }
359                                 break;
360                         case 'part':
361                                 if ($this->inSkin) {
362                                         $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
363                                 } else {
364                                         $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
365                                 }
366                                 break;
367                         default:
368                                 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name), '<br />';
369                                 break;
370                 }
371                 $this->clearCharacterData();
372
373         }
374
375         /**
376          * Called by XML parser for data inside elements
377          */
378         function characterData ($parser, $data) {
379                 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />';
380                 $this->cdata .= $data;
381         }
382
383         /**
384          * Returns the data collected so far
385          */
386         function getCharacterData() {
387                 return $this->cdata;
388         }
389
390         /**
391          * Clears the data buffer
392          */
393         function clearCharacterData() {
394                 $this->cdata = '';
395         }
396
397         /**
398          * Static method that looks for importable XML files in subdirs of the given dir
399          */
400         function searchForCandidates($dir) {
401                 $candidates = array();
402
403                 $dirhandle = opendir($dir);
404                 while ($filename = readdir($dirhandle)) {
405                         if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
406                                 $xml_file = $dir . $filename . '/skinbackup.xml';
407                                 if (file_exists($xml_file) && is_readable($xml_file)) {
408                                         $candidates[$filename] = $filename; //$xml_file;
409                                 }
410
411                                 // backwards compatibility
412                                 $xml_file = $dir . $filename . '/skindata.xml';
413                                 if (file_exists($xml_file) && is_readable($xml_file)) {
414                                         $candidates[$filename] = $filename; //$xml_file;
415                                 }
416                         }
417                 }
418                 closedir($dirhandle);
419
420                 return $candidates;
421
422         }
423
424
425 }
426
427
428 class SKINEXPORT {
429
430         var $templates;
431         var $skins;
432         var $info;
433
434         /**
435          * Constructor initializes data structures
436          */
437         function SKINEXPORT() {
438                 // list of templateIDs to export
439                 $this->templates = array();
440
441                 // list of skinIDs to export
442                 $this->skins = array();
443
444                 // extra info to be in XML file
445                 $this->info = '';
446         }
447
448         /**
449          * Adds a template to be exported
450          *
451          * @param id
452          *              template ID
453          * @result false when no such ID exists
454          */
455         function addTemplate($id) {
456                 if (!TEMPLATE::existsID($id)) return 0;
457
458                 $this->templates[$id] = TEMPLATE::getNameFromId($id);
459
460                 return 1;
461         }
462
463         /**
464          * Adds a skin to be exported
465          *
466          * @param id
467          *              skin ID
468          * @result false when no such ID exists
469          */
470         function addSkin($id) {
471                 if (!SKIN::existsID($id)) return 0;
472
473                 $this->skins[$id] = SKIN::getNameFromId($id);
474
475                 return 1;
476         }
477
478         /**
479          * Sets the extra info to be included in the exported file
480          */
481         function setInfo($info) {
482                 $this->info = $info;
483         }
484
485
486         /**
487          * Outputs the XML contents of the export file
488          *
489          * @param $setHeaders
490          *              set to 0 if you don't want to send out headers
491          *              (optional, default 1)
492          */
493         function export($setHeaders = 1) {
494                 if ($setHeaders) {
495                         // make sure the mimetype is correct, and that the data does not show up
496                         // in the browser, but gets saved into and XML file (popup download window)
497                         header('Content-Type: text/xml');
498                         header('Content-Disposition: attachment; filename="skinbackup.xml"');
499                         header('Expires: 0');
500                         header('Pragma: no-cache');
501                 }
502
503                 echo "<nucleusskin>\n";
504
505                 // meta
506                 echo "\t<meta>\n";
507                         // skins
508                         foreach ($this->skins as $skinId => $skinName) {
509                                 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n";
510                         }
511                         // templates
512                         foreach ($this->templates as $templateId => $templateName) {
513                                 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n";
514                         }
515                         // extra info
516                         if ($this->info)
517                                 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n";
518                 echo "\t</meta>\n\n\n";
519
520                 // contents skins
521                 foreach ($this->skins as $skinId => $skinName) {
522                         $skinId = intval($skinId);
523                         $skinObj = new SKIN($skinId);
524
525                         echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n";
526
527                         echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n";
528
529                         $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId);
530                         while ($partObj = mysql_fetch_object($res)) {
531                                 echo "\t\t",'<part name="',htmlspecialchars($partObj->stype),'">';
532                                 echo '<![CDATA[', $this->escapeCDATA($partObj->scontent),']]>';
533                                 echo "</part>\n\n";
534                         }
535
536                         echo "\t</skin>\n\n\n";
537                 }
538
539                 // contents templates
540                 foreach ($this->templates as $templateId => $templateName) {
541                         $templateId = intval($templateId);
542
543                         echo "\t",'<template name="',htmlspecialchars($templateName),'">',"\n";
544
545                         echo "\t\t",'<description>',htmlspecialchars(TEMPLATE::getDesc($templateId)),'</description>',"\n";
546
547                         $res = sql_query('SELECT tpartname, tcontent FROM '.sql_table('template').' WHERE tdesc='.$templateId);
548                         while ($partObj = mysql_fetch_object($res)) {
549                                 echo "\t\t",'<part name="',htmlspecialchars($partObj->tpartname),'">';
550                                 echo '<![CDATA[', $this->escapeCDATA($partObj->tcontent) ,']]>';
551                                 echo '</part>',"\n\n";
552                         }
553
554                         echo "\t</template>\n\n\n";
555                 }
556
557                 echo '</nucleusskin>';
558         }
559
560         /**
561          * Escapes CDATA content so it can be included in another CDATA section
562          */
563         function escapeCDATA($cdata)
564         {
565                 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);
566
567         }
568 }
569
570 ?>