dokuwiki-source-plugin – Diff between revs 2 and 3

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 2 Rev 3
1 <?php 1 <?php
2 /** 2 /**
3 * Source Plugin: includes a source file using the geshi highlighter 3 * Source Plugin: includes a source file using the geshi highlighter
4 * 4 *
5 * Syntax: <source filename lang|title> 5 * Syntax: <source filename lang|title>
6 * filename (required) can be a local path/file name or a remote file uri 6 * filename (required) can be a local path/file name or a remote file uri
7 * to use remote file uri, allow_url_fopen=On must be set in the server's php.ini 7 * to use remote file uri, allow_url_fopen=On must be set in the server's php.ini
8 * filenames with spaces must be surrounded by matched pairs of quotes (" or ') 8 * filenames with spaces must be surrounded by matched pairs of quotes (" or ')
9 * lang (optional) programming language name, is passed to geshi for code highlighting 9 * lang (optional) programming language name, is passed to geshi for code highlighting
10 * if not provided, the plugin will attempt to derive a value from the file name 10 * if not provided, the plugin will attempt to derive a value from the file name
11 * (refer $extensions in render() method) 11 * (refer $extensions in render() method)
12 * title (optional) all text after '|' will be rendered above the main code text with a 12 * title (optional) all text after '|' will be rendered above the main code text with a
13 * different style. If no title is present, it will be set to "file: filename" 13 * different style. If no title is present, it will be set to "file: filename"
14 * 14 *
15 * *** WARNING *** 15 * *** WARNING ***
16 * 16 *
17 * Unless configured correctly this plugin can be a huge security risk. 17 * Unless configured correctly this plugin can be a huge security risk.
18 * Please review/consider 18 * Please review/consider
19 * - users who have access to the wiki 19 * - users who have access to the wiki
20 * - php.ini setting, allow_url_fopen 20 * - php.ini setting, allow_url_fopen
21 * - php.ini setting, open_basedir 21 * - php.ini setting, open_basedir
22 * - this plugin's location, allow & deny settings. 22 * - this plugin's location, allow & deny settings.
23 * 23 *
24 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 24 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
25 * @author Wizardry and Steamworks office@grimore.org 25 * @author Wizardry and Steamworks office@grimore.org
26 * original: Christopher Smith <chris@jalakai.co.uk> 26 * original: Christopher Smith <chris@jalakai.co.uk>
27 */ 27 */
28 if(!defined('DOKU_INC')) die(); // no Dokuwiki, no go 28 if(!defined('DOKU_INC')) die(); // no Dokuwiki, no go
29   29  
30 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 30 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
31 require_once(DOKU_PLUGIN.'syntax.php'); 31 require_once(DOKU_PLUGIN.'syntax.php');
32   32  
33 /** 33 /**
34 * All DokuWiki plugins to extend the parser/rendering mechanism 34 * All DokuWiki plugins to extend the parser/rendering mechanism
35 * need to inherit from this class 35 * need to inherit from this class
36 */ 36 */
37 class syntax_plugin_source extends DokuWiki_Syntax_Plugin { 37 class syntax_plugin_source extends DokuWiki_Syntax_Plugin {
38   38  
39 //------------------- [ Security settings ] --------------------------------------------- 39 //------------------- [ Security settings ] ---------------------------------------------
40 var $location = ''; // prepended to all file names, restricting the filespace exposed to the plugin 40 var $location = ''; // prepended to all file names, restricting the filespace exposed to the plugin
41 var $allow = array(); // if not empty, ONLY files with the extensions listed will be allowed 41 var $allow = array(); // if not empty, ONLY files with the extensions listed will be allowed
42 var $deny = array(); // if $allow array is empty, any file with an extension listed in $deny array will be denied 42 var $deny = array(); // if $allow array is empty, any file with an extension listed in $deny array will be denied
43 var $rules = array(); // more complex allow/deny rules, refer documentation 43 var $rules = array(); // more complex allow/deny rules, refer documentation
44   44  
45 //------------------------[ Other settings ] --------------------------------------------- 45 //------------------------[ Other settings ] ---------------------------------------------
46 var $extensions = array( 46 var $extensions = array(
47 'htm' => 'html4strict', 47 'htm' => 'html4strict',
48 'html' => 'html4strict', 48 'html' => 'html4strict',
49 'js' => 'javascript' 49 'js' => 'javascript'
50 ); 50 );
51   51  
52 /** 52 /**
53 * return some info 53 * return some info
54 */ 54 */
55 function getInfo(){ 55 function getInfo(){
56 return array( 56 return array(
57 'author' => 'Christopher Smith', 57 'author' => 'Christopher Smith',
58 'email' => 'chris@jalakai.co.uk', 58 'email' => 'chris@jalakai.co.uk',
59 'date' => '2008-08-13', 59 'date' => '2008-08-13',
60 'name' => 'Source Plugin', 60 'name' => 'Source Plugin',
61 'desc' => 'Include a remote source file 61 'desc' => 'Include a remote source file
62 Syntax: <source filename #startline-endline language|title>', 62 Syntax: <source filename #startline-endline language|title>',
63 'url' => 'http://www.dokuwiki.org/plugin:source', 63 'url' => 'http://www.dokuwiki.org/plugin:source',
64 ); 64 );
65 } 65 }
66   66  
67 function getType() { return 'substition'; } 67 function getType() { return 'substition'; }
68 function getPType() { return 'block'; } 68 function getPType() { return 'block'; }
69 function getSort() { return 330; } 69 function getSort() { return 330; }
70   70  
71 /** 71 /**
72 * Connect pattern to lexer 72 * Connect pattern to lexer
73 */ 73 */
74 function connectTo($mode) { 74 function connectTo($mode) {
75 $this->Lexer->addSpecialPattern('<source.*?>',$mode,substr(get_class($this), 7)); 75 $this->Lexer->addSpecialPattern('<source.*?>',$mode,substr(get_class($this), 7));
76 } 76 }
77   77  
78 /** 78 /**
79 * Handle the match 79 * Handle the match
80 */ 80 */
81 function handle($match, $state, $pos, &$handler){ 81 function handle($match, $state, $pos, Doku_Handler $handler){
82 $match = trim(substr($match,7,-1)); //strip <source from start and > from end 82 $match = trim(substr($match,7,-1)); //strip <source from start and > from end
83   83  
84 // ['|"]?<filename>[\1] [#<line#>-<line#>] <lang> | <title> 84 // ['|"]?<filename>[\1] [#<line#>-<line#>] <lang> | <title>
85 list($attr, $title) = preg_split('/\|/u', $match, 2); //split out title 85 list($attr, $title) = preg_split('/\|/u', $match, 2); //split out title
86   86  
87 $attr = trim($attr); 87 $attr = trim($attr);
88 $pattern = ($attr{0} == '"' || $attr{0} == "'") ? $attr{0} : '\s'; 88 $pattern = ($attr{0} == '"' || $attr{0} == "'") ? $attr{0} : '\s';
89 list($file, $prop) = preg_split("/$pattern/u", $attr, 3, PREG_SPLIT_NO_EMPTY); 89 list($file, $prop) = preg_split("/$pattern/u", $attr, 3, PREG_SPLIT_NO_EMPTY);
90   90  
91 if (isset($prop) && trim($prop)) { 91 if (isset($prop) && trim($prop)) {
92 $matches = array(); 92 $matches = array();
93 if (preg_match('/\s*(?:(?:#(\d+)-(\d+))\s*)?(\w+)?/',$prop,$matches)) { 93 if (preg_match('/\s*(?:(?:#(\d+)-(\d+))\s*)?(\w+)?/',$prop,$matches)) {
94 list(,$start,$end,$lang) = $matches; 94 list(,$start,$end,$lang) = $matches;
95 if (!isset($lang)) $lang = ''; 95 if (!isset($lang)) $lang = '';
96 } 96 }
97 } else { 97 } else {
98 $start = $end = $lang = ''; 98 $start = $end = $lang = '';
99 } 99 }
100   100  
101 return array(trim($file), $lang, (isset($title)?trim($title):''), $start, $end); 101 return array(trim($file), $lang, (isset($title)?trim($title):''), $start, $end);
102 } 102 }
103   103  
104 /** 104 /**
105 * Create output 105 * Create output
106 */ 106 */
107 function render($format, &$renderer, $data) { 107 function render($format, Doku_Renderer $renderer, $data) {
108   108  
109 $this->_loadSettings(); 109 $this->_loadSettings();
110   110  
111 list($file, $lang, $title, $start, $end) = $data; 111 list($file, $lang, $title, $start, $end) = $data;
112 $ext = substr(strrchr($file, '.'),1); 112 $ext = substr(strrchr($file, '.'),1);
113   113  
114 $ok = false; 114 $ok = false;
115 if (count($this->allow)) { 115 if (count($this->allow)) {
116 if (in_array($ext, $this->allow)) $ok = true; 116 if (in_array($ext, $this->allow)) $ok = true;
117 } else { 117 } else {
118 if (!in_array($ext, $this->deny)) $ok = true; 118 if (!in_array($ext, $this->deny)) $ok = true;
119 } 119 }
120   120  
121 // prevent filenames which attempt to move up directory tree by using ".." 121 // prevent filenames which attempt to move up directory tree by using ".."
122 if ($ok && $this->location && preg_match('/(?:^|\/)\.\.(?:\/|$)/', $file)) $ok = false; 122 if ($ok && $this->location && preg_match('/(?:^|\/)\.\.(?:\/|$)/', $file)) $ok = false;
123 if ($ok && $this->rules) $ok = $this->_checkRules($file); 123 if ($ok && $this->rules) $ok = $this->_checkRules($file);
124   124  
125 if (!$lang) { $lang = isset($this->extensions[$ext]) ? $this->extensions[$ext] : $ext; } 125 if (!$lang) { $lang = isset($this->extensions[$ext]) ? $this->extensions[$ext] : $ext; }
126   126  
127 switch ($format) { 127 switch ($format) {
128 case 'xhtml' : 128 case 'xhtml' :
129   129  
130 if ($ok && ($source = $this->_getSource($file,$start,$end))) { 130 if ($ok && ($source = $this->_getSource($file,$start,$end))) {
131   131  
132 $title = ($title) ? "<span>".hsc($title)."</span>" 132 $title = ($title) ? "<span>".hsc($title)."</span>"
133 : $this->_makeTitle($file, $start, $end); 133 : $this->_makeTitle($file, $start, $end);
134   134  
135 $renderer->doc .= "<div class='source'><p class='title'>$title</p>"; 135 $renderer->doc .= "<div class='source'><p class='title'>$title</p>";
136 $renderer->code($source, $lang); 136 $renderer->code($source, $lang);
137 $renderer->doc .= "</div>"; 137 $renderer->doc .= "</div>";
138 } else { 138 } else {
139 $error = sprintf($this->getLang('error_file'),hsc($file)); 139 $error = sprintf($this->getLang('error_file'),hsc($file));
140 $renderer->doc .= '<div class="source"><p><span>'.$error.'</span></p></div>'; 140 $renderer->doc .= '<div class="source"><p><span>'.$error.'</span></p></div>';
141 } 141 }
142 break; 142 break;
143   143  
144 case 'metadata' : 144 case 'metadata' :
145 if ($ok) { 145 if ($ok) {
146 $renderer->meta['relation']['haspart'][$file] = array('owner'=>$this->getPluginName()); 146 $renderer->meta['relation']['haspart'][$file] = array('owner'=>$this->getPluginName());
147 } 147 }
148 break; 148 break;
149   149  
150 default : 150 default :
151 if ($ok) { 151 if ($ok) {
152 $renderer->code($this->_getSource($file,$start,$end), $lang); 152 $renderer->code($this->_getSource($file,$start,$end), $lang);
153 } 153 }
154 } 154 }
155   155  
156 } 156 }
157   157  
158 function _makeTitle($file,$start,$end) { 158 function _makeTitle($file,$start,$end) {
159 $lines = $start ? sprintf($this->getLang('lines'),$start,$end) : ''; 159 $lines = $start ? sprintf($this->getLang('lines'),$start,$end) : '';
160 $title = sprintf($this->getLang('title'),hsc($file),$lines); 160 $title = sprintf($this->getLang('title'),hsc($file),$lines);
161   161  
162 return $title; 162 return $title;
163 } 163 }
164   164  
165 function _getSource($file,$start,$end) { 165 function _getSource($file,$start,$end) {
166   166  
167 $source = @file($this->location.$file); 167 $source = @file($this->location.$file);
168 if (empty($source)) return ''; 168 if (empty($source)) return '';
169   169  
170 // $start is a 1 based index, need to correct to 0 based when slicing arrray 170 // $start is a 1 based index, need to correct to 0 based when slicing arrray
171 if (!empty($start)) { 171 if (!empty($start)) {
172 $lines = count($source); 172 $lines = count($source);
173 if ($start > $lines) { 173 if ($start > $lines) {
174 $source = $this->getLang('error_start'); 174 $source = $this->getLang('error_start');
175 } else if ($end < $start) { 175 } else if ($end < $start) {
176 $source = $this->getLang('error_end'); 176 $source = $this->getLang('error_end');
177 } else if ($end > $lines) { 177 } else if ($end > $lines) {
178 $source = join('',array_slice($source,$start-1)); 178 $source = join('',array_slice($source,$start-1));
179 } else { 179 } else {
180 $source = join('',array_slice($source,$start-1,$end-$start)); 180 $source = join('',array_slice($source,$start-1,$end-$start));
181 } 181 }
182 } else { 182 } else {
183 $source = join('',$source); 183 $source = join('',$source);
184 } 184 }
185   185  
186 return $source; 186 return $source;
187 } 187 }
188   188  
189 function _checkRules($file) { 189 function _checkRules($file) {
190 $permit = true; 190 $permit = true;
191 foreach ($this->rules as $rule) { 191 foreach ($this->rules as $rule) {
192 list($allow_deny, $pattern) = $rule; 192 list($allow_deny, $pattern) = $rule;
193 if ($allow_deny == 'allow') { 193 if ($allow_deny == 'allow') {
194 if (preg_match($pattern,$file)) $permit = true; 194 if (preg_match($pattern,$file)) $permit = true;
195 } else { 195 } else {
196 if (preg_match($pattern,$file)) $permit = false; 196 if (preg_match($pattern,$file)) $permit = false;
197 } 197 }
198 } 198 }
199   199  
200 return $permit; 200 return $permit;
201 } 201 }
202   202  
203 function _loadSettings() { 203 function _loadSettings() {
204 static $loaded = false; 204 static $loaded = false;
205   205  
206 if ($loaded) return; 206 if ($loaded) return;
207   207  
208 $this->location = $this->getConf('location'); 208 $this->location = $this->getConf('location');
209   209  
210 $allow = $this->getConf('allow'); 210 $allow = $this->getConf('allow');
211 $this->allow = !empty($allow) ? explode('|',$allow) : array(); 211 $this->allow = !empty($allow) ? explode('|',$allow) : array();
212   212  
213 $deny = $this->getConf('deny'); 213 $deny = $this->getConf('deny');
214 $this->deny = !empty($deny) ? explode('|',$deny) : array(); 214 $this->deny = !empty($deny) ? explode('|',$deny) : array();
215   215  
216 $rules = $this->getConf('rules'); 216 $rules = $this->getConf('rules');
217 if (!empty($rules)) $this->_parseRules($rules); 217 if (!empty($rules)) $this->_parseRules($rules);
218   218  
219 $loaded = true; 219 $loaded = true;
220 } 220 }
221   221  
222 function _parseRules($rules) { 222 function _parseRules($rules) {
223 $rules = explode("\n",$rules); 223 $rules = explode("\n",$rules);
224 foreach ($rules as $rule) { 224 foreach ($rules as $rule) {
225 $rule = trim($rule); 225 $rule = trim($rule);
226 if (!$rule || $rule{0} == ';') continue; 226 if (!$rule || $rule{0} == ';') continue;
227   227  
228 $match = array(); 228 $match = array();
229 if (!preg_match('/^(allow|deny)\s+(.+)$/i',$rule,$match)) continue; 229 if (!preg_match('/^(allow|deny)\s+(.+)$/i',$rule,$match)) continue;
230   230  
231 $this->rules[] = array(strtolower($match[1]),$match[2]); 231 $this->rules[] = array(strtolower($match[1]),$match[2]);
232 } 232 }
233 } 233 }
234 } 234 }
235   235  
236 //Setup VIM: ex: et ts=4 enc=utf-8 : 236 //Setup VIM: ex: et ts=4 enc=utf-8 :
237   237  
238
Generated by GNU Enscript 1.6.5.90.
238
Generated by GNU Enscript 1.6.5.90.
239   239  
240   240  
241   241