// smd_remote_file by Stef Dawson // Yuk :-( global $smd_remote_file_vars; $smd_remote_file_vars = array( 'msg' => '', 'min_txp_ver' => '4.0.6', ); if (@txpinterface === 'admin' && gps('event') === "file") { if (version_compare(@txp_version, $smd_remote_file_vars['min_txp_ver'], ">=")) { register_callback('smd_remote_file_replace', 'file', 'smd_remote_file_replace'); register_callback('smd_remote_file_update', 'file', 'file_save'); register_callback('smd_remote_file_create', 'file', 'smd_remote_file_create', 1); register_callback('smd_remote_file', 'file'); } else { trigger_error(gTxt('txp_version') . " " .$smd_remote_file_vars['min_txp_ver']. " required."); } } if (txpinterface === 'public') { register_callback('smd_remote_download', 'file_download'); } // Blow-by-blow equivalent of file_download_link, just a remote-aware version. // Adds the choose option. Defaults to 0 ( = random, for load balancing). Specify // any higher integer to grab that particular entry from the file (if it exists, else use 1st) function smd_file_download_link($atts, $thing) { global $thisfile; extract(lAtts(array( 'filename' => '', 'id' => '', 'choose' => '0', 'show_link' => '0', ), $atts)); // Remove the extra attributes not in the original tag unset($atts['show_link']); unset($atts['choose']); $keys = array("smd_choose" => $choose); $out = file_download_link($atts, $thing); if (strpos($out, ".link")) { $origLink = explode('"', $out); $idx = (count($origLink) == 1) ? 0 : 1; $origLink[$idx] = (($show_link) ? $origLink[$idx] : str_replace(".link", "", $origLink[$idx])) . join_qs($keys); // Will ignore join_qs if choose is 0 $out = implode('"', $origLink); } return $out; } // Equivalent to file_download_name but optionally removes the .link function smd_file_download_name($atts) { global $thisfile; extract(lAtts(array( 'show_link' => '0', ), $atts)); assert_file(); return ($show_link) ? $thisfile['filename'] : str_replace(".link", "", $thisfile['filename']); } // Add an image to the download form which, by default, is based on the filename of the download itself function smd_file_download_image($atts) { global $thisfile; assert_file(); extract(lAtts(array( 'filename' => '', 'id' => '', 'extension' => 'jpg', 'ifmissing' => '?ref', 'thumb' => '0', 'class' => '', 'wraptag' => '', ), $atts)); $filelink = $thisfile['filename']; if ($filename == "") { $filename = $filelink; } $filename = str_replace(".link", "", $filename) . (($extension=="") ? '' : '.'.$extension); $img = ''; if ($id) { $img = ($thumb==0) ? @image(array("id" => $id, "class" => $class, "wraptag" => $wraptag)) : @thumbnail(array("id" => $id, "class" => $class, "wraptag" => $wraptag)); } else if ((strpos($filename, "http://") === 0) || (strpos($filename, "/") === 0)) { $img = (($wraptag=="") ? '' : '<'.$wraptag. (($class=="") ? '' : ' class="'.$class.'"') .'>') . ''. (($wraptag=="") ? '' : ''); } else { $img = ($thumb==0) ? @image(array("name" => $filename, "class" => $class, "wraptag" => $wraptag)) : @thumbnail(array("name" => $filename, "class" => $class, "wraptag" => $wraptag)); } $wrapper = (($wraptag=="") ? '@@REPL' : '<'.$wraptag. (($class=="") ? '' : ' class="'.$class.'"') .'>@@REPL'); if (strpos($ifmissing, "?ref") === 0) { $display = ($id) ? $id : $filename; $missing = str_replace("@@REPL", $display, $wrapper); } else if (strpos($ifmissing, "?image") === 0) { $imgParts = explode(":", $ifmissing); if (count($imgParts) == 2) { $imgOpts = array(); if(is_numeric($imgParts[1])) { $imgOpts["id"] = $imgParts[1]; } else { $imgOpts["name"] = $imgParts[1]; } $missing = str_replace("@@REPL", (($thumb==0) ? @image($imgOpts) : @thumbnail($imgOpts)), $wrapper); } else { $missing = ""; } } else if ($ifmissing == "") { $missing = ""; } else { $missing = str_replace("@@REPL", $ifmissing, $wrapper); } return ($img) ? $img : $missing; } // Generic callback which is fired _after_ the Files page has loaded. // Performs display cleanup/insertion and post processing of any file inserts/edits function smd_remote_file($event, $step) { global $smd_remote_file_vars; $helpLink = '?'; if ($step == "file_edit" || $step == "file_replace") { $id = assert_int(gps('id')); $rs = safe_row('filename','txp_file','id = '. $id); if (strpos($rs['filename'], ".link") !== false) { $ul_form = trim(form(eInput('file'). sInput('smd_remote_file_replace'). hInput('id',$id). graf('Add URL'.sp.$helpLink.sp.fInput('text', 'smd_remote_url', '', 'edit', 'Enter a URL to add to this file', '', '32').sp. "Overwrite:".sp.checkbox('smd_remote_blat', 'smd_remote_blat', 0).sp. fInput('submit', '', 'Upload', 'smallerbox')) , 'text-align: center;')); echo smd_remote_js('$(".upload-form").replaceWith(\''.$ul_form.'\');'); } } else { $ul_form = trim(form(eInput('file'). sInput('smd_remote_file_create'). graf('or URL'.sp.$helpLink.sp.fInput('text', 'smd_remote_url', '', 'edit', 'Enter a URL to upload to TXP', '', '32').sp. fInput('submit', '', 'Upload', 'smallerbox')) , 'text-align: center;')); echo smd_remote_js('$(".upload-form").append(\''.$ul_form.'\');'); } if ($smd_remote_file_vars['msg'] != "") { echo smd_remote_js('$("#nav-primary table td:eq(0)").append(\''.$smd_remote_file_vars['msg'].'\');'); } } // When a file is "replaced" with a new URL the existing file name must remain // unchanged. By default the new URL is appended to the .link file unless the 'overwrite' // checkbox is set function smd_remote_file_replace() { global $file_base_path, $smd_remote_file_vars; extract(gpsa(array('smd_remote_url', 'smd_remote_blat', 'id'))); $id = assert_int($id); $url = trim($smd_remote_url); $rs = safe_row('filename','txp_file','id = '.$id); if (strpos($rs['filename'], ".link") !== false) { if (strpos($url, "http") === 0) { $writeMode = ($smd_remote_blat) ? "w" : "a"; $dest_filepath = build_file_path($file_base_path, $rs['filename']); smd_remote_file_write($dest_filepath, $rs['filename'], $url, $writeMode); } else { $smd_remote_file_vars['msg'] = gTxt('file_upload_failed') . ((empty($smd_remote_url)) ? ' - '.gTxt('upload_err_no_file') : ''); } } } // Every time a file is saved/edited, TXP recalculates its size from the real file (grrr). // This is undesirable so it is replaced with the size of the remote URL file instead. function smd_remote_file_update() { extract(gpsa(array('id'))); smd_remote_set_size($id); } // Sets the size of the given TXP database file to that of its corresponding "real" remote URL file size function smd_remote_set_size($id_or_file) { global $file_base_path; if (is_numeric($id_or_file)) { $filename = trim(safe_field("filename", "txp_file", "id=".intval($id_or_file))); } else { $filename = trim(doSlash($id_or_file)); } if (strpos($filename, ".link")) { $filepath = build_file_path($file_base_path, $filename); $url = smd_remote_file_list($filepath, 1, 1); if (count($url) > 0) { $hdrs = smd_get_headers($url[0], 1); $size = ($hdrs === false || !isset($hdrs['content-length'])) ? 1 : $hdrs['content-length']; safe_update("txp_file", "size=".$size, "filename='".$filename."'"); } } } // Callback for uploading a URL from the Files tab function smd_remote_file_create() { global $file_base_path, $smd_remote_file_vars; extract(doSlash(gpsa(array('smd_remote_url','category','permissions','description')))); $url = trim($smd_remote_url); // Only intercept remote files; leave everything else for TXP to manage if (strpos($url, "http") === 0) { $hdrs = smd_get_headers($url, 1); $size = ($hdrs === false || !isset($hdrs['content-length'])) ? 1 : $hdrs['content-length']; // Make a filename and full path: unencoded $dest_filename = basename(urldecode($url)).".link"; $dest_filepath = build_file_path($file_base_path, $dest_filename); if (file_exists($dest_filepath)) { smd_remote_file_write($dest_filepath, $dest_filename, $url); } else { // File doesn't exist so create it and put the URL inside $tmp = tempnam("/tmp", "smd_"); $handle = fopen($tmp, "w"); fwrite($handle, $url.n); fclose($handle); rename($tmp, $dest_filepath); // Add the file to TXP $ret = safe_insert("txp_file", "filename = '$dest_filename', category = '$category', permissions = '$permissions', description = '$description', size = '$size', created = now(), modified = now() "); $smd_remote_file_vars['msg'] = gTxt('file_uploaded', array('{name}' => urldecode($url))); } } else { $smd_remote_file_vars['msg'] = gTxt('file_upload_failed') . ((empty($smd_remote_url)) ? ' - '.gTxt('upload_err_no_file') : ''); } } // Append a URL to the given file. If writeMode="w" the whole file is replaced function smd_remote_file_write($filepath, $filename, $url, $writeMode="a") { global $smd_remote_file_vars; // Read the whole file because we only want to add the URL if it's not there already $lines = smd_remote_file_list($filepath, 0, 1); if (!in_array($url, $lines) || $writeMode == "w") { $handle = fopen($filepath, $writeMode); fwrite($handle, $url.n); fclose($handle); $smd_remote_file_vars['msg'] = gTxt('file_uploaded', array('{name}' => urldecode($url))); } else { $smd_remote_file_vars['msg'] = gTxt('file_already-exists', array('{name}' => urldecode($url))); } // Set the size just in case smd_remote_set_size($filename); } // Read the contents of the chosen file (full path required) and get lines from within, adding them to an array. // $offset is where to start from. Normally 1 = 1st row, 2 = 2nd row and so on. 0 = a random row. // $qty specifies how many rows to pull. 0 = unlimited. function smd_remote_file_list($fname, $qty=1, $offset=0) { $out = array(); if (file_exists($fname)) { $fd = fopen($fname, "r"); // Read the whole file in (yes there's the file() call, but fgets() is supposedly quicker on txt files) while (!feof($fd)) { $line = rtrim(fgets($fd)); if ($line != "") { $lines[] = $line; } } fclose ($fd); if ($offset == 0) { shuffle($lines); $offset = 1; } $offset = ($offset > count($lines)) ? 1 : $offset; $out = ($qty == 0) ? $lines : array_slice($lines, $offset-1, $qty); } return $out; } // Called just before a download is initiated function smd_remote_download($event, $step) { global $pretext, $id, $file_base_path, $file_error; if (!isset($file_error)) { // Get the "true" filename (unfortunately from the database = 1 extra query). $real_file = safe_row("filename, size", "txp_file", "id=".intval($id)); if (strpos($real_file['filename'], ".link") > 0) { $choose = 0; // Get any overriding value of smd_choose from the query string if ($pretext['qs']) { list($qkey, $qval) = explode("=", $pretext['qs']); if ($qkey == "smd_choose") { if ($qval > 0) { $choose = intval($qval); } } } // The file size, however, is that of the remote file $remoteURL = smd_remote_file_list(build_file_path($file_base_path, $real_file['filename']), 1, $choose); if (count($remoteURL) > 0) { $url = $remoteURL[0]; // Test the file exists: slow, but reduces false download count increments $hdrs = smd_get_headers($url, 1); $allkey = strtolower(implode(" ", array_keys($hdrs))); if (strpos($allkey, "200") > 0 && strpos($allkey, "ok") > 0) { header('Content-Description: File Download'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($real_file['filename']) . '"; size = "'.$real_file['size'].'"'); // Fix for lame IE 6 pdf bug on servers configured to send cache headers header('Cache-Control: private'); @ini_set("zlib.output_compression", "Off"); @set_time_limit(0); @ignore_user_abort(true); // Hand-off to the remote file header('Location: ' . $url); // record download if the file sizes match if (intval($hdrs['content-length']) == intval($real_file['size'])) { safe_update("txp_file", "downloads=downloads+1", 'id='.intval($id)); log_hit('200'); } // remote download done: game over exit(0); } else { $file_error = 404; } } else { $file_error = 404; } } } // remote download not done - leave to TXP to handle error or "local" file download return; } function smd_remote_js($content) { $out = ''.n; return $out; } // PHP4 emulation(ish) of PHP5's get_headers(). Stolen from php.net and modded function smd_get_headers($url, $format=0) { if (!$url) { return false; } $uinfo=parse_url($url); $headers = array(); if (is_callable('checkdnsrr') && !checkdnsrr($uinfo['host'].'.','MX') && !checkdnsrr($uinfo['host'].'.','A')) { return false; } $port = isset($uinfo['port']) ? $uinfo['port'] : 80; $fp=fsockopen($uinfo['host'], $port, $errno, $errstr, 5); if($fp) { $head = "GET ".@$uinfo['path']."?".@$uinfo['query']." HTTP/1.0\r\n"; $head .= "Host: ".@$uinfo['host'].":".$port."\r\n"; $head .= "Connection: Close\r\n"; $head .= "Accept: */*\r\n"; if (@$uinfo['user']) { $head .= "Authorization: Basic ".base64_encode($uinfo['user'].':'.$uinfo['pass'])."\r\n"; } $head .= "\r\n"; fputs($fp, $head); $eoheader = false; while(!feof($fp) or ($eoheader==true)) { if($header=fgets($fp, 1024)) { if ($header == "\r\n") { $eoheader = true; break; } else { $header = trim($header); } if($format == 1) { $key = strtolower(array_shift(explode(': ',$header))); if($key == $header) { $headers[] = $header; } else { $headers[$key]=substr($header,strlen($key)+2); } unset($key); } else { $headers[] = $header; } } } return $headers; } else { return false; } }