PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/api/soap/mc_api.php

https://github.com/fusenigk/mantisbt-1
PHP | 479 lines | 308 code | 83 blank | 88 comment | 46 complexity | 866e7560dc8330c92e2f95bd4c13e6ab MD5 | raw file
  1. <?php
  2. # MantisConnect - A webservice interface to Mantis Bug Tracker
  3. # Copyright (C) 2004-2011 Victor Boctor - vboctor@users.sourceforge.net
  4. # This program is distributed under dual licensing. These include
  5. # GPL and a commercial licenses. Victor Boctor reserves the right to
  6. # change the license of future releases.
  7. # See docs/ folder for more details
  8. # set up error_handler() as the new default error handling function
  9. set_error_handler( 'mc_error_handler' );
  10. # override some MantisBT configurations
  11. $g_show_detailed_errors = OFF;
  12. $g_stop_on_errors = ON;
  13. $g_display_errors = array(
  14. E_WARNING => 'halt',
  15. E_NOTICE => 'halt',
  16. E_USER_ERROR => 'halt',
  17. E_USER_WARNING => 'halt',
  18. E_USER_NOTICE => 'halt',
  19. );
  20. /**
  21. * Get the MantisConnect webservice version.
  22. */
  23. function mc_version() {
  24. return MANTIS_VERSION;
  25. }
  26. # Checks if MantisBT installation is marked as offline by the administrator.
  27. # true: offline, false: online
  28. function mci_is_mantis_offline() {
  29. $t_offline_file = dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'mantis_offline.php';
  30. return file_exists( $t_offline_file );
  31. }
  32. # return user_id if successful, otherwise false.
  33. function mci_check_login( $p_username, $p_password ) {
  34. if( mci_is_mantis_offline() ) {
  35. return false;
  36. }
  37. # if no user name supplied, then attempt to login as anonymous user.
  38. if( is_blank( $p_username ) ) {
  39. $t_anon_allowed = config_get( 'allow_anonymous_login' );
  40. if( OFF == $t_anon_allowed ) {
  41. return false;
  42. }
  43. $p_username = config_get( 'anonymous_account' );
  44. # do not use password validation.
  45. $p_password = null;
  46. }
  47. if( false === auth_attempt_script_login( $p_username, $p_password ) ) {
  48. return false;
  49. }
  50. return auth_get_current_user_id();
  51. }
  52. function mci_has_readonly_access( $p_user_id, $p_project_id = ALL_PROJECTS ) {
  53. $t_access_level = user_get_access_level( $p_user_id, $p_project_id );
  54. return( $t_access_level >= config_get( 'mc_readonly_access_level_threshold' ) );
  55. }
  56. function mci_has_readwrite_access( $p_user_id, $p_project_id = ALL_PROJECTS ) {
  57. $t_access_level = user_get_access_level( $p_user_id, $p_project_id );
  58. return( $t_access_level >= config_get( 'mc_readwrite_access_level_threshold' ) );
  59. }
  60. function mci_has_access( $p_access_level, $p_user_id, $p_project_id = ALL_PROJECTS ) {
  61. $t_access_level = user_get_access_level( $p_user_id, $p_project_id );
  62. return( $t_access_level >= (int) $p_access_level );
  63. }
  64. function mci_has_administrator_access( $p_user_id, $p_project_id = ALL_PROJECTS ) {
  65. $t_access_level = user_get_access_level( $p_user_id, $p_project_id );
  66. return( $t_access_level >= config_get( 'mc_admin_access_level_threshold' ) );
  67. }
  68. function mci_get_project_id( $p_project ) {
  69. if( (int) $p_project['id'] != 0 ) {
  70. $t_project_id = (int) $p_project['id'];
  71. } else {
  72. $t_project_id = project_get_id_by_name( $p_project['name'] );
  73. }
  74. return $t_project_id;
  75. }
  76. function mci_get_project_status_id( $p_status ) {
  77. return mci_get_enum_id_from_objectref( 'project_status', $p_status );
  78. }
  79. function mci_get_project_view_state_id( $p_view_state ) {
  80. return mci_get_enum_id_from_objectref( 'project_view_state', $p_view_state );
  81. }
  82. function mci_get_user_id( $p_user ) {
  83. $t_user_id = 0;
  84. if ( isset( $p_user['id'] ) && (int) $p_user['id'] != 0 ) {
  85. $t_user_id = (int) $p_user['id'];
  86. } elseif ( isset( $p_user['name'] ) ) {
  87. $t_user_id = user_get_id_by_name( $p_user['name'] );
  88. }
  89. return $t_user_id;
  90. }
  91. function mci_get_user_lang( $p_user_id ) {
  92. $t_lang = user_pref_get_pref( $p_user_id, 'language' );
  93. if( $t_lang == 'auto' ) {
  94. $t_lang = config_get( 'fallback_language' );
  95. }
  96. return $t_lang;
  97. }
  98. function mci_get_status_id( $p_status ) {
  99. return mci_get_enum_id_from_objectref( 'status', $p_status );
  100. }
  101. function mci_get_severity_id( $p_severity ) {
  102. return mci_get_enum_id_from_objectref( 'severity', $p_severity );
  103. }
  104. function mci_get_priority_id( $p_priority ) {
  105. return mci_get_enum_id_from_objectref( 'priority', $p_priority );
  106. }
  107. function mci_get_reproducibility_id( $p_reproducibility ) {
  108. return mci_get_enum_id_from_objectref( 'reproducibility', $p_reproducibility );
  109. }
  110. function mci_get_resolution_id( $p_resolution ) {
  111. return mci_get_enum_id_from_objectref( 'resolution', $p_resolution );
  112. }
  113. function mci_get_projection_id( $p_projection ) {
  114. return mci_get_enum_id_from_objectref( 'projection', $p_projection );
  115. }
  116. function mci_get_eta_id( $p_eta ) {
  117. return mci_get_enum_id_from_objectref( 'eta', $p_eta );
  118. }
  119. function mci_get_view_state_id( $p_view_state ) {
  120. return mci_get_enum_id_from_objectref( 'view_state', $p_view_state );
  121. }
  122. # Get null on empty value.
  123. #
  124. # @param Object $p_value The value
  125. # @return Object The value if not empty; null otherwise.
  126. #
  127. function mci_null_if_empty( $p_value ) {
  128. if( !is_blank( $p_value ) ) {
  129. return $p_value;
  130. }
  131. return null;
  132. }
  133. /**
  134. * Gets the url for MantisBT.
  135. *
  136. * @return MantisBT URL terminated by a /.
  137. */
  138. function mci_get_mantis_path() {
  139. return config_get( 'path' );
  140. }
  141. # Given a enum string and num, return the appropriate localized string
  142. function mci_get_enum_element( $p_enum_name, $p_val, $p_lang ) {
  143. $t_enum_string = config_get( $p_enum_name . '_enum_string' );
  144. $t_localized_enum_string = lang_get( $p_enum_name . '_enum_string', $p_lang );
  145. return MantisEnum::getLocalizedLabel( $t_enum_string, $t_localized_enum_string, $p_val );
  146. }
  147. # Gets the sub-projects that are accessible to the specified user / project.
  148. function mci_user_get_accessible_subprojects( $p_user_id, $p_parent_project_id, $p_lang = null ) {
  149. if( $p_lang === null ) {
  150. $t_lang = mci_get_user_lang( $p_user_id );
  151. } else {
  152. $t_lang = $p_lang;
  153. }
  154. $t_result = array();
  155. foreach( user_get_accessible_subprojects( $p_user_id, $p_parent_project_id ) as $t_subproject_id ) {
  156. $t_subproject_row = project_cache_row( $t_subproject_id );
  157. $t_subproject = array();
  158. $t_subproject['id'] = $t_subproject_id;
  159. $t_subproject['name'] = $t_subproject_row['name'];
  160. $t_subproject['status'] = mci_enum_get_array_by_id( $t_subproject_row['status'], 'project_status', $t_lang );
  161. $t_subproject['enabled'] = $t_subproject_row['enabled'];
  162. $t_subproject['view_state'] = mci_enum_get_array_by_id( $t_subproject_row['view_state'], 'project_view_state', $t_lang );
  163. $t_subproject['access_min'] = mci_enum_get_array_by_id( $t_subproject_row['access_min'], 'access_levels', $t_lang );
  164. $t_subproject['file_path'] = array_key_exists( 'file_path', $t_subproject_row ) ? $t_subproject_row['file_path'] : "";
  165. $t_subproject['description'] = array_key_exists( 'description', $t_subproject_row ) ? $t_subproject_row['description'] : "";
  166. $t_subproject['subprojects'] = mci_user_get_accessible_subprojects( $p_user_id, $t_subproject_id, $t_lang );
  167. $t_result[] = $t_subproject;
  168. }
  169. return $t_result;
  170. }
  171. function translate_category_name_to_id( $p_category_name, $p_project_id ) {
  172. if ( !isset( $p_category_name ) ) {
  173. return 0;
  174. }
  175. $t_cat_array = category_get_all_rows( $p_project_id );
  176. foreach( $t_cat_array as $t_category_row ) {
  177. if( $t_category_row['name'] == $p_category_name ) {
  178. return $t_category_row['id'];
  179. }
  180. }
  181. return 0;
  182. }
  183. /**
  184. * Basically this is a copy of core/filter_api.php#filter_db_get_available_queries().
  185. * The only difference is that the result of this function is not an array of filter
  186. * names but an array of filter structures.
  187. */
  188. function mci_filter_db_get_available_queries( $p_project_id = null, $p_user_id = null ) {
  189. $t_filters_table = db_get_table( 'filters' );
  190. $t_overall_query_arr = array();
  191. if( null === $p_project_id ) {
  192. $t_project_id = helper_get_current_project();
  193. } else {
  194. $t_project_id = db_prepare_int( $p_project_id );
  195. }
  196. if( null === $p_user_id ) {
  197. $t_user_id = auth_get_current_user_id();
  198. } else {
  199. $t_user_id = db_prepare_int( $p_user_id );
  200. }
  201. # If the user doesn't have access rights to stored queries, just return
  202. if( !access_has_project_level( config_get( 'stored_query_use_threshold' ) ) ) {
  203. return $t_overall_query_arr;
  204. }
  205. # Get the list of available queries. By sorting such that public queries are
  206. # first, we can override any query that has the same name as a private query
  207. # with that private one
  208. $query = "SELECT * FROM $t_filters_table
  209. WHERE (project_id='$t_project_id'
  210. OR project_id='0')
  211. AND name!=''
  212. ORDER BY is_public DESC, name ASC";
  213. $result = db_query( $query );
  214. $query_count = db_num_rows( $result );
  215. for( $i = 0;$i < $query_count;$i++ ) {
  216. $row = db_fetch_array( $result );
  217. if(( $row['user_id'] == $t_user_id ) || db_prepare_bool( $row['is_public'] ) ) {
  218. $t_filter_detail = explode( '#', $row['filter_string'], 2 );
  219. $t_filter = unserialize( $t_filter_detail[1] );
  220. $t_filter = filter_ensure_valid_filter( $t_filter );
  221. $row['url'] = filter_get_url( $t_filter );
  222. $t_overall_query_arr[$row['name']] = $row;
  223. }
  224. }
  225. return array_values( $t_overall_query_arr );
  226. }
  227. /**
  228. * Get a category definition.
  229. *
  230. * @param integer $p_category_id The id of the category to retrieve.
  231. * @return Array an Array containing the id and the name of the category.
  232. */
  233. function mci_category_as_array_by_id( $p_category_id ) {
  234. $t_result = array();
  235. $t_result['id'] = $p_category_id;
  236. $t_result['name'] = category_get_name( $p_category_id );
  237. return $t_result;
  238. }
  239. /**
  240. * Returns time tracking information from a bug note.
  241. *
  242. * @param int $p_issue_id The id of the issue
  243. * @param Array $p_note A note as passed to the soap api methods
  244. *
  245. * @return String the string time entry to be added to the bugnote, in 'HH:mm' format
  246. */
  247. function mci_get_time_tracking_from_note( $p_issue_id, $p_note) {
  248. if ( !access_has_bug_level( config_get( 'time_tracking_view_threshold' ), $p_issue_id ) )
  249. return '00:00';
  250. if ( !isset( $p_note['time_tracking'] ))
  251. return '00:00';
  252. return db_minutes_to_hhmm($p_note['time_tracking']);
  253. }
  254. /**
  255. * SECURITY NOTE: these globals are initialized here to prevent them
  256. * being spoofed if register_globals is turned on
  257. */
  258. $g_error_parameters = array();
  259. $g_error_handled = false;
  260. $g_error_proceed_url = null;
  261. # Default error handler
  262. #
  263. # This handler will not receive E_ERROR, E_PARSE, E_CORE_*, or E_COMPILE_*
  264. # errors.
  265. #
  266. # E_USER_* are triggered by us and will contain an error constant in $p_error
  267. # The others, being system errors, will come with a string in $p_error
  268. #
  269. function mc_error_handler( $p_type, $p_error, $p_file, $p_line, $p_context ) {
  270. global $g_error_parameters, $g_error_handled, $g_error_proceed_url;
  271. global $g_lang_overrides;
  272. global $g_error_send_page_header;
  273. global $l_oServer;
  274. # check if errors were disabled with @ somewhere in this call chain
  275. # also suppress php 5 strict warnings
  276. if( 0 == error_reporting() || 2048 == $p_type ) {
  277. return;
  278. }
  279. $t_lang_pushed = false;
  280. # flush any language overrides to return to user's natural default
  281. if( function_exists( 'db_is_connected' ) ) {
  282. if( db_is_connected() ) {
  283. lang_push( lang_get_default() );
  284. $t_lang_pushed = true;
  285. }
  286. }
  287. $t_short_file = basename( $p_file );
  288. $t_method_array = config_get( 'display_errors' );
  289. if( isset( $t_method_array[$p_type] ) ) {
  290. $t_method = $t_method_array[$p_type];
  291. } else {
  292. $t_method = 'none';
  293. }
  294. # build an appropriate error string
  295. switch( $p_type ) {
  296. case E_WARNING:
  297. $t_error_type = 'SYSTEM WARNING';
  298. $t_error_description = $p_error;
  299. break;
  300. case E_NOTICE:
  301. $t_error_type = 'SYSTEM NOTICE';
  302. $t_error_description = $p_error;
  303. break;
  304. case E_USER_ERROR:
  305. $t_error_type = "APPLICATION ERROR #$p_error";
  306. $t_error_description = error_string( $p_error );
  307. break;
  308. case E_USER_WARNING:
  309. $t_error_type = "APPLICATION WARNING #$p_error";
  310. $t_error_description = error_string( $p_error );
  311. break;
  312. case E_USER_NOTICE:
  313. # used for debugging
  314. $t_error_type = 'DEBUG';
  315. $t_error_description = $p_error;
  316. break;
  317. default:
  318. #shouldn't happen, just display the error just in case
  319. $t_error_type = '';
  320. $t_error_description = $p_error;
  321. }
  322. $t_error_description = $t_error_description;
  323. $t_error_stack = error_get_stack_trace();
  324. $l_oServer->fault( 'Server', "Error Type: $t_error_type,\nError Description:\n$t_error_description,\nStack Trace:\n$t_error_stack" );
  325. $l_oServer->send_response();
  326. exit();
  327. }
  328. # Get a stack trace if PHP provides the facility or xdebug is present
  329. function error_get_stack_trace() {
  330. $t_trace = '';
  331. if ( extension_loaded( 'xdebug' ) ) {
  332. #check for xdebug presence
  333. $t_stack = xdebug_get_function_stack();
  334. # reverse the array in a separate line of code so the
  335. # array_reverse() call doesn't appear in the stack
  336. $t_stack = array_reverse( $t_stack );
  337. array_shift( $t_stack );
  338. #remove the call to this function from the stack trace
  339. foreach( $t_stack as $t_frame ) {
  340. $t_trace .= ( isset( $t_frame['file'] ) ? basename( $t_frame['file'] ) : 'UnknownFile' ) . ' L' . ( isset( $t_frame['line'] ) ? $t_frame['line'] : '?' ) . ' ' . ( isset( $t_frame['function'] ) ? $t_frame['function'] : 'UnknownFunction' );
  341. $t_args = array();
  342. if ( isset( $t_frame['params'] ) && ( count( $t_frame['params'] ) > 0 ) ) {
  343. $t_trace .= ' Params: ';
  344. foreach( $t_frame['params'] as $t_value ) {
  345. $t_args[] = error_build_parameter_string( $t_value );
  346. }
  347. $t_trace .= '(' . implode( $t_args, ', ' ) . ')';
  348. } else {
  349. $t_trace .= '()';
  350. }
  351. $t_trace .= "\n";
  352. }
  353. } else {
  354. $t_stack = debug_backtrace();
  355. array_shift( $t_stack ); #remove the call to this function from the stack trace
  356. array_shift( $t_stack ); #remove the call to the error handler from the stack trace
  357. foreach( $t_stack as $t_frame ) {
  358. $t_trace .= ( isset( $t_frame['file'] ) ? basename( $t_frame['file'] ) : 'UnknownFile' ) . ' L' . ( isset( $t_frame['line'] ) ? $t_frame['line'] : '?' ) . ' ' . ( isset( $t_frame['function'] ) ? $t_frame['function'] : 'UnknownFunction' );
  359. $t_args = array();
  360. if( isset( $t_frame['args'] ) ) {
  361. foreach( $t_frame['args'] as $t_value ) {
  362. $t_args[] = error_build_parameter_string( $t_value );
  363. }
  364. $t_trace .= '(' . implode( $t_args, ', ' ) . ')';
  365. } else {
  366. $t_trace .= '()';
  367. }
  368. $t_trace .= "\n";
  369. }
  370. }
  371. return $t_trace;
  372. }
  373. /**
  374. * Returns a soap_fault signalling corresponding to a failed login
  375. * situation
  376. *
  377. * @return soap_fault
  378. */
  379. function mci_soap_fault_login_failed() {
  380. return new soap_fault('Client', '', 'Access denied.');
  381. }
  382. /**
  383. * Returns a soap_fault signalling that the user does not have
  384. * access rights for the specific action.
  385. *
  386. * @param int $p_user_id a valid user id
  387. * @param string $p_detail The optional details to append to the error message
  388. * @return soap_fault
  389. */
  390. function mci_soap_fault_access_denied( $p_user_id, $p_detail = '' ) {
  391. $t_user_name = user_get_name( $p_user_id );
  392. $t_reason = 'Access denied for user '. $t_user_name . '.';
  393. if ( !is_blank( $p_detail ))
  394. $t_reason .= ' Reason: ' . $p_detail . '.';
  395. return new soap_fault( 'Client', '', $t_reason );
  396. }