| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
1 <?php 2 # MantisBT - A PHP based bugtracking system 3 4 # MantisBT is free software: you can redistribute it and/or modify 5 # it under the terms of the GNU General Public License as published by 6 # the Free Software Foundation, either version 2 of the License, or 7 # (at your option) any later version. 8 # 9 # MantisBT is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with MantisBT. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * File API 19 * 20 * @package CoreAPI 21 * @subpackage FileAPI 22 * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org 23 * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net 24 * @link http://www.mantisbt.org 25 * 26 * @uses access_api.php 27 * @uses authentication_api.php 28 * @uses bug_api.php 29 * @uses config_api.php 30 * @uses constant_inc.php 31 * @uses database_api.php 32 * @uses gpc_api.php 33 * @uses helper_api.php 34 * @uses history_api.php 35 * @uses project_api.php 36 * @uses utility_api.php 37 */ 38 39 require_api( 'access_api.php' ); 40 require_api( 'authentication_api.php' ); 41 require_api( 'bug_api.php' ); 42 require_api( 'config_api.php' ); 43 require_api( 'constant_inc.php' ); 44 require_api( 'database_api.php' ); 45 require_api( 'gpc_api.php' ); 46 require_api( 'helper_api.php' ); 47 require_api( 'history_api.php' ); 48 require_api( 'project_api.php' ); 49 require_api( 'utility_api.php' ); 50 51 $g_cache_file_count = array(); 52 53 # ## File API ### 54 # Gets the filename without the bug id prefix. 55 function file_get_display_name( $p_filename ) { 56 $t_array = explode( '-', $p_filename, 2 ); 57 58 # Check if it's a project document filename (doc-0000000-filename) 59 # or a bug attachment filename (0000000-filename) 60 # for newer filenames, the filename in schema is correct. 61 # This is important to handle filenames with '-'s properly 62 $t_doc_match = '/^' . config_get( 'document_files_prefix' ) . '-\d{7}-/'; 63 $t_name = preg_split( $t_doc_match, $p_filename ); 64 if( isset( $t_name[1] ) ) { 65 return $t_name[1]; 66 } else { 67 $t_bug_match = '/^\d{7}-/'; 68 $t_name = preg_split( $t_bug_match, $p_filename ); 69 if( isset( $t_name[1] ) ) { 70 return $t_name[1]; 71 } else { 72 return $p_filename; 73 } 74 } 75 } 76 77 # Check the number of attachments a bug has (if any) 78 function file_bug_attachment_count( $p_bug_id ) { 79 global $g_cache_file_count; 80 81 $c_bug_id = db_prepare_int( $p_bug_id ); 82 $t_bug_file_table = db_get_table( 'bug_file' ); 83 84 # First check if we have a cache hit 85 if( isset( $g_cache_file_count[$p_bug_id] ) ) { 86 return $g_cache_file_count[$p_bug_id]; 87 } 88 89 # If there is no cache hit, check if there is anything in 90 # the cache. If the cache isn't empty and we didn't have 91 # a hit, then there are not attachments for this bug. 92 if( count( $g_cache_file_count ) > 0 ) { 93 return 0; 94 } 95 96 # Otherwise build the cache and return the attachment count 97 # for the given bug (if any). 98 $query = "SELECT bug_id, COUNT(bug_id) AS attachments 99 FROM $t_bug_file_table 100 GROUP BY bug_id"; 101 $result = db_query_bound( $query ); 102 103 $t_file_count = 0; 104 while( $row = db_fetch_array( $result ) ) { 105 $g_cache_file_count[$row['bug_id']] = $row['attachments']; 106 if( $p_bug_id == $row['bug_id'] ) { 107 $t_file_count = $row['attachments']; 108 } 109 } 110 111 # If no attachments are present, mark the cache to avoid 112 # repeated queries for this. 113 if( count( $g_cache_file_count ) == 0 ) { 114 $g_cache_file_count['_no_files_'] = -1; 115 } 116 117 return $t_file_count; 118 } 119 120 # Check if a specific bug has attachments 121 function file_bug_has_attachments( $p_bug_id ) { 122 if( file_bug_attachment_count( $p_bug_id ) > 0 ) { 123 return true; 124 } else { 125 return false; 126 } 127 } 128 129 # Check if the current user can view attachments for the specified bug. 130 function file_can_view_bug_attachments( $p_bug_id, $p_uploader_user_id = null ) { 131 $t_uploaded_by_me = auth_get_current_user_id() === $p_uploader_user_id; 132 $t_can_view = access_has_bug_level( config_get( 'view_attachments_threshold' ), $p_bug_id ); 133 $t_can_view = $t_can_view || ( $t_uploaded_by_me && config_get( 'allow_view_own_attachments' ) ); 134 return $t_can_view; 135 } 136 137 # Check if the current user can download attachments for the specified bug. 138 function file_can_download_bug_attachments( $p_bug_id, $p_uploader_user_id = null ) { 139 $t_uploaded_by_me = auth_get_current_user_id() === $p_uploader_user_id; 140 $t_can_download = access_has_bug_level( config_get( 'download_attachments_threshold' ), $p_bug_id ); 141 $t_can_download = $t_can_download || ( $t_uploaded_by_me && config_get( 'allow_download_own_attachments' ) ); 142 return $t_can_download; 143 } 144 145 # Check if the current user can delete attachments from the specified bug. 146 function file_can_delete_bug_attachments( $p_bug_id, $p_uploader_user_id = null ) { 147 if( bug_is_readonly( $p_bug_id ) ) { 148 return false; 149 } 150 $t_uploaded_by_me = auth_get_current_user_id() === $p_uploader_user_id; 151 $t_can_delete = access_has_bug_level( config_get( 'delete_attachments_threshold' ), $p_bug_id ); 152 $t_can_delete = $t_can_delete || ( $t_uploaded_by_me && config_get( 'allow_delete_own_attachments' ) ); 153 return $t_can_delete; 154 } 155 156 # Get icon corresponding to the specified filename 157 # returns an associative array with "url" and "alt" text. 158 function file_get_icon_url( $p_display_filename ) { 159 $t_file_type_icons = config_get( 'file_type_icons' ); 160 161 $ext = utf8_strtolower( pathinfo( $p_display_filename, PATHINFO_EXTENSION ) ); 162 if( is_blank( $ext ) || !isset( $t_file_type_icons[$ext] ) ) { 163 $ext = '?'; 164 } 165 166 $t_name = $t_file_type_icons[$ext]; 167 return array( 'url' => config_get( 'icon_path' ) . 'fileicons/' . $t_name, 'alt' => $ext ); 168 } 169 170 /** 171 * Combines a path and a file name making sure that the separator exists. 172 * 173 * @param string $p_path The path. 174 * @param string $p_filename The file name. 175 * 176 * @return The combined full path. 177 */ 178 function file_path_combine( $p_path, $p_filename ) { 179 $t_path = $p_path; 180 if ( utf8_substr( $t_path, -1 ) != '/' && utf8_substr( $t_path, -1 ) != '\\' ) { 181 $t_path .= DIRECTORY_SEPARATOR; 182 } 183 184 $t_path .= $p_filename; 185 186 return $t_path; 187 } 188 189 /** 190 * Nomalizes the disk file path based on the following algorithm: 191 * 1. If disk file exists, then return as is. 192 * 2. If not, and a project path is available, then check with that, if exists return it. 193 * 3. If not, then use default upload path, then check with that, if exists return it. 194 * 4. If disk file doesn't include a path, then return expected path based on project path or default path. 195 * 5. Otherwise return as is. 196 * 197 * @param string $p_diskfile The disk file (full path or just filename). 198 * @param integer The project id - shouldn't be 0 (ALL_PROJECTS). 199 * @return The normalized full path. 200 */ 201 function file_normalize_attachment_path( $p_diskfile, $p_project_id ) { 202 if ( file_exists( $p_diskfile ) ) { 203 return $p_diskfile; 204 } 205 206 $t_basename = basename( $p_diskfile ); 207 208 $t_expected_file_path = ''; 209 210 if ( $p_project_id != ALL_PROJECTS ) { 211 $t_path = project_get_field( $p_project_id, 'file_path' ); 212 if ( !is_blank( $t_path ) ) { 213 $t_diskfile = file_path_combine( $t_path, $t_basename ); 214 215 if ( file_exists( $t_diskfile ) ) { 216 return $t_diskfile; 217 } 218 219 // if we don't find the file, then this is the path we want to return. 220 $t_expected_file_path = $t_diskfile; 221 } 222 } 223 224 $t_path = config_get( 'absolute_path_default_upload_folder' ); 225 if ( !is_blank( $t_path ) ) { 226 $t_diskfile = file_path_combine( $t_path, $t_basename ); 227 228 if ( file_exists( $t_diskfile ) ) { 229 return $t_diskfile; 230 } 231 232 // if the expected path not set to project directory, then set it to default directory. 233 if ( is_blank( $t_expected_file_path ) ) { 234 $t_expected_file_path = $t_diskfile; 235 } 236 } 237 238 // if diskfile doesn't include a path, then use the expected filename. 239 if ( ( strstr( $p_diskfile, DIRECTORY_SEPARATOR ) === false || 240 strstr( $p_diskfile, '\\' ) === false ) && 241 !is_blank( $t_expected_file_path ) ) { 242 return $t_expected_file_path; 243 } 244 245 // otherwise return as is. 246 return $p_diskfile; 247 } 248 249 # -------------------- 250 # Gets an array of attachments that are visible to the currently logged in user. 251 # Each element of the array contains the following: 252 # display_name - The attachment display name (i.e. file name dot extension) 253 # size - The attachment size in bytes. 254 # date_added - The date where the attachment was added. 255 # can_download - true: logged in user has access to download the attachment, false: otherwise. 256 # diskfile - The name of the file on disk. Typically this is a hash without an extension. 257 # download_url - The download URL for the attachment (only set if can_download is true). 258 # exists - Applicable for DISK attachments. true: file exists, otherwise false. 259 # can_delete - The logged in user can delete the attachments. 260 # preview - true: the attachment should be previewable, otherwise false. 261 # type - Can be "image", "text" or empty for other types. 262 # alt - The alternate text to be associated with the icon. 263 # icon - array with icon information, contains 'url' and 'alt' elements. 264 function file_get_visible_attachments( $p_bug_id ) { 265 $t_attachment_rows = bug_get_attachments( $p_bug_id ); 266 $t_visible_attachments = array(); 267 268 $t_attachments_count = count( $t_attachment_rows ); 269 if( $t_attachments_count === 0 ) { 270 return $t_visible_attachments; 271 } 272 273 $t_attachments = array(); 274 275 $t_preview_text_ext = config_get( 'preview_text_extensions' ); 276 $t_preview_image_ext = config_get( 'preview_image_extensions' ); 277 278 $image_previewed = false; 279 for( $i = 0;$i < $t_attachments_count;$i++ ) { 280 $t_row = $t_attachment_rows[$i]; 281 282 if ( !file_can_view_bug_attachments( $p_bug_id, (int)$t_row['user_id'] ) ) { 283 continue; 284 } 285 286 $t_id = $t_row['id']; 287 $t_filename = $t_row['filename']; 288 $t_filesize = $t_row['filesize']; 289 $t_diskfile = file_normalize_attachment_path( $t_row['diskfile'], bug_get_field( $p_bug_id, 'project_id' ) ); 290 $t_date_added = $t_row['date_added']; 291 292 $t_attachment = array(); 293 $t_attachment['id'] = $t_id; 294 $t_attachment['display_name'] = file_get_display_name( $t_filename ); 295 $t_attachment['size'] = $t_filesize; 296 $t_attachment['date_added'] = $t_date_added; 297 $t_attachment['diskfile'] = $t_diskfile; 298 299 $t_attachment['can_download'] = file_can_download_bug_attachments( $p_bug_id, (int)$t_row['user_id'] ); 300 $t_attachment['can_delete'] = file_can_delete_bug_attachments( $p_bug_id, (int)$t_row['user_id'] ); 301 302 if( $t_attachment['can_download'] ) { 303 $t_attachment['download_url'] = "file_download.php?file_id=$t_id&type=bug"; 304 } 305 306 if( $image_previewed ) { 307 $image_previewed = false; 308 } 309 310 $t_attachment['exists'] = config_get( 'file_upload_method' ) != DISK || file_exists( $t_diskfile ); 311 $t_attachment['icon'] = file_get_icon_url( $t_attachment['display_name'] ); 312 313 $t_attachment['preview'] = false; 314 $t_attachment['type'] = ''; 315 316 $t_ext = strtolower( pathinfo( $t_attachment['display_name'], PATHINFO_EXTENSION ) ); 317 $t_attachment['alt'] = $t_ext; 318 319 if ( $t_attachment['exists'] && $t_attachment['can_download'] && $t_filesize != 0 && $t_filesize <= config_get( 'preview_attachments_inline_max_size' ) ) { 320 if ( in_array( $t_ext, $t_preview_text_ext, true ) ) { 321 $t_attachment['preview'] = true; 322 $t_attachment['type'] = 'text'; 323 } else if ( in_array( $t_ext, $t_preview_image_ext, true ) ) { 324 $t_attachment['preview'] = true; 325 $t_attachment['type'] = 'image'; 326 } 327 } 328 329 $t_attachments[] = $t_attachment; 330 } 331 332 return $t_attachments; 333 } 334 335 # delete all files that are associated with the given bug 336 function file_delete_attachments( $p_bug_id ) { 337 $c_bug_id = db_prepare_int( $p_bug_id ); 338 339 $t_bug_file_table = db_get_table( 'bug_file' ); 340 341 $t_method = config_get( 'file_upload_method' ); 342 343 # Delete files from disk 344 $query = "SELECT diskfile, filename 345 FROM $t_bug_file_table 346 WHERE bug_id=" . db_param(); 347 $result = db_query_bound( $query, Array( $c_bug_id ) ); 348 349 $file_count = db_num_rows( $result ); 350 if( 0 == $file_count ) { 351 return true; 352 } 353 354 if(( DISK == $t_method ) || ( FTP == $t_method ) ) { 355 356 # there may be more than one file 357 $ftp = 0; 358 if( FTP == $t_method ) { 359 $ftp = file_ftp_connect(); 360 } 361 362 for( $i = 0;$i < $file_count;$i++ ) { 363 $row = db_fetch_array( $result ); 364 365 $t_local_diskfile = file_normalize_attachment_path( $row['diskfile'], bug_get_field( $p_bug_id, 'project_id' ) ); 366 file_delete_local( $t_local_diskfile ); 367 368 if( FTP == $t_method ) { 369 file_ftp_delete( $ftp, $row['diskfile'] ); 370 } 371 } 372 373 if( FTP == $t_method ) { 374 file_ftp_disconnect( $ftp ); 375 } 376 } 377 378 # Delete the corresponding db records 379 $query = "DELETE FROM $t_bug_file_table 380 WHERE bug_id=" . db_param(); 381 $result = db_query_bound( $query, Array( $c_bug_id ) ); 382 383 # db_query errors on failure so: 384 return true; 385 } 386 387 function file_delete_project_files( $p_project_id ) { 388 $t_project_file_table = db_get_table( 'project_file' ); 389 $t_method = config_get( 'file_upload_method' ); 390 391 # Delete the file physically (if stored via DISK or FTP) 392 if(( DISK == $t_method ) || ( FTP == $t_method ) ) { 393 394 # Delete files from disk 395 $query = "SELECT diskfile, filename 396 FROM $t_project_file_table 397 WHERE project_id=" . db_param(); 398 $result = db_query_bound( $query, array( (int) $p_project_id ) ); 399 400 $file_count = db_num_rows( $result ); 401 402 $ftp = 0; 403 if( FTP == $t_method ) { 404 $ftp = file_ftp_connect(); 405 } 406 407 for( $i = 0;$i < $file_count;$i++ ) { 408 $row = db_fetch_array( $result ); 409 410 $t_local_diskfile = file_normalize_attachment_path( $row['diskfile'], $p_project_id ); 411 file_delete_local( $t_local_diskfile ); 412 413 if( FTP == $t_method ) { 414 file_ftp_delete( $ftp, $row['diskfile'] ); 415 } 416 } 417 418 if( FTP == $t_method ) { 419 file_ftp_disconnect( $ftp ); 420 } 421 } 422 423 # Delete the corresponding db records 424 $query = "DELETE FROM $t_project_file_table 425 WHERE project_id=" . db_param(); 426 $result = db_query_bound( $query, Array( (int) $p_project_id ) ); 427 } 428 429 # Delete all cached files that are older than configured number of days. 430 function file_ftp_cache_cleanup() { 431 } 432 433 # Connect to ftp server using configured server address, user name, and password. 434 function file_ftp_connect() { 435 $conn_id = ftp_connect( config_get( 'file_upload_ftp_server' ) ); 436 $login_result = ftp_login( $conn_id, config_get( 'file_upload_ftp_user' ), config_get( 'file_upload_ftp_pass' ) ); 437 438 if(( !$conn_id ) || ( !$login_result ) ) { 439 trigger_error( ERROR_FTP_CONNECT_ERROR, ERROR ); 440 } 441 442 return $conn_id; 443 } 444 445 # Put a file to the ftp server. 446 function file_ftp_put( $p_conn_id, $p_remote_filename, $p_local_filename ) { 447 helper_begin_long_process(); 448 $upload = ftp_put( $p_conn_id, $p_remote_filename, $p_local_filename, FTP_BINARY ); 449 } 450 451 # Get a file from the ftp server. 452 function file_ftp_get( $p_conn_id, $p_local_filename, $p_remote_filename ) { 453 helper_begin_long_process(); 454 $download = ftp_get( $p_conn_id, $p_local_filename, $p_remote_filename, FTP_BINARY ); 455 } 456 457 # Delete a file from the ftp server 458 function file_ftp_delete( $p_conn_id, $p_filename ) { 459 @ftp_delete( $p_conn_id, $p_filename ); 460 } 461 462 # Disconnect from the ftp server 463 function file_ftp_disconnect( $p_conn_id ) { 464 ftp_quit( $p_conn_id ); 465 } 466 467 # Delete a local file even if it is read-only. 468 function file_delete_local( $p_filename ) { 469 if( file_exists( $p_filename ) ) { 470 chmod( $p_filename, 0775 ); 471 unlink( $p_filename ); 472 } 473 } 474 475 # Return the specified field value 476 function file_get_field( $p_file_id, $p_field_name, $p_table = 'bug' ) { 477 $c_field_name = db_prepare_string( $p_field_name ); 478 $t_bug_file_table = db_get_table( $p_table . '_file' ); 479 480 # get info 481 $query = "SELECT $c_field_name 482 FROM $t_bug_file_table 483 WHERE id=" . db_param(); 484 $result = db_query_bound( $query, Array( (int) $p_file_id ), 1 ); 485 486 return db_result( $result ); 487 } 488 489 function file_delete( $p_file_id, $p_table = 'bug' ) { 490 $t_upload_method = config_get( 'file_upload_method' ); 491 492 $c_file_id = db_prepare_int( $p_file_id ); 493 $t_filename = file_get_field( $p_file_id, 'filename', $p_table ); 494 $t_diskfile = file_get_field( $p_file_id, 'diskfile', $p_table ); 495 496 if ( $p_table == 'bug' ) { 497 $t_bug_id = file_get_field( $p_file_id, 'bug_id', $p_table ); 498 $t_project_id = bug_get_field( $t_bug_id, 'project_id' ); 499 } else { 500 $t_project_id = file_get_field( $p_file_id, 'project_id', $p_table ); 501 } 502 503 if(( DISK == $t_upload_method ) || ( FTP == $t_upload_method ) ) { 504 if( FTP == $t_upload_method ) { 505 $ftp = file_ftp_connect(); 506 file_ftp_delete( $ftp, $t_diskfile ); 507 file_ftp_disconnect( $ftp ); 508 } 509 510 $t_local_disk_file = file_normalize_attachment_path( $t_diskfile, $t_project_id ); 511 if ( file_exists( $t_local_disk_file ) ) { 512 file_delete_local( $t_local_disk_file ); 513 } 514 } 515 516 if( 'bug' == $p_table ) { 517 # log file deletion 518 history_log_event_special( $t_bug_id, FILE_DELETED, file_get_display_name( $t_filename ) ); 519 } 520 521 $t_file_table = db_get_table( $p_table . '_file' ); 522 $query = "DELETE FROM $t_file_table 523 WHERE id=" . db_param(); 524 db_query_bound( $query, Array( $c_file_id ) ); 525 return true; 526 } 527 528 # File type check 529 function file_type_check( $p_file_name ) { 530 $t_allowed_files = config_get( 'allowed_files' ); 531 $t_disallowed_files = config_get( 'disallowed_files' );; 532 533 # grab extension 534 $t_extension = pathinfo( $p_file_name, PATHINFO_EXTENSION ); 535 536 # check against disallowed files 537 $t_disallowed_arr = explode( ',', $t_disallowed_files ); 538 foreach( $t_disallowed_arr as $t_val ) { 539 if( 0 == strcasecmp( $t_val, $t_extension ) ) { 540 return false; 541 } 542 } 543 544 # if the allowed list is note populated then the file must be allowed 545 if( is_blank( $t_allowed_files ) ) { 546 return true; 547 } 548 549 # check against allowed files 550 $t_allowed_arr = explode( ',', $t_allowed_files ); 551 foreach( $t_allowed_arr as $t_val ) { 552 if( 0 == strcasecmp( $t_val, $t_extension ) ) { 553 return true; 554 } 555 } 556 557 return false; 558 } 559 560 # clean file name by removing sensitive characters and replacing them with underscores 561 function file_clean_name( $p_filename ) { 562 return preg_replace( '/[\/*?"<>|\\ :&]/', "_", $p_filename ); 563 } 564 565 # Generate a string to use as the identifier for the file 566 # It is not guaranteed to be unique and should be checked 567 # The string returned should be 32 characters in length 568 function file_generate_name( $p_seed ) { 569 return md5( $p_seed . time() ); 570 } 571 572 # Generate a UNIQUE string to use as the identifier for the file 573 # The string returned should be 64 characters in length 574 function file_generate_unique_name( $p_seed, $p_filepath ) { 575 do { 576 $t_string = file_generate_name( $p_seed ); 577 } 578 while( !diskfile_is_name_unique( $t_string, $p_filepath ) ); 579 580 return $t_string; 581 } 582 583 # Return true if the diskfile name identifier is unique, false otherwise 584 function diskfile_is_name_unique( $p_name, $p_filepath ) { 585 $t_file_table = db_get_table( 'bug_file' ); 586 587 $c_name = $p_filepath . $p_name; 588 589 $query = "SELECT COUNT(*) 590 FROM $t_file_table 591 WHERE diskfile=" . db_param(); 592 $result = db_query_bound( $query, Array( $c_name ) ); 593 $t_count = db_result( $result ); 594 595 if( $t_count > 0 ) { 596 return false; 597 } else { 598 return true; 599 } 600 } 601 602 # Return true if the file name identifier is unique, false otherwise 603 function file_is_name_unique( $p_name, $p_bug_id ) { 604 $t_file_table = db_get_table( 'bug_file' ); 605 606 $query = "SELECT COUNT(*) 607 FROM $t_file_table 608 WHERE filename=" . db_param() . " AND bug_id=" . db_param(); 609 $result = db_query_bound( $query, Array( $p_name, $p_bug_id ) ); 610 $t_count = db_result( $result ); 611 612 if( $t_count > 0 ) { 613 return false; 614 } else { 615 return true; 616 } 617 } 618 619 /** 620 * Add a file to the system using the configured storage method 621 * 622 * @param integer $p_bug_id the bug id 623 * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() 624 * @param string $p_table table ('bug' or 'doc') 625 * @param string $p_title file title 626 * @param string $p_desc file description 627 * @param int $p_user_id user id 628 * @param int $p_date_added date added 629 * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bug attachments) 630 */ 631 function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null, $p_date_added = 0, $p_skip_bug_update = false ) { 632 633 file_ensure_uploaded( $p_file ); 634 $t_file_name = $p_file['name']; 635 $t_tmp_file = $p_file['tmp_name']; 636 $c_date_added = $p_date_added <= 0 ? db_now() : db_prepare_int( $p_date_added ); 637 638 if( !file_type_check( $t_file_name ) ) { 639 trigger_error( ERROR_FILE_NOT_ALLOWED, ERROR ); 640 } 641 642 if( !file_is_name_unique( $t_file_name, $p_bug_id ) ) { 643 trigger_error( ERROR_DUPLICATE_FILE, ERROR ); 644 } 645 646 if( 'bug' == $p_table ) { 647 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 648 $t_bug_id = bug_format_id( $p_bug_id ); 649 } else { 650 $t_project_id = helper_get_current_project(); 651 $t_bug_id = 0; 652 } 653 654 if( $p_user_id === null ) { 655 $c_user_id = auth_get_current_user_id(); 656 } else { 657 $c_user_id = (int)$p_user_id; 658 } 659 660 # prepare variables for insertion 661 $c_bug_id = db_prepare_int( $p_bug_id ); 662 $c_project_id = db_prepare_int( $t_project_id ); 663 $c_file_type = db_prepare_string( $p_file['type'] ); 664 $c_title = db_prepare_string( $p_title ); 665 $c_desc = db_prepare_string( $p_desc ); 666 667 if( $t_project_id == ALL_PROJECTS ) { 668 $t_file_path = config_get( 'absolute_path_default_upload_folder' ); 669 } else { 670 $t_file_path = project_get_field( $t_project_id, 'file_path' ); 671 if( $t_file_path == '' ) { 672 $t_file_path = config_get( 'absolute_path_default_upload_folder' ); 673 } 674 } 675 $c_file_path = db_prepare_string( $t_file_path ); 676 $c_new_file_name = db_prepare_string( $t_file_name ); 677 678 $t_file_hash = ( 'bug' == $p_table ) ? $t_bug_id : config_get( 'document_files_prefix' ) . '-' . $t_project_id; 679 $t_unique_name = file_generate_unique_name( $t_file_hash . '-' . $t_file_name, $t_file_path ); 680 $t_disk_file_name = $t_file_path . $t_unique_name; 681 $c_unique_name = db_prepare_string( $t_unique_name ); 682 683 $t_file_size = filesize( $t_tmp_file ); 684 if( 0 == $t_file_size ) { 685 trigger_error( ERROR_FILE_NO_UPLOAD_FAILURE, ERROR ); 686 } 687 $t_max_file_size = (int) min( ini_get_number( 'upload_max_filesize' ), ini_get_number( 'post_max_size' ), config_get( 'max_file_size' ) ); 688 if( $t_file_size > $t_max_file_size ) { 689 trigger_error( ERROR_FILE_TOO_BIG, ERROR ); 690 } 691 $c_file_size = db_prepare_int( $t_file_size ); 692 693 $t_method = config_get( 'file_upload_method' ); 694 695 switch( $t_method ) { 696 case FTP: 697 case DISK: 698 file_ensure_valid_upload_path( $t_file_path ); 699 700 if( !file_exists( $t_disk_file_name ) ) { 701 if( FTP == $t_method ) { 702 $conn_id = file_ftp_connect(); 703 file_ftp_put( $conn_id, $t_disk_file_name, $t_tmp_file ); 704 file_ftp_disconnect( $conn_id ); 705 } 706 707 if( !move_uploaded_file( $t_tmp_file, $t_disk_file_name ) ) { 708 trigger_error( ERROR_FILE_MOVE_FAILED, ERROR ); 709 } 710 711 chmod( $t_disk_file_name, config_get( 'attachments_file_permissions' ) ); 712 713 $c_content = "''"; 714 } else { 715 trigger_error( ERROR_FILE_DUPLICATE, ERROR ); 716 } 717 break; 718 case DATABASE: 719 $c_content = db_prepare_binary_string( fread( fopen( $t_tmp_file, 'rb' ), $t_file_size ) ); 720 break; 721 default: 722 trigger_error( ERROR_GENERIC, ERROR ); 723 } 724 725 $t_file_table = db_get_table( $p_table . '_file' ); 726 $c_id = ( 'bug' == $p_table ) ? $c_bug_id : $c_project_id; 727 728 $query = "INSERT INTO $t_file_table 729 (" . $p_table . "_id, title, description, diskfile, filename, folder, filesize, file_type, date_added, content, user_id) 730 VALUES 731 ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . $c_date_added . "', $c_content, $c_user_id)"; 732 db_query( $query ); 733 734 if( 'bug' == $p_table ) { 735 736 # updated the last_updated date 737 if ( !$p_skip_bug_update ) { 738 $result = bug_update_date( $p_bug_id ); 739 } 740 741 # log new bug 742 history_log_event_special( $p_bug_id, FILE_ADDED, $t_file_name ); 743 } 744 } 745 746 # -------------------- 747 # Return true if file uploading is enabled (in our config and PHP's), 748 # false otherwise 749 function file_is_uploading_enabled() { 750 if( ini_get_bool( 'file_uploads' ) && ( ON == config_get( 'allow_file_upload' ) ) ) { 751 return true; 752 } else { 753 return false; 754 } 755 } 756 757 # Check if the user can upload files for this project 758 # return true if they can, false otherwise 759 # the project defaults to the current project and the user to the current user 760 function file_allow_project_upload( $p_project_id = null, $p_user_id = null ) { 761 if( null === $p_project_id ) { 762 $p_project_id = helper_get_current_project(); 763 } 764 if( null === $p_user_id ) { 765 $p_user_id = auth_get_current_user_id(); 766 } 767 return( file_is_uploading_enabled() && ( access_has_project_level( config_get( 'upload_project_file_threshold' ), $p_project_id, $p_user_id ) ) ); 768 } 769 770 # -------------------- 771 # Check if the user can upload files for this bug 772 # return true if they can, false otherwise 773 # the user defaults to the current user 774 # 775 # if the bug null (the default) we answer whether the user can 776 # upload a file to a new bug in the current project 777 function file_allow_bug_upload( $p_bug_id = null, $p_user_id = null ) { 778 if( null === $p_user_id ) { 779 $p_user_id = auth_get_current_user_id(); 780 } 781 782 # If uploads are disbled just return false 783 if( !file_is_uploading_enabled() ) { 784 return false; 785 } 786 787 if( null === $p_bug_id ) { 788 789 # new bug 790 $t_project_id = helper_get_current_project(); 791 792 # the user must be the reporter if they're reporting a new bug 793 $t_reporter = true; 794 } else { 795 796 # existing bug 797 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 798 799 # check if the user is the reporter of the bug 800 $t_reporter = bug_is_user_reporter( $p_bug_id, $p_user_id ); 801 } 802 803 # *** If we ever wanted to have a per-project setting enabling file 804 # uploads, we'd want to check it here before exempting the reporter 805 806 if( $t_reporter && ( ON == config_get( 'allow_reporter_upload' ) ) ) { 807 return true; 808 } 809 810 # Check the access level against the config setting 811 return access_has_project_level( config_get( 'upload_bug_file_threshold' ), $t_project_id, $p_user_id ); 812 } 813 814 # -------------------- 815 # checks whether the specified upload path exists and is writable 816 function file_ensure_valid_upload_path( $p_upload_path ) { 817 if( !file_exists( $p_upload_path ) || !is_dir( $p_upload_path ) || !is_writable( $p_upload_path ) || !is_readable( $p_upload_path ) ) { 818 trigger_error( ERROR_FILE_INVALID_UPLOAD_PATH, ERROR ); 819 } 820 } 821 822 /** 823 * Ensure a file was uploaded 824 * 825 * This function perform various checks for determining if the upload 826 * was successful 827 * 828 * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() 829 */ 830 function file_ensure_uploaded( $p_file ) { 831 switch( $p_file['error'] ) { 832 case UPLOAD_ERR_INI_SIZE: 833 case UPLOAD_ERR_FORM_SIZE: 834 trigger_error( ERROR_FILE_TOO_BIG, ERROR ); 835 break; 836 case UPLOAD_ERR_PARTIAL: 837 case UPLOAD_ERR_NO_FILE: 838 trigger_error( ERROR_FILE_NO_UPLOAD_FAILURE, ERROR ); 839 break; 840 default: 841 break; 842 } 843 844 if(( '' == $p_file['tmp_name'] ) || ( '' == $p_file['name'] ) ) { 845 trigger_error( ERROR_FILE_NO_UPLOAD_FAILURE, ERROR ); 846 } 847 if( !is_readable( $p_file['tmp_name'] ) ) { 848 trigger_error( ERROR_UPLOAD_FAILURE, ERROR ); 849 } 850 } 851 852 /** 853 * Get file content 854 * 855 * @param int $p_file_id file id 856 * @param string $p_type file type (either 'bug' or 'doc') 857 * @return array|bool array containing file type and content or false on failure to retrieve file 858 */ 859 function file_get_content( $p_file_id, $p_type = 'bug' ) { 860 # we handle the case where the file is attached to a bug 861 # or attached to a project as a project doc. 862 $query = ''; 863 switch ( $p_type ) { 864 case 'bug': 865 $t_bug_file_table = db_get_table( 'bug_file' ); 866 $query = "SELECT * 867 FROM $t_bug_file_table 868 WHERE id=" . db_param(); 869 break; 870 case 'doc': 871 $t_project_file_table = db_get_table( 'project_file' ); 872 $query = "SELECT * 873 FROM $t_project_file_table 874 WHERE id=" . db_param(); 875 break; 876 default: 877 return false; 878 } 879 $result = db_query_bound( $query, Array( $p_file_id ) ); 880 $row = db_fetch_array( $result ); 881 882 if ( $f_type == 'bug' ) { 883 $t_project_id = bug_get_field( $row['bug_id'], 'project_id' ); 884 } else { 885 $t_project_id = $row['bug_id']; 886 } 887 888 # If finfo is available (always true for PHP >= 5.3.0) we can use it to determine the MIME type of files 889 $finfo_available = false; 890 if ( class_exists( 'finfo' ) ) { 891 $t_info_file = config_get( 'fileinfo_magic_db_file' ); 892 893 if ( is_blank( $t_info_file ) ) { 894 $finfo = new finfo( FILEINFO_MIME ); 895 } else { 896 $finfo = new finfo( FILEINFO_MIME, $t_info_file ); 897 } 898 899 if ( $finfo ) { 900 $finfo_available = true; 901 } 902 } 903 904 $t_content_type = $row['file_type']; 905 906 switch ( config_get( 'file_upload_method' ) ) { 907 case DISK: 908 $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); 909 910 if ( file_exists( $t_local_disk_file ) ) { 911 if ( $finfo_available ) { 912 $t_file_info_type = $finfo->file( $t_local_disk_file ); 913 914 if ( $t_file_info_type !== false ) { 915 $t_content_type = $t_file_info_type; 916 } 917 } 918 return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); 919 } 920 break; 921 case FTP: 922 $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); 923 924 if ( !file_exists( $t_local_disk_file ) ) { 925 $ftp = file_ftp_connect(); 926 file_ftp_get ( $ftp, $t_local_disk_file, $row['diskfile'] ); 927 file_ftp_disconnect( $ftp ); 928 } 929 930 if ( $finfo_available ) { 931 $t_file_info_type = $finfo->file( $t_local_disk_file ); 932 933 if ( $t_file_info_type !== false ) { 934 $t_content_type = $t_file_info_type; 935 } 936 } 937 return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); 938 break; 939 default: 940 if ( $finfo_available ) { 941 $t_file_info_type = $finfo->buffer( $row['content'] ); 942 943 if ( $t_file_info_type !== false ) { 944 $t_content_type = $t_file_info_type; 945 } 946 } 947 return array( 'type' => $t_content_type, 'content' => $row['content'] ); 948 break; 949 } 950 } 951 952 /** 953 * Move any attachments as needed when a bug is moved from project to project. 954 * 955 * @param int $p_bug_id ID of bug containing attachments to be moved 956 * @param int $p_project_id_to destination project ID for the bug 957 * @return null 958 */ 959 function file_move_bug_attachments( $p_bug_id, $p_project_id_to ) { 960 $t_project_id_from = bug_get_field( $p_bug_id, 'project_id' ); 961 if ( $t_project_id_from == $p_project_id_to ) { 962 return; 963 } 964 965 $t_method = config_get( 'file_upload_method' ); 966 if ( $t_method != DISK ) { 967 return; 968 } 969 970 if ( !file_bug_has_attachments( $p_bug_id ) ) { 971 return; 972 } 973 974 $t_path_from = project_get_field( $t_project_id_from, 'file_path' ); 975 if ( is_blank( $t_path_from ) ) { 976 $t_path_from = config_get( 'absolute_path_default_upload_folder', null, null, $t_project_id_from ); 977 } 978 file_ensure_valid_upload_path( $t_path_from ); 979 $t_path_to = project_get_field( $p_project_id_to, 'file_path' ); 980 if ( is_blank( $t_path_to ) ) { 981 $t_path_to = config_get( 'absolute_path_default_upload_folder', null, null, $p_project_id_to ); 982 } 983 file_ensure_valid_upload_path( $t_path_to ); 984 if ( $t_path_from == $t_path_to ) { 985 return; 986 } 987 988 # Initialize the update query to update a single row 989 $t_bug_file_table = db_get_table( 'bug_file' ); 990 $c_bug_id = db_prepare_int( $p_bug_id ); 991 $query_disk_attachment_update = "UPDATE $t_bug_file_table 992 SET folder=" . db_param() . " 993 WHERE bug_id=" . db_param() . " 994 AND id =" . db_param(); 995 996 $t_attachment_rows = bug_get_attachments( $p_bug_id ); 997 $t_attachments_count = count( $t_attachment_rows ); 998 for ( $i = 0; $i < $t_attachments_count; $i++ ) { 999 $t_row = $t_attachment_rows[$i]; 1000 $t_basename = basename( $t_row['diskfile'] ); 1001 1002 $t_disk_file_name_from = file_path_combine( $t_path_from, $t_basename ); 1003 $t_disk_file_name_to = file_path_combine( $t_path_to, $t_basename ); 1004 1005 if ( !file_exists( $t_disk_file_name_to ) ) { 1006 chmod( $t_disk_file_name_from, 0775 ); 1007 if ( !rename( $t_disk_file_name_from, $t_disk_file_name_to ) ) { 1008 if ( !copy( $t_disk_file_name_from, $t_disk_file_name_to ) ) { 1009 trigger_error( FILE_MOVE_FAILED, ERROR ); 1010 } 1011 file_delete_local( $t_disk_file_name_from ); 1012 } 1013 chmod( $t_disk_file_name_to, config_get( 'attachments_file_permissions' ) ); 1014 db_query_bound( $query_disk_attachment_update, Array( db_prepare_string( $t_path_to ), $c_bug_id, db_prepare_int( $t_row['id'] ) ) ); 1015 } else { 1016 trigger_error( ERROR_FILE_DUPLICATE, ERROR ); 1017 } 1018 } 1019 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |