1<?php
2/**
3 * eclipseUpdate Plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     i-net software <tools@inetsoftware.de>
7 * @author     Gerry Weissbach <gweissbach@inetsoftware.de>
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
13
14require_once(DOKU_PLUGIN.'syntax.php');
15
16class syntax_plugin_eclipseupdateurl extends DokuWiki_Syntax_Plugin {
17
18    function getType() { return 'substition'; }
19    function getPType() { return 'block'; }
20    function getSort() { return 99; }
21
22    function connectTo($mode) {
23        $this->Lexer->addSpecialPattern("\[\[eclipseUpdate>.*?\]\]", $mode, 'plugin_eclipseupdateurl');
24    }
25
26    function handle($match, $state, $pos, Doku_Handler $handler) {
27
28		$ID = substr($match, 16, -2);
29		list( $ID, $opts) = explode( '|', $ID, 2 );
30
31		$opts = explode( '|', $opts );
32		$fOpts = array();
33		$fOpts['amount'] = $this->getConf('showamount');
34
35		foreach ( $opts as $opt ) {
36
37			if ( empty($opt) ) continue;
38			if ( is_numeric($opt) ) {
39				$fOpts['amount'] = $opt;
40			} else {
41				list($key, $value) = explode('=', $opt, 2);
42
43				switch ( $key ) {
44					case 'direct' :		$fOpts['direct'] = true;
45										break;
46					case 'name' :		$fOpts['name'] = $value;
47										break;
48					case 'category' :	$fOpts['category'] = $value;
49										break;
50					case 'id' :			$fOpts['id'] = $value;
51										break;
52				}
53			}
54		}
55
56
57		return array( $ID, $fOpts );
58    }
59
60    function render($mode, Doku_Renderer $renderer, $data) {
61
62		list ( $data, $opts ) = $data;
63		if ( $mode == 'xhtml' ) {
64
65			if ( !preg_match("/site.xml$/", $data) ) {
66				msg("'$data' is not a valid site.xml for Eclipse Updates");
67				return false;
68			}
69
70			if ( !preg_match( "&^(?i)https?://&", $data ) ) {
71				$fn = mediaFN($data);
72				if ( !file_exists($fn) ) {
73					msg("'$data' does not exist");
74					return false;
75				}
76
77				$fn = 'file://' . $fn;
78			} else {
79				$fn = $data;
80			}
81
82			list ($PLUGINDESC, $FEATURELIST) = $this->_getSiteXML($fn);
83
84			if ( $opts['direct'] ) {
85
86				// Selected Category or first
87				$ITEMS = $opts['category'] ? $FEATURELIST[$opts['category']] : array_shift($FEATURELIST);
88
89				// Latest
90				$ITEM = $ITEMS['features'][count($ITEMS['features'])-1];
91
92				// Plugin Element
93				$PLUGIN = $ITEM['list'][0];
94
95				if ( $opt['id'] ) {
96					foreach( $ITEM['list'] as $PLUGIN ) {
97						if ( $PLUGIN['id'] != $opt['id'] ) { continue; }
98						break;
99					}
100				}
101
102				// Name
103				$NAME = $opts['name'] ? $opts['name'] : $PLUGIN['id'] . '_' . $PLUGIN['version'] . '.jar';
104
105				//link
106				$renderer->doc .= $this->tpl_link($ITEM['url'] . 'plugins/' .  $PLUGIN['id'] . '_' . $PLUGIN['version'] . '.jar', $NAME);
107
108				return true;
109			}
110
111			$this->_getEclipseList($renderer, $PLUGINSDESC, $FEATURELIST);
112
113		}
114
115		return true;
116	}
117
118	function _getEclipseList(&$renderer, $PLUGINDESC, $FEATURELIST) {
119
120		$renderer->doc .= "<p>$PLUGINDESC</p>";
121		foreach ( $FEATURELIST as $CATEGORY => $ITEMS ) {
122
123			$renderer->doc .= '<strong>' . $CATEGORY . '</strong>';
124
125			$renderer->doc .= '<table class="eclipsepluginurl inline">';
126
127			$renderer->doc .= '<tr>';
128				$renderer->doc .= '<th>Version</th>';
129				$renderer->doc .= '<th>Component</th>';
130				$renderer->doc .= '<th>Size</th>';
131			$renderer->doc .= '</tr>';
132
133			foreach ( array_reverse(array_slice($ITEMS['features'], -$opts['amount'])) as $ITEM ) {
134
135				if ( !is_array($ITEM['list']) )
136					$ITEM['list'] = array($ITEM['list']);
137
138				$renderer->doc .= '<tr>';
139					$renderer->doc .= '<td rowspan="' . count($ITEM['list']) . '">' . $ITEM['version'] . '</td>';
140					$renderer->doc .= '<td>';
141
142					foreach ( $ITEM['list'] as $PLUGIN ) {
143						if ( count($ITEM['list']) > 1 && $PLUGIN != $ITEM['list'][0] )
144							$renderer->doc .= '</td></tr><tr><td>';
145
146						$renderer->doc .= $this->tpl_link($ITEM['url'] . 'plugins/' .  $PLUGIN['id'] . '_' . $PLUGIN['version'] . '.jar', $PLUGIN['id'] . '_' . $PLUGIN['version'] . '.jar');
147
148						$renderer->doc .= '</td><td>';
149
150						$renderer->doc .= sprintf("%.2f&nbsp;MB", ($PLUGIN['download-size'] / 1024));
151						$renderer->doc .= '</td>';
152					}
153
154				$renderer->doc .= '</tr>';
155			}
156
157			$renderer->doc .= '</table>';
158		}
159	}
160
161
162	function tpl_link($url,$name,$more=''){
163
164		$return = '<a href="'.$url.'" ';
165		if ($more) $return .= ' '.$more;
166		$return .= ">$name</a>";
167		return $return;
168	}
169
170	function _getSiteXML($URI) {
171
172		$reader = new XMLReader();
173		$reader->open($URI);
174
175		$PLUGINURL = null;
176		$PLUGINDESC = null;
177		$PLUGINLIST = array();
178
179		$FEATURE = null;
180
181		while ($reader->read()) {
182
183			if ( $reader->nodeType !== XMLReader::ELEMENT ) { continue; }
184
185			switch ($reader->name) {
186
187				case 'description':		$PLUGINURL = $reader->getAttribute('url');
188										$PLUGINDESC = $reader->readString();
189										break;
190
191				case 'feature' :		$FEATURE = $this->_getPluginURL($PLUGINURL . $reader->getAttribute('url'));
192
193										if ( $URL === false ) { $FEATURE = null; }
194
195										$FEATURE['id'] = $reader->getAttribute('id');
196										$FEATURE['version'] = $reader->getAttribute('version');
197
198										break;
199
200				case 'category' :		if ( empty($FEATURE) ) { continue; }
201										$CATEGORY = $reader->getAttribute('name');
202
203										// Add category
204										if ( empty( $PLUGINLIST[$CATEGORY] ) )
205											$PLUGINLIST[$CATEGORY] = array	(
206																				'label' => null,
207																				'features' => array(),
208																			);
209
210										// Put feature and reset
211										$PLUGINLIST[$CATEGORY]['features'][] = $FEATURE;
212										$FEATURE = null;
213
214										break;
215
216				case 'category-def' :	$CATEGORY = $reader->getAttribute('name');
217										$LABEL = $reader->getAttribute('label');
218
219										if ( !empty( $PLUGINLIST[$CATEGORY] ) )
220											$PLUGINLIST[$CATEGORY]['label'] = $LABEL;
221
222										break;
223			}
224		}
225
226		return array ($PLUGINDESC, $PLUGINLIST);
227	}
228
229	function _getFeatureXML($STRING) {
230
231		if ( empty($STRING) ) return false;
232
233		$reader = new XMLReader();
234		$reader->XML($STRING);
235
236		$PLUGINURL = null;
237		$PLUGINDESC = null;
238		$PLUGINLIST = array();
239
240		while ($reader->read()) {
241
242			if ( $reader->nodeType !== XMLReader::ELEMENT ) { continue; }
243
244			switch ($reader->name) {
245				case 'update'	:	$PLUGINURL = $reader->getAttribute('url');
246									$PLUGINDESC = $reader->getAttribute('label');
247									break;
248
249				case 'plugin'	:	$PLUGINLIST[] = array	(
250														'id' => $reader->getAttribute('id'),
251														'download-size' => $reader->getAttribute('download-size'),
252														'install-size' => $reader->getAttribute('install-size'),
253														'version' => $reader->getAttribute('version'),
254													);
255									break;
256
257				default			: 	continue;
258			}
259		}
260
261		return array ( 'url' => $PLUGINURL, 'desc' => $PLUGINDESC, 'list' => $PLUGINLIST);
262	}
263
264	function _getPluginURL($FEATUREURL) {
265		global $conf;
266
267		$CACHE  = $conf['cachetime'];
268		$FILE = $this->media_get_from_URL($FEATUREURL,'jar',$CACHE);
269
270		if ( $FILE === false ) {
271			return false;
272		}
273
274		return $this->_getFeatureXML($this->__fileExistsInJar($FILE, 'feature.xml', true));
275	}
276
277	/**
278	* Download jar files
279	*
280	* @author Andreas Gohr <andi@splitbrain.org>
281	*/
282	function media_get_from_URL($url,$ext,$cache) {
283		global $conf;
284
285		// if no cache or fetchsize just redirect
286		if ($cache==0)           return false;
287		if (!$conf['fetchsize']) return false;
288
289		$local = getCacheName(strtolower($url),".media.$ext");
290		$mtime = @filemtime($local); // 0 if not exists
291
292		//decide if download needed:
293		if( ($mtime == 0) ||                           // cache does not exist
294			($cache != -1 && $mtime < time()-$cache)   // 'recache' and cache has expired
295		){
296			if($this->media_download($url,$local)){
297				return $local;
298			}else{
299				return false;
300			}
301		}
302
303		//if cache exists use it else
304		if($mtime) return $local;
305
306		//else return false
307		return false;
308	}
309
310	/**
311	 * Download image files
312	 *
313	 * @author Andreas Gohr <andi@splitbrain.org>
314	 */
315	function media_download($url,$file){
316		global $conf;
317
318		//print $url;
319
320		$http = new DokuHTTPClient();
321		$http->max_bodysize = $conf['fetchsize'];
322		$http->timeout = 25; //max. 25 sec
323
324		$data = $http->get($url);
325		if(!$data) return false;
326
327		$fileexists = @file_exists($file);
328		$fp = @fopen($file,"w");
329		if(!$fp) return false;
330		fwrite($fp,$data);
331		fclose($fp);
332		if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
333
334		// check if it is really a zip
335		$info = @getimagesize($file);
336		if(!$this->__fileExistsInJar($file, 'feature.xml')){
337			@unlink($file);
338			return false;
339		}
340
341		return true;
342	}
343
344	function __fileExistsInJar($JAR, $NAME, $GET = false) {
345
346		if ( empty( $JAR ) ) return;
347
348		$zip = new ZipArchive;
349		$code = $zip->open($JAR);
350		if ($code === TRUE && !($zip->statName($NAME) === FALSE)) {
351
352			return $GET ? $zip->getFromName($NAME) : TRUE;
353		}
354
355		return false;
356	}
357}
358// vim:ts=4:sw=4:et:enc=utf-8:
359