dokuwiki-source-plugin – Blame information for rev 2

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