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 * A webservice interface to Mantis Bug Tracker 19 * 20 * @package MantisBT 21 * @copyright Copyright 2004 Victor Boctor - vboctor@users.sourceforge.net 22 * @copyright Copyright 2005 MantisBT Team - mantisbt-dev@lists.sourceforge.net 23 * @link http://www.mantisbt.org 24 */ 25 26/** 27 * Check if the current user can download attachments for the specified bug. 28 * @param integer $p_bug_id A bug identifier. 29 * @param integer $p_user_id A user identifier. 30 * @return boolean 31 */ 32function mci_file_can_download_bug_attachments( $p_bug_id, $p_user_id ) { 33 $t_can_download = access_has_bug_level( config_get( 'download_attachments_threshold' ), $p_bug_id ); 34 if( $t_can_download ) { 35 return true; 36 } 37 38 $t_reported_by_me = bug_is_user_reporter( $p_bug_id, $p_user_id ); 39 return( $t_reported_by_me && config_get( 'allow_download_own_attachments' ) ); 40} 41 42/** 43 * Read a local file and return its content. 44 * @param string $p_diskfile Name of file on disk. 45 * @return string 46 */ 47function mci_file_read_local( $p_diskfile ) { 48 $t_handle = fopen( $p_diskfile, 'r' ); 49 $t_content = fread( $t_handle, filesize( $p_diskfile ) ); 50 fclose( $t_handle ); 51 return $t_content; 52} 53 54/** 55 * Write a local file. 56 * @param string $p_diskfile Name of file on disk. 57 * @param string $p_content File content to write. 58 * @return void 59 */ 60function mci_file_write_local( $p_diskfile, $p_content ) { 61 $t_handle = fopen( $p_diskfile, 'w' ); 62 fwrite( $t_handle, $p_content ); 63 fclose( $t_handle ); 64} 65 66/** 67 * Add a file 68 * @param integer $p_id File id. 69 * @param string $p_name File name. 70 * @param string $p_content File content to write. 71 * @param string $p_file_type File type. 72 * @param string $p_table Database table name. 73 * @param string $p_title Title. 74 * @param string $p_desc Description. 75 * @param string $p_user_id User id. 76 * @return mixed 77 */ 78function mci_file_add( $p_id, $p_name, $p_content, $p_file_type, $p_table, $p_title = '', $p_desc = '', $p_user_id = null ) { 79 if( !file_type_check( $p_name ) ) { 80 return ApiObjectFactory::faultForbidden( 'File type not allowed.' ); 81 } 82 83 if( !file_is_name_unique( $p_name, $p_id ) ) { 84 return ApiObjectFactory::faultConflict( 'Duplicate filename.' ); 85 } 86 87 $t_file_size = strlen( $p_content ); 88 $t_max_file_size = file_get_max_file_size(); 89 if( $t_file_size > $t_max_file_size ) { 90 return ApiObjectFactory::faultBadRequest( 'File is too big. Max size is "' . $t_max_file_size . '" bytes.' ); 91 } 92 93 if( 'bug' == $p_table ) { 94 $t_project_id = bug_get_field( $p_id, 'project_id' ); 95 $t_id = (int)$p_id; 96 $t_issue_id = bug_format_id( $p_id ); 97 } else { 98 $t_project_id = $p_id; 99 $t_id = $t_project_id; 100 $t_issue_id = 0; 101 } 102 103 if( $p_user_id === null ) { 104 $p_user_id = auth_get_current_user_id(); 105 } 106 107 if( $t_project_id == ALL_PROJECTS ) { 108 $t_file_path = config_get_global( 'absolute_path_default_upload_folder' ); 109 } else { 110 $t_file_path = project_get_field( $t_project_id, 'file_path' ); 111 if( is_blank( $t_file_path ) ) { 112 $t_file_path = config_get_global( 'absolute_path_default_upload_folder' ); 113 } 114 } 115 116 $t_unique_name = file_generate_unique_name( $t_file_path ); 117 $t_disk_file_name = $t_file_path . $t_unique_name; 118 119 $t_method = config_get( 'file_upload_method' ); 120 121 switch( $t_method ) { 122 case DISK: 123 if( !file_exists( $t_file_path ) || !is_dir( $t_file_path ) || !is_writable( $t_file_path ) || !is_readable( $t_file_path ) ) { 124 return ApiObjectFactory::faultServerError( "Upload folder doesn't exist." ); 125 } 126 127 file_ensure_valid_upload_path( $t_file_path ); 128 129 if( !file_exists( $t_disk_file_name ) ) { 130 mci_file_write_local( $t_disk_file_name, $p_content ); 131 chmod( $t_disk_file_name, config_get( 'attachments_file_permissions' ) ); 132 $c_content = "''"; 133 } 134 break; 135 case DATABASE: 136 $c_content = db_prepare_binary_string( $p_content ); 137 $t_file_path = ''; 138 break; 139 } 140 141 $t_file_table = db_get_table( $p_table . '_file' ); 142 $t_id_col = $p_table . '_id'; 143 144 $t_param = array( 145 $t_id_col => $t_id, 146 'title' => $p_title, 147 'description' => $p_desc, 148 'diskfile' => $t_unique_name, 149 'filename' => $p_name, 150 'folder' => $t_file_path, 151 'filesize' => $t_file_size, 152 'file_type' => $p_file_type, 153 'date_added' => db_now(), 154 'user_id' => (int)$p_user_id, 155 ); 156 # Oracle has to update BLOBs separately 157 if( !db_is_oracle() ) { 158 $t_param['content'] = $c_content; 159 } 160 $t_query_param = db_param(); 161 for( $i = 1; $i < count( $t_param ); $i++ ) { 162 $t_query_param .= ', ' . db_param(); 163 } 164 165 $t_query = 'INSERT INTO ' . $t_file_table . ' 166 ( ' . implode( ', ', array_keys( $t_param ) ) . ' ) 167 VALUES 168 ( ' . $t_query_param . ' )'; 169 db_query( $t_query, array_values( $t_param ) ); 170 171 # get attachment id 172 $t_attachment_id = db_insert_id( $t_file_table ); 173 174 if( db_is_oracle() ) { 175 db_update_blob( $t_file_table, 'content', $c_content, "diskfile='$t_unique_name'" ); 176 } 177 178 if( 'bug' == $p_table ) { 179 # bump the last_updated date 180 bug_update_date( $t_issue_id ); 181 182 # add history entry 183 history_log_event_special( $t_issue_id, FILE_ADDED, $p_name ); 184 } 185 186 return $t_attachment_id; 187} 188 189/** 190 * Returns the attachment contents 191 * 192 * @param integer $p_file_id File identifier. 193 * @param string $p_type The file type, bug or doc. 194 * @param integer $p_user_id A valid user identifier. 195 * @return string|soap_fault the string contents, or a soap_fault 196 */ 197function mci_file_get( $p_file_id, $p_type, $p_user_id ) { 198 # we handle the case where the file is attached to a bug 199 # or attached to a project as a project doc. 200 $t_query = ''; 201 switch( $p_type ) { 202 case 'bug': 203 $t_query = 'SELECT * FROM {bug_file} WHERE id=' . db_param(); 204 break; 205 case 'doc': 206 $t_query = 'SELECT * FROM {project_file} WHERE id=' . db_param(); 207 break; 208 default: 209 return ApiObjectFactory::faultServerError( 'Invalid file type '. $p_type . ' .' ); 210 } 211 212 $t_result = db_query( $t_query, array( $p_file_id ) ); 213 214 if( $t_result->EOF ) { 215 return ApiObjectFactory::faultNotFound( 'Unable to find an attachment with type ' . $p_type . ' and id ' . 216 $p_file_id . '.' ); 217 } 218 219 $t_row = db_fetch_array( $t_result ); 220 221 if( $p_type == 'doc' ) { 222 $t_project_id = $t_row['project_id']; 223 } else if( $p_type == 'bug' ) { 224 $t_bug_id = $t_row['bug_id']; 225 $t_project_id = bug_get_field( $t_bug_id, 'project_id' ); 226 } 227 228 $t_diskfile = file_normalize_attachment_path( $t_row['diskfile'], $t_project_id ); 229 $t_content = $t_row['content']; 230 231 # Check access rights 232 switch( $p_type ) { 233 case 'bug': 234 if( !mci_file_can_download_bug_attachments( $t_bug_id, $p_user_id ) ) { 235 return mci_fault_access_denied( $p_user_id ); 236 } 237 break; 238 case 'doc': 239 # Check if project documentation feature is enabled. 240 if( OFF == config_get( 'enable_project_documentation' ) ) { 241 return mci_fault_access_denied( $p_user_id ); 242 } 243 if( !access_has_project_level( config_get( 'view_proj_doc_threshold' ), $t_project_id, $p_user_id ) ) { 244 return mci_fault_access_denied( $p_user_id ); 245 } 246 break; 247 } 248 249 # dump file content to the connection. 250 switch( config_get( 'file_upload_method' ) ) { 251 case DISK: 252 if( file_exists( $t_diskfile ) ) { 253 return mci_file_read_local( $t_diskfile ) ; 254 } else { 255 return ApiObjectFactory::faultNotFound( 'Unable to find an attachment with type ' . $p_type . 256 ' and id ' . $p_file_id . '.' ); 257 } 258 case DATABASE: 259 return $t_content; 260 default: 261 trigger_error( ERROR_GENERIC, ERROR ); 262 } 263} 264