1<?php 2 3 4/** 5 * Frontend for retrieving images and files attached to a database record or 6 * available in an editor session. 7 * 8 * This file is part of DokuWiki plugin database2 and is available under 9 * GPL version 2. See the following URL for a copy of this license! 10 * 11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 12 * 13 * 14 * @author Thomas Urban <soletan@nihilum.de> 15 * @version 0.2 16 * @copyright GPLv2 17 * 18 */ 19 20 21/* 22 * initialize DokuWiki 23 */ 24 25if ( !defined( 'DOKU_INC' ) ) 26{ 27 28 foreach ( array( dirname( __FILE__ ), $_SERVER['SCRIPT_FILENAME'] ) as $path ) 29 { 30 31 while ( $path != '/' ) 32 if ( is_file( $path . '/doku.php' ) && is_dir( $path . '/lib' ) ) 33 break; 34 else 35 $path = dirname( $path ); 36 37 if ( $path != '/' ) 38 break; 39 40 } 41 42 if ( $path && ( $path != '/' ) ) 43 define( 'DOKU_INC', $path . '/' ); 44 else 45 die( "Failed to locate DokuWiki installation folder!"); 46 47} 48 49 50ob_start(); // capture and flush all initial output of DokuWiki 51 52include_once( DOKU_INC . 'inc/init.php' ); 53include_once( DOKU_INC . 'inc/common.php' ); 54include_once( DOKU_INC . 'inc/events.php' ); 55include_once( DOKU_INC . 'inc/pageutils.php' ); 56include_once( DOKU_INC . 'inc/html.php' ); 57include_once( DOKU_INC . 'inc/auth.php' ); 58include_once( DOKU_INC . 'inc/actions.php' ); 59include_once( DOKU_INC . 'lib/plugin.php' ); 60 61ob_end_clean(); 62 63 64 65 66try 67{ 68 69 if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) 70 throw new Exception( 'invalid request method', 400 ); 71 72 73 @session_start(); 74 75 76 if ( $_GET['s'] ) 77 { 78 // read media from session 79 80 $source = @unserialize( @base64_decode( $_GET['s'] ) ); 81 if ( !$source ) 82 throw new Exception( 'invalid request', 400 ); 83 84 85 list( $pageID, $ioIndex, $column ) = $source; 86 87 $session = $_SESSION['database2'][$pageID]['tables'][$ioIndex]['editors'][$column]; 88 if ( !is_array( $session ) ) 89 throw new Exception( 'no such media', 404 ); 90 91 $data = $session['file']; 92 $mime = $session['mime']; 93 $name = $session['name']; 94 95 if ( !$data ) 96 throw new Exception( 'file is empty', 404 ); 97 98 if ( !$name ) 99 $_GET['d'] = false; 100 101 if ( !$mime ) 102 throw new Exception( 'invalid media', 403 ); 103 104 } 105 else 106 { 107 // read media from database 108 109 $source = @unserialize( @gzuncompress( @base64_decode( $_GET['a'] ) ) ); 110 $hash = trim( @base64_decode( $_GET['b'] ) ); 111 112 if ( !is_array( $source ) || ( $hash === '' ) ) 113 throw new Exception( 'invalid request parameter', 400 ); 114 115 list( $dsn, $authSlot, $table, $column, $idColumn, $rowid, $pageID, 116 $ioIndex, $user, $addr ) = $source; 117 118 if ( $addr !== $_SERVER['REMOTE_ADDR'] ) 119 throw new Exception( 'invalid request context', 400 ); 120 121 122 123 $t = sha1( implode( '/', $source ) ); 124 $salt = $_SESSION['database2'][$pageID]['tables'][$ioIndex]['linkedMediaSalts'][$t]; 125 126 if ( !is_string( $salt ) || ( trim( $salt ) === '' ) ) 127 throw new Exception( 'access denied', 403 ); 128 129 130 131 // include Database2 ... 132 $libFile = '/database2.php'; 133 134 // support working with development version if available and 135 // selected to enable development in a production wiki 136 // (as used on wiki.nihilum.de) 137 if ( is_file( dirname( __FILE__ ) . '/database2.dev.php' ) ) 138 if ( $_SESSION['useDevIP'] ) 139 if ( $_SESSION['useDevIP'] == $_SERVER['REMOTE_ADDR'] ) 140 $libFile = '/database2.dev.php'; 141 142 include_once( dirname( __FILE__ ) . $libFile ); 143 144 // ... and derive it to gain access on internals to 145 class Database2_media extends Database2 146 { 147 148 public function __construct( $dsn, $authSlot, $table, $ioIndex, 149 $pageId ) 150 { 151 152 if ( !$this->connect( $dsn, $authSlot ) ) 153 throw new Exception( 'database not available/not found', 404 ); 154 155 $table = trim( $table ); 156 if ( !self::isValidName( $table ) ) 157 throw new Exception( 'invalid media selector (table)', 400 ); 158 159 $this->table = $table; 160 $this->ioIndex = $ioIndex; 161 162 $this->explicitPageID = $pageId; 163 164 } 165 166 final public function getMedia( $column, $idColumn, $rowid ) 167 { 168 169 $column = trim( $column ); 170 $idColumn = trim( $idColumn ); 171 $rowid = intval( $rowid ); 172 173 if ( !self::isValidName( $column ) || 174 !self::isValidName( $idColumn ) || !$rowid ) 175 throw new Exception( 'invalid media selector', 400 ); 176 177 178 $st = $this->getLink()->prepare( 'SELECT ' . $column . 179 ' FROM ' . $this->table . 180 ' WHERE ' . $idColumn . '=?' ); 181 if ( !$st ) 182 throw new Exception( 'failed to prepare retrieval', 500 ); 183 184 if ( !$st->execute( array( $rowid ) ) ) 185 throw new Exception( 'failed to retrieve', 500 ); 186 187 188 $media = $st->fetch( PDO::FETCH_NUM ); 189 if ( !is_array( $media ) ) 190 throw new Exception( 'no such media', 404 ); 191 192 $st->closeCursor(); 193 194 return $media[0]; 195 196 } 197 198 final public function printTable() 199 { 200 return $this->showTable( false, true, true, null, true ); 201 } 202 } 203 204 205 206 207 // check salted hash provided in URL (used to proove authorization) 208 $providedHash = @base64_decode( $_GET['b'] ); 209 if ( !$providedHash ) 210 throw new Exception( 'access denied, missing valid hash', 403 ); 211 212 $source = serialize( $source ); 213 if ( Database2::ssha( $source, $salt ) !== $providedHash ) 214 throw new Exception( 'access denied, invalid hash', 403 ); 215 216 217 218 // query database for selected media file 219 $db = new Database2_media( $dsn, $authSlot, $table, $ioIndex, $pageID ); 220 221 switch ( $_GET['m'] ) 222 { 223 224 case 'print' : 225 $data = $db->printTable(); 226 227 $title = sprintf( $db->getLang( 'printtitle' ), $table ); 228 229 $data = <<<EOT 230<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 231<html xmlns="http://www.w3.org/1999/xhtml"> 232<head> 233<title>$title</title> 234<link rel="stylesheet" type="text/css" href="./print.css" /> 235</head> 236<body class="print" onload="print()"> 237$data 238</body> 239</html> 240EOT; 241 242 // add meta information for proper download 243 $mime = 'text/html; charset=utf-8'; 244 $name = $table . '_print_' . date( 'Y-m-d_H-i' ) . '.html'; 245 246 break; 247 248 case 'xml' : 249 case 'csv' : 250 251 // get list of all columns not containing binary data 252 $meta = $db->getColumnsMeta(); 253 $cols = array(); 254 foreach ( $meta as $column => $def ) 255 if ( $def['isColumn'] && ( $def['type'] != 'data' ) ) 256 $cols[$column] = $column; 257 258 // retrieve records 259 $rows = $db->__recordsList( $cols ); 260 261 // extract separate header row 262 $header = array_shift( array_slice( $rows, 0, 1 ) ); 263 foreach ( $header as $column => $data ) 264 $header[$column] = $meta[$column]['label'] ? $meta[$column]['label'] : $column; 265 266 // write header row to CSV 267 $data = $db->__csvLine( $header ); 268 269 // write all fetched rows to CSV 270 foreach ( $rows as $row ) 271 $data .= $db->__csvLine( $row ); 272 273 274 // add meta information for proper download 275 $mime = 'text/csv; charset=utf-8'; 276 $name = $table . '_export_' . date( 'Y-m-d_H-i' ) . '.csv'; 277 278 break; 279 280 case 'log' : 281 282 $st = $db->getLink()->prepare( 'SELECT rowid,action,username,ctime FROM __log WHERE tablename=? ORDER BY ctime DESC' ); 283 if ( !$st ) 284 throw new Exception( 'failed to prepare viewing log', 500 ); 285 286 if ( !$st->execute( array( $table ) ) ) 287 throw new Exception( 'failed to execute viewing log', 500 ); 288 289 290 // retrieve records 291 $rows = $st->fetchAll( PDO::FETCH_ASSOC ); 292 if ( empty( $rows ) ) 293 $data = ''; 294 else 295 { 296 297 // write header row to CSV 298 $data = $db->__csvLine( array_keys( array_shift( array_slice( $rows, 0, 1 ) ) ) ); 299 300 // write all fetched rows to CSV 301 foreach ( $rows as $row ) 302 { 303 304 $row['ctime'] = date( 'r', $row['ctime'] ); 305 306 $data .= $db->__csvLine( $row ); 307 308 } 309 } 310 311 312 // add meta information for proper download 313 $mime = 'text/csv; charset=utf-8'; 314 $name = $table . '_log_' . date( 'Y-m-d_H-i' ) . '.csv'; 315 316 break; 317 318 default : 319 $data = $db->getMedia( $column, $idColumn, $rowid ); 320 321 // strip off MIME and filename embedded in retrieved file 322 $sepPos = strpos( $data, '|' ); 323 if ( !$sepPos || ( $sepPos > 256 ) ) 324 throw new Exception( 'untyped data record', 403 ); 325 326 $mime = substr( $data, 0, $sepPos ); 327 $data = substr( $data, $sepPos + 1 ); 328 329 330 $sepPos = strpos( $data, '|' ); 331 if ( !$sepPos || ( $sepPos > 256 ) ) 332 // missing filename, thus reject to support download 333 $_GET['d'] = false; 334 else 335 $name = substr( $data, 0, $sepPos ); 336 337 $data = substr( $data, $sepPos + 1 ); 338 339 } 340 } 341 342 343 344 345 // provide file ... 346 if ( $_GET['d'] ) 347 // ... for download otionally 348 header( 'Content-Disposition: attachment; filename=' . $name ); 349 350 351 if ( isset( $_GET['thumb'] ) ) 352 { 353 // derive thumbnail from file 354 355 list( $major, $minor ) = explode( '/', $mime ); 356 357 // try to parse file as image first 358 $img = ( $major == 'image' ) ? @imagecreatefromstring( $data ) : false; 359 if ( $img ) 360 { 361 362 // get desired size of thumbnail 363 $temp = ( trim( $_GET['thumb'] ) === '' ) ? '200x150' : $_GET['thumb']; 364 365 list( $width, $height ) = explode( 'x', $temp ); 366 367 $width = intval( $width ) ? intval( $width ) : null; 368 $height = intval( $height ) ? intval( $height ) : null; 369 370 if ( !$width || !$height ) 371 { 372 373 if ( !$width && !$height ) 374 $width = 200; 375 376 $aspect = ( imagesx( $img ) / imagesy( $img ) ); 377 378 if ( $width ) 379 $height = round( $width / $aspect ); 380 else 381 $width = round( $height * $aspect ); 382 383 } 384 385 if ( ( $width < imagesx( $img ) ) || ( $height < imagesy( $img ) ) ) 386 { 387 // scale-down image to thumbnail size 388 389 $dest = imagecreatetruecolor( $width, $height ); 390 391 imagesavealpha( $dest, ( $minor === 'png' ) ); 392 imagealphablending( $dest, ( $minor !== 'png' ) ); 393 394 if ( $minor !== 'png' ) 395 imagecolorallocate( $dest, 255, 255, 255 ); 396 397 imagecopyresampled( $dest, $img, 0, 0, 0, 0, 398 imagesx( $dest ), imagesy( $dest ), 399 imagesx( $img ), imagesy( $img ) ); 400 401 } 402 else 403 // keep original, as it is smaller than requested thumbnail size 404 $dest = $img; 405 406 407 // render thumbnail image 408 ob_start(); 409 410 switch ( $minor ) 411 { 412 413 case 'gif' : 414 imagegif( $dest ); 415 break; 416 417 case 'jpeg' : 418 case 'pjpeg' : 419 case 'jpg' : 420 imagejpeg( $dest ); 421 break; 422 423 default : 424 $mime = 'image/png'; 425 case 'png' : 426 imagepng( $dest ); 427 428 } 429 430 $data = ob_get_clean(); 431 432 } 433 } 434 435 436 437 header( 'Content-Type: ' . $mime ); 438 439 echo $data; 440 441} 442catch ( Exception $e ) 443{ 444 445 header( 'HTTP/1.1 ' . $e->getCode() . ' ' . $e->getMessage() ); 446 447 echo $e->getMessage(); 448 449} 450 451 452?>