1a34ed36bSDennis Ploeger<?php 2a34ed36bSDennis Ploeger/** 3a34ed36bSDennis Ploeger * Ditaa-Plugin: Converts Ascii-Flowcharts into a png-File 4a34ed36bSDennis Ploeger * 5a34ed36bSDennis Ploeger * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6a34ed36bSDennis Ploeger * @author Dennis Ploeger <develop [at] dieploegers [dot] de> 7a34ed36bSDennis Ploeger * @author Christoph Mertins <c [dot] mertins [at] gmail [dot] com> 8c5eb4ae0SGerry Weißbach * @author Gerry Weißbach / i-net software <tools [at] inetsoftware [dot] de> 9c9ea14c6Seinhirn * @author Christian Marg <marg@rz.tu-clausthal.de> 10d402f512SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 11a34ed36bSDennis Ploeger */ 12a34ed36bSDennis Ploeger 13a34ed36bSDennis Ploegerif(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/'); 14a34ed36bSDennis Ploegerif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 15a34ed36bSDennis Ploegerrequire_once(DOKU_PLUGIN . 'syntax.php'); 16a34ed36bSDennis Ploeger 1735cad792SAndreas Gohr/** 1835cad792SAndreas Gohr * Class syntax_plugin_ditaa 1935cad792SAndreas Gohr */ 20a34ed36bSDennis Ploegerclass syntax_plugin_ditaa extends DokuWiki_Syntax_Plugin { 21a34ed36bSDennis Ploeger 22a34ed36bSDennis Ploeger /** 23a34ed36bSDennis Ploeger * What about paragraphs? 24a34ed36bSDennis Ploeger */ 2535cad792SAndreas Gohr public function getPType() { 26a34ed36bSDennis Ploeger return 'normal'; 27a34ed36bSDennis Ploeger } 28a34ed36bSDennis Ploeger 29a34ed36bSDennis Ploeger /** 30a34ed36bSDennis Ploeger * What kind of syntax are we? 31a34ed36bSDennis Ploeger */ 3235cad792SAndreas Gohr public function getType() { 33a34ed36bSDennis Ploeger return 'substition'; 34a34ed36bSDennis Ploeger } 35a34ed36bSDennis Ploeger 36a34ed36bSDennis Ploeger /** 37a34ed36bSDennis Ploeger * Where to sort in? 38a34ed36bSDennis Ploeger */ 3935cad792SAndreas Gohr public function getSort() { 40a34ed36bSDennis Ploeger return 200; 41a34ed36bSDennis Ploeger } 42a34ed36bSDennis Ploeger 43a34ed36bSDennis Ploeger /** 4472a2bf1dSAndreas Gohr * Connect pattern to lexer 4535cad792SAndreas Gohr * 4635cad792SAndreas Gohr * @param string $mode 47a34ed36bSDennis Ploeger */ 4835cad792SAndreas Gohr public function connectTo($mode) { 4972a2bf1dSAndreas Gohr $this->Lexer->addSpecialPattern('<ditaa.*?>\n.*?\n</ditaa>', $mode, 'plugin_ditaa'); 50a34ed36bSDennis Ploeger } 51a34ed36bSDennis Ploeger 52a34ed36bSDennis Ploeger /** 5335cad792SAndreas Gohr * Stores all infor about the diagram in two files. One is the actual ditaa data, the other 5435cad792SAndreas Gohr * contains the options. 5535cad792SAndreas Gohr * 5635cad792SAndreas Gohr * @param string $match The text matched by the patterns 5735cad792SAndreas Gohr * @param int $state The lexer state for the match 5835cad792SAndreas Gohr * @param int $pos The character position of the matched text 5935cad792SAndreas Gohr * @param Doku_Handler $handler The Doku_Handler object 6035cad792SAndreas Gohr * @return bool|array Return an array with all data you want to use in render, false don't add an instruction 61a34ed36bSDennis Ploeger */ 6235cad792SAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) { 632a956e66SAndreas Gohr $info = $this->getInfo(); 642a956e66SAndreas Gohr 6572a2bf1dSAndreas Gohr // prepare default data 6672a2bf1dSAndreas Gohr $return = array( 6772a2bf1dSAndreas Gohr 'width' => 0, 6872a2bf1dSAndreas Gohr 'height' => 0, 6972a2bf1dSAndreas Gohr 'antialias' => true, 7072a2bf1dSAndreas Gohr 'edgesep' => true, 7172a2bf1dSAndreas Gohr 'round' => false, 7272a2bf1dSAndreas Gohr 'shadow' => true, 7372a2bf1dSAndreas Gohr 'scale' => 1, 7472a2bf1dSAndreas Gohr 'align' => '', 75*e2392f8aSAndreas Gohr 'version' => $info['date'], 76*e2392f8aSAndreas Gohr 'now' => time() 7772a2bf1dSAndreas Gohr ); 78a34ed36bSDennis Ploeger 7972a2bf1dSAndreas Gohr // prepare input 8072a2bf1dSAndreas Gohr $lines = explode("\n", $match); 8172a2bf1dSAndreas Gohr $conf = array_shift($lines); 8272a2bf1dSAndreas Gohr array_pop($lines); 8372a2bf1dSAndreas Gohr 8472a2bf1dSAndreas Gohr // match config options 8572a2bf1dSAndreas Gohr if(preg_match('/\b(left|center|right)\b/i', $conf, $match)) $return['align'] = $match[1]; 8672a2bf1dSAndreas Gohr if(preg_match('/\b(\d+)x(\d+)\b/', $conf, $match)) { 8772a2bf1dSAndreas Gohr $return['width'] = $match[1]; 8872a2bf1dSAndreas Gohr $return['height'] = $match[2]; 89a34ed36bSDennis Ploeger } 902a956e66SAndreas Gohr if(preg_match('/\b(\d+(\.\d+)?)X\b/', $conf, $match)) $return['scale'] = $match[1]; 9172a2bf1dSAndreas Gohr if(preg_match('/\bwidth=([0-9]+)\b/i', $conf, $match)) $return['width'] = $match[1]; 9272a2bf1dSAndreas Gohr if(preg_match('/\bheight=([0-9]+)\b/i', $conf, $match)) $return['height'] = $match[1]; 9372a2bf1dSAndreas Gohr // match boolean toggles 9472a2bf1dSAndreas Gohr if(preg_match_all('/\b(no)?(antialias|edgesep|round|shadow)\b/i', $conf, $matches, PREG_SET_ORDER)) { 9572a2bf1dSAndreas Gohr foreach($matches as $match) { 9672a2bf1dSAndreas Gohr $return[$match[2]] = !$match[1]; 9772a2bf1dSAndreas Gohr } 9872a2bf1dSAndreas Gohr } 9972a2bf1dSAndreas Gohr 10005cbae88SAndreas Gohr $input = join("\n", $lines); 101*e2392f8aSAndreas Gohr $return['md5'] = md5($input.$this->_prepareData($return)); // we only pass a hash around 10205cbae88SAndreas Gohr 10335cad792SAndreas Gohr // store input for later use in _imagefile() 10435cad792SAndreas Gohr io_saveFile(getCacheName($return['md5'], '.ditaa.txt'), $input); 10535cad792SAndreas Gohr io_saveFile(getCacheName($return['md5'], '.ditaa.cfg'), serialize($return)); 10672a2bf1dSAndreas Gohr 10772a2bf1dSAndreas Gohr return $return; 108a34ed36bSDennis Ploeger } 109a34ed36bSDennis Ploeger 110a34ed36bSDennis Ploeger /** 11135cad792SAndreas Gohr * Output the image 11235cad792SAndreas Gohr * 11335cad792SAndreas Gohr * @param string $format output format being rendered 11435cad792SAndreas Gohr * @param Doku_Renderer $R the current renderer object 11535cad792SAndreas Gohr * @param array $data data created by handler() 11635cad792SAndreas Gohr * @return boolean rendered correctly? 117c5eb4ae0SGerry Weißbach */ 11835cad792SAndreas Gohr public function render($format, Doku_Renderer $R, $data) { 11938c92790SGerry Weißbach global $ID; 12095615d6cSAndreas Gohr if($format == 'xhtml') { 12138c92790SGerry Weißbach // Only use the md5 key 122*e2392f8aSAndreas Gohr $img = ml($ID, array('ditaa' => $data['md5'], 't' => $data['now'])); 12395615d6cSAndreas Gohr $R->doc .= '<img src="' . $img . '" class="media' . $data['align'] . '" alt=""'; 12495615d6cSAndreas Gohr if($data['width']) $R->doc .= ' width="' . $data['width'] . '"'; 12595615d6cSAndreas Gohr if($data['height']) $R->doc .= ' height="' . $data['height'] . '"'; 126c77baa73SWilli Schönborn if($data['align'] == 'right') $R->doc .= ' align="right"'; 127c77baa73SWilli Schönborn if($data['align'] == 'left') $R->doc .= ' align="left"'; 12895615d6cSAndreas Gohr $R->doc .= '/>'; 12995615d6cSAndreas Gohr return true; 13095615d6cSAndreas Gohr } else if($format == 'odt') { 13135cad792SAndreas Gohr $src = $this->_imgfile($data['md5']); 13235cad792SAndreas Gohr /** @var renderer_plugin_odt $R */ 13395615d6cSAndreas Gohr $R->_odtAddImage($src, $data['width'], $data['height'], $data['align']); 13495615d6cSAndreas Gohr return true; 13595615d6cSAndreas Gohr } 13695615d6cSAndreas Gohr return false; 13795615d6cSAndreas Gohr } 13895615d6cSAndreas Gohr 13995615d6cSAndreas Gohr /** 140*e2392f8aSAndreas Gohr * Prepares the Data that is used for the cache name 141*e2392f8aSAndreas Gohr * Width, height and scale are left out. 142*e2392f8aSAndreas Gohr * Ensures sanity. 143*e2392f8aSAndreas Gohr */ 144*e2392f8aSAndreas Gohr protected function _prepareData($input) { 145*e2392f8aSAndreas Gohr $output = array(); 146*e2392f8aSAndreas Gohr foreach($input as $key => $value) { 147*e2392f8aSAndreas Gohr switch($key) { 148*e2392f8aSAndreas Gohr case 'scale': 149*e2392f8aSAndreas Gohr case 'antialias': 150*e2392f8aSAndreas Gohr case 'edgesep': 151*e2392f8aSAndreas Gohr case 'round': 152*e2392f8aSAndreas Gohr case 'shadow': 153*e2392f8aSAndreas Gohr case 'version': 154*e2392f8aSAndreas Gohr $output[$key] = $value; 155*e2392f8aSAndreas Gohr }; 156*e2392f8aSAndreas Gohr } 157*e2392f8aSAndreas Gohr ksort($output); 158*e2392f8aSAndreas Gohr return $output; 159*e2392f8aSAndreas Gohr } 160*e2392f8aSAndreas Gohr 161*e2392f8aSAndreas Gohr /** 16295615d6cSAndreas Gohr * Return path to the rendered image on our local system 16335cad792SAndreas Gohr * 16435cad792SAndreas Gohr * @param string $md5 MD5 of the input data, used to identify the cache files 16535cad792SAndreas Gohr * @return false|string path to file or fals on error 16695615d6cSAndreas Gohr */ 16735cad792SAndreas Gohr public function _imgfile($md5) { 16835cad792SAndreas Gohr $file_cfg = getCacheName($md5, '.ditaa.cfg'); // configs 16935cad792SAndreas Gohr $file_txt = getCacheName($md5, '.ditaa.txt'); // input 17035cad792SAndreas Gohr $file_png = getCacheName($md5, '.ditaa.png'); // ouput 17138c92790SGerry Weißbach 17235cad792SAndreas Gohr if(!file_exists($file_cfg) || !file_exists($file_txt)) { 17335cad792SAndreas Gohr return false; 17438c92790SGerry Weißbach } 17535cad792SAndreas Gohr $data = unserialize(io_readFile($file_cfg, false)); 17635cad792SAndreas Gohr 17735cad792SAndreas Gohr // file does not exist or is outdated 17835cad792SAndreas Gohr if(@filemtime($file_png) < filemtime($file_cfg)) { 17938c92790SGerry Weißbach 18095615d6cSAndreas Gohr if($this->getConf('java')) { 18135cad792SAndreas Gohr $ok = $this->_runJava($data, $file_txt, $file_png); 18295615d6cSAndreas Gohr } else { 18335cad792SAndreas Gohr $ok = $this->_runGo($data, $file_txt, $file_png); 184967610bbSAndreas Gohr #$ok = $this->_remote($data, $in, $cache); 18595615d6cSAndreas Gohr } 18695615d6cSAndreas Gohr if(!$ok) return false; 18735cad792SAndreas Gohr 18835cad792SAndreas Gohr clearstatcache($file_png); 18995615d6cSAndreas Gohr } 19095615d6cSAndreas Gohr 19195615d6cSAndreas Gohr // resized version 19295615d6cSAndreas Gohr if($data['width']) { 19335cad792SAndreas Gohr $file_png = media_resize_image($file_png, 'png', $data['width'], $data['height']); 19495615d6cSAndreas Gohr } 19595615d6cSAndreas Gohr 19695615d6cSAndreas Gohr // something went wrong, we're missing the file 19735cad792SAndreas Gohr if(!file_exists($file_png)) return false; 19895615d6cSAndreas Gohr 19935cad792SAndreas Gohr return $file_png; 20095615d6cSAndreas Gohr } 20195615d6cSAndreas Gohr 20295615d6cSAndreas Gohr /** 20305cbae88SAndreas Gohr * Render the output remotely at ditaa.org 20435cad792SAndreas Gohr * 20535cad792SAndreas Gohr * @deprecated ditaa.org is no longer available, so this defunct 20635cad792SAndreas Gohr * @param array $data The config settings 20735cad792SAndreas Gohr * @param string $in Path to the ditaa input file (txt) 20835cad792SAndreas Gohr * @param string $out Path to the output file (PNG) 20935cad792SAndreas Gohr * @return bool true if the image was created, false otherwise 21005cbae88SAndreas Gohr */ 21135cad792SAndreas Gohr protected function _remote($data, $in, $out) { 212967610bbSAndreas Gohr global $conf; 213967610bbSAndreas Gohr 21495615d6cSAndreas Gohr if(!file_exists($in)) { 21595615d6cSAndreas Gohr if($conf['debug']) { 21695615d6cSAndreas Gohr dbglog($in, 'no such ditaa input file'); 21795615d6cSAndreas Gohr } 21895615d6cSAndreas Gohr return false; 21995615d6cSAndreas Gohr } 22095615d6cSAndreas Gohr 22105cbae88SAndreas Gohr $http = new DokuHTTPClient(); 22205cbae88SAndreas Gohr $http->timeout = 30; 22305cbae88SAndreas Gohr 22405cbae88SAndreas Gohr $pass = array(); 22505cbae88SAndreas Gohr $pass['scale'] = $data['scale']; 22605cbae88SAndreas Gohr $pass['timeout'] = 25; 22705cbae88SAndreas Gohr $pass['grid'] = io_readFile($in); 22805cbae88SAndreas Gohr if(!$data['antialias']) $pass['A'] = 'on'; 22905cbae88SAndreas Gohr if(!$data['shadow']) $pass['S'] = 'on'; 23005cbae88SAndreas Gohr if($data['round']) $pass['r'] = 'on'; 23105cbae88SAndreas Gohr if(!$data['edgesep']) $pass['E'] = 'on'; 23205cbae88SAndreas Gohr 23305cbae88SAndreas Gohr $img = $http->post('http://ditaa.org/ditaa/render', $pass); 23405cbae88SAndreas Gohr if(!$img) return false; 23505cbae88SAndreas Gohr 23695615d6cSAndreas Gohr return io_saveFile($out, $img); 23705cbae88SAndreas Gohr } 23805cbae88SAndreas Gohr 239a34ed36bSDennis Ploeger /** 2402a956e66SAndreas Gohr * Run the ditaa Java program 24135cad792SAndreas Gohr * 24235cad792SAndreas Gohr * @param array $data The config settings 24335cad792SAndreas Gohr * @param string $in Path to the ditaa input file (txt) 24435cad792SAndreas Gohr * @param string $out Path to the output file (PNG) 24535cad792SAndreas Gohr * @return bool true if the image was created, false otherwise 246a34ed36bSDennis Ploeger */ 24735cad792SAndreas Gohr protected function _runJava($data, $in, $out) { 2482a956e66SAndreas Gohr global $conf; 249a34ed36bSDennis Ploeger 25005cbae88SAndreas Gohr if(!file_exists($in)) { 25105cbae88SAndreas Gohr if($conf['debug']) { 25205cbae88SAndreas Gohr dbglog($in, 'no such ditaa input file'); 25305cbae88SAndreas Gohr } 25405cbae88SAndreas Gohr return false; 25505cbae88SAndreas Gohr } 256a34ed36bSDennis Ploeger 2572a956e66SAndreas Gohr $cmd = $this->getConf('java'); 25836721e14SAndreas Gohr $cmd .= ' -Djava.awt.headless=true -Dfile.encoding=UTF-8 -jar'; 259c65b5f53SAndreas Gohr $cmd .= ' ' . escapeshellarg(__DIR__ . '/ditaa/ditaa.jar'); 26036721e14SAndreas Gohr $cmd .= ' --encoding UTF-8'; 26105cbae88SAndreas Gohr $cmd .= ' ' . escapeshellarg($in); //input 26205cbae88SAndreas Gohr $cmd .= ' ' . escapeshellarg($out); //output 2632a956e66SAndreas Gohr $cmd .= ' -s ' . escapeshellarg($data['scale']); 2642a956e66SAndreas Gohr if(!$data['antialias']) $cmd .= ' -A'; 2652a956e66SAndreas Gohr if(!$data['shadow']) $cmd .= ' -S'; 2662a956e66SAndreas Gohr if($data['round']) $cmd .= ' -r'; 2672a956e66SAndreas Gohr if(!$data['edgesep']) $cmd .= ' -E'; 268a34ed36bSDennis Ploeger 269a34ed36bSDennis Ploeger exec($cmd, $output, $error); 270a34ed36bSDennis Ploeger 2713c74ed32SAndreas Gohr if($error != 0) { 2723c74ed32SAndreas Gohr if($conf['debug']) { 2733c74ed32SAndreas Gohr dbglog(join("\n", $output), 'ditaa command failed: ' . $cmd); 2743c74ed32SAndreas Gohr } 2753c74ed32SAndreas Gohr return false; 2763c74ed32SAndreas Gohr } 27705cbae88SAndreas Gohr 278a34ed36bSDennis Ploeger return true; 279a34ed36bSDennis Ploeger } 280a34ed36bSDennis Ploeger 281967610bbSAndreas Gohr /** 282967610bbSAndreas Gohr * Run the ditaa Go program 28335cad792SAndreas Gohr * 28435cad792SAndreas Gohr * @param array $data The config settings - currently not used because the Go relase supports no options 28535cad792SAndreas Gohr * @param string $in Path to the ditaa input file (txt) 28635cad792SAndreas Gohr * @param string $out Path to the output file (PNG) 28735cad792SAndreas Gohr * @return bool true if the image was created, false otherwise 288967610bbSAndreas Gohr */ 28935cad792SAndreas Gohr protected function _runGo($data, $in, $out) { 290967610bbSAndreas Gohr global $conf; 291967610bbSAndreas Gohr 292967610bbSAndreas Gohr if(!file_exists($in)) { 293967610bbSAndreas Gohr if($conf['debug']) { 294967610bbSAndreas Gohr dbglog($in, 'no such ditaa input file'); 295967610bbSAndreas Gohr } 296967610bbSAndreas Gohr return false; 297967610bbSAndreas Gohr } 298967610bbSAndreas Gohr 299967610bbSAndreas Gohr $cmd = $this->getLocalBinary(); 300967610bbSAndreas Gohr if(!$cmd) return false; 301967610bbSAndreas Gohr $cmd .= ' ' . escapeshellarg($in); //input 302967610bbSAndreas Gohr $cmd .= ' ' . escapeshellarg($out); //output 303967610bbSAndreas Gohr 304967610bbSAndreas Gohr exec($cmd, $output, $error); 305967610bbSAndreas Gohr 306967610bbSAndreas Gohr if($error != 0) { 307967610bbSAndreas Gohr if($conf['debug']) { 308967610bbSAndreas Gohr dbglog(join("\n", $output), 'ditaa command failed: ' . $cmd); 309967610bbSAndreas Gohr } 310967610bbSAndreas Gohr return false; 311967610bbSAndreas Gohr } 312967610bbSAndreas Gohr 313967610bbSAndreas Gohr return true; 314967610bbSAndreas Gohr } 315967610bbSAndreas Gohr 316967610bbSAndreas Gohr /** 317967610bbSAndreas Gohr * Detects the platform of the PHP host and constructs the appropriate binary name 318967610bbSAndreas Gohr * 319967610bbSAndreas Gohr * @return false|string 320967610bbSAndreas Gohr */ 321967610bbSAndreas Gohr protected function getBinaryName() { 322967610bbSAndreas Gohr $ext = ''; 323967610bbSAndreas Gohr 324967610bbSAndreas Gohr $os = php_uname('s'); 325967610bbSAndreas Gohr if(preg_match('/darwin/i', $os)) { 326967610bbSAndreas Gohr $os = 'darwin'; 327967610bbSAndreas Gohr } elseif(preg_match('/win/i', $os)) { 328967610bbSAndreas Gohr $os = 'windows'; 329967610bbSAndreas Gohr $ext = '.exe'; 330967610bbSAndreas Gohr } elseif(preg_match('/linux/i', $os)) { 331967610bbSAndreas Gohr $os = 'linux'; 332967610bbSAndreas Gohr } elseif(preg_match('/freebsd/i', $os)) { 333967610bbSAndreas Gohr $os = 'freebsd'; 334967610bbSAndreas Gohr } elseif(preg_match('/openbsd/i', $os)) { 335967610bbSAndreas Gohr $os = 'openbsd'; 336967610bbSAndreas Gohr } elseif(preg_match('/netbsd/i', $os)) { 337967610bbSAndreas Gohr $os = 'netbsd'; 338967610bbSAndreas Gohr } elseif(preg_match('/(solaris|netbsd)/i', $os)) { 339967610bbSAndreas Gohr $os = 'freebsd'; 340967610bbSAndreas Gohr } else { 341967610bbSAndreas Gohr return false; 342967610bbSAndreas Gohr } 343967610bbSAndreas Gohr 344967610bbSAndreas Gohr $arch = php_uname('m'); 345967610bbSAndreas Gohr if($arch == 'x86_64') { 346967610bbSAndreas Gohr $arch = 'amd64'; 347967610bbSAndreas Gohr } elseif(preg_match('/arm/i', $arch)) { 348967610bbSAndreas Gohr $arch = 'amd'; 349967610bbSAndreas Gohr } else { 350967610bbSAndreas Gohr $arch = '386'; 351967610bbSAndreas Gohr } 352967610bbSAndreas Gohr 353967610bbSAndreas Gohr return "ditaa-$os-$arch$ext"; 354967610bbSAndreas Gohr } 355967610bbSAndreas Gohr 356967610bbSAndreas Gohr /** 357967610bbSAndreas Gohr * Returns the local binary to use 358967610bbSAndreas Gohr * 35935cad792SAndreas Gohr * Downloads it if necessary 36035cad792SAndreas Gohr * 361967610bbSAndreas Gohr * @return bool|string 362967610bbSAndreas Gohr */ 363967610bbSAndreas Gohr protected function getLocalBinary() { 364967610bbSAndreas Gohr global $conf; 365967610bbSAndreas Gohr 366967610bbSAndreas Gohr $bin = $this->getBinaryName(); 367967610bbSAndreas Gohr if(!$bin) return false; 368967610bbSAndreas Gohr 369967610bbSAndreas Gohr // check distributed files first 370967610bbSAndreas Gohr if(file_exists(__DIR__ . '/ditaa/' . $bin)) { 371967610bbSAndreas Gohr return __DIR__ . '/ditaa/' . $bin; 372967610bbSAndreas Gohr } 373967610bbSAndreas Gohr 374967610bbSAndreas Gohr $info = $this->getInfo(); 375967610bbSAndreas Gohr $cache = getCacheName($info['date'], ".$bin"); 376967610bbSAndreas Gohr 377967610bbSAndreas Gohr if(file_exists($cache)) return $cache; 378967610bbSAndreas Gohr 379400b2bf3SAndreas Gohr $url = 'https://github.com/akavel/ditaa/releases/download/g1.0.0/' . $bin; 380967610bbSAndreas Gohr if(io_download($url, $cache, false, '', 0)) { 381967610bbSAndreas Gohr @chmod($cache, $conf['dmode']); 382967610bbSAndreas Gohr return $cache; 383967610bbSAndreas Gohr } 384967610bbSAndreas Gohr 385967610bbSAndreas Gohr return false; 386967610bbSAndreas Gohr } 387a34ed36bSDennis Ploeger} 388a34ed36bSDennis Ploeger 389