prod_monitor.update.inc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. /**
  3. * ALL code here is taken from the Drupal core's update module and MODIFIED for
  4. * integration with prod_monitor. prod_check recommends that you turn the update
  5. * module off. prod_check provides the VERY BASIC functionality to get a list of
  6. * the installed modules with version info and transfer this to prod_monitor
  7. * over XMLRPC (alot of data here!) so that prod_monitor can do all the update
  8. * checking locally.
  9. */
  10. /**
  11. * Taken from D6 Core(!): modules/update/update.fetch.inc, line 25 and MODIFIED!
  12. * We chose to stick with the regular process instead of the more optimised D7
  13. * way. The D7 way is perfect when the update.module checks the site it is
  14. * running on, but we need to check several sites on an entirely different setup.
  15. *
  16. * Fetch project info via XML from a central server.
  17. */
  18. function _prod_monitor_update_refresh($id, $projects, $site_key) {
  19. global $base_url;
  20. $fail = &drupal_static(__FUNCTION__, array());
  21. // contains update_xml_parser class
  22. module_load_include('inc', 'update', 'update.fetch');
  23. $available = array();
  24. // As replacement for DRUPAL_CORE_COMPATIBILITY since prod_check and
  25. // prod_monitor should be site independant.
  26. $core = explode('.', $projects['drupal']['info']['version']);
  27. $core = $core[0] . '.x';
  28. $max_fetch_attempts = UPDATE_MAX_FETCH_ATTEMPTS;
  29. // Prepare object to store generated data to DB.
  30. $modules = new stdClass();
  31. $modules->id = $id;
  32. foreach ($projects as $key => $project) {
  33. $url = _prod_monitor_update_build_fetch_url($project, $site_key, $core);
  34. $fetch_url_base = _prod_monitor_update_get_fetch_url_base($project);
  35. if (empty($fail[$fetch_url_base]) || count($fail[$fetch_url_base]) < $max_fetch_attempts) {
  36. $xml = drupal_http_request($url);
  37. if (isset($xml->data)) {
  38. $data = update_parse_xml($xml->data);
  39. $available[$data['short_name']] = $data;
  40. }
  41. else {
  42. // Connection likely broken; prepare to give up.
  43. $fail[$fetch_url_base][$key] = 1;
  44. }
  45. }
  46. else {
  47. // Didn't bother trying to fetch.
  48. $fail[$fetch_url_base][$key] = 1;
  49. }
  50. }
  51. if (!empty($available) && is_array($available)) {
  52. // Record the projects where we failed to fetch data.
  53. foreach ($fail as $fetch_url_base => $failures) {
  54. foreach ($failures as $key => $value) {
  55. $available[$key]['project_status'] = 'not-fetched';
  56. }
  57. }
  58. $modules->available = serialize($available);
  59. watchdog('prod_monitor', 'Fetched information about all available new releases and updates for %link.', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_NOTICE, l(t('view'), 'admin/reports/prod-monitor/site/' . $id . '/view/updates'));
  60. }
  61. else {
  62. watchdog('prod_monitor', 'Unable to fetch any information about available new releases and updates for %link.', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR, l(t('view'), 'admin/reports/prod-monitor/site/' . $id . '/view/updates'));
  63. }
  64. // Whether this worked or not, we did just (try to) check for updates.
  65. $modules->lastupdate = time();
  66. $result = drupal_write_record('prod_monitor_site_modules', $modules, array('id'));
  67. if (!$result) {
  68. watchdog('prod_monitor', 'Could not update module data for %link', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR);
  69. }
  70. return $available;
  71. }
  72. /**
  73. * Taken from Core: modules/update/update.fetch.inc, line 266 and MODIFIED!
  74. *
  75. * This figures out the right URL to use, based on the project's .info file
  76. * and the global defaults. Appends optional query arguments when the site is
  77. * configured to report usage stats.
  78. *
  79. * @param $project
  80. * The array of project information from update_get_projects().
  81. * @param $site_key
  82. * The anonymous site key hash (optional).
  83. *
  84. * @see update_fetch_data()
  85. * @see _update_process_fetch_task()
  86. * @see update_get_projects()
  87. */
  88. function _prod_monitor_update_build_fetch_url($project, $site_key = '', $core) {
  89. $name = $project['name'];
  90. $url = _prod_monitor_update_get_fetch_url_base($project);
  91. $url .= '/' . $name . '/' . $core;
  92. // Only append a site_key and the version information if we have a site_key
  93. // in the first place, and if this is not a disabled module or theme. We do
  94. // not want to record usage statistics for disabled code.
  95. if (!empty($site_key) && (strpos($project['project_type'], 'disabled') === FALSE)) {
  96. $url .= (strpos($url, '?') === TRUE) ? '&' : '?';
  97. $url .= 'site_key=';
  98. $url .= rawurlencode($site_key);
  99. if (!empty($project['info']['version'])) {
  100. $url .= '&version=';
  101. $url .= rawurlencode($project['info']['version']);
  102. }
  103. }
  104. return $url;
  105. }
  106. /**
  107. * Taken from Core: modules/update/update.fetch.inc, line 297 and MODIFIED!
  108. *
  109. * Return the base of the URL to fetch available update data for a project.
  110. *
  111. * @param $project
  112. * The array of project information from update_get_projects().
  113. * @return
  114. * The base of the URL used for fetching available update data. This does
  115. * not include the path elements to specify a particular project, version,
  116. * site_key, etc.
  117. *
  118. * @see _update_build_fetch_url()
  119. */
  120. function _prod_monitor_update_get_fetch_url_base($project) {
  121. return isset($project['info']['project status url']) ? $project['info']['project status url'] : UPDATE_DEFAULT_URL;
  122. }
  123. /**
  124. * Taken from Core: modules/update/update.compare.inc, line 300 and MODIFIED!
  125. *
  126. * Calculate the current update status of all projects on the site.
  127. *
  128. * The results of this function are expensive to compute, especially on sites
  129. * with lots of modules or themes, since it involves a lot of comparisons and
  130. * other operations. Therefore, we cache the results into the {cache_update}
  131. * table using the 'update_project_data' cache ID. However, since this is not
  132. * the data about available updates fetched from the network, it is ok to
  133. * invalidate it somewhat quickly. If we keep this data for very long, site
  134. * administrators are more likely to see incorrect results if they upgrade to
  135. * a newer version of a module or theme but do not visit certain pages that
  136. * automatically clear this cache.
  137. *
  138. * @param array $available
  139. * Data about available project releases.
  140. *
  141. * @see update_get_available()
  142. * @see update_get_projects()
  143. * @see update_process_project_info()
  144. * @see update_project_cache()
  145. */
  146. function _prod_monitor_calculate_project_data($id, $projects, $available) {
  147. module_load_include('inc', 'update', 'update.compare');
  148. update_process_project_info($projects);
  149. foreach ($projects as $project => $project_info) {
  150. if (isset($available[$project])) {
  151. update_calculate_project_update_status($project, $projects[$project], $available[$project]);
  152. }
  153. else {
  154. $projects[$project]['status'] = UPDATE_UNKNOWN;
  155. $projects[$project]['reason'] = t('No available releases found');
  156. }
  157. }
  158. // Handle the ignored modules.
  159. if (($ignored = _prod_monitor_get_site_ignored($id))
  160. && !empty($ignored['updates'])) {
  161. foreach ($ignored['updates'] as $project_name) {
  162. if (isset($projects[$project_name])) {
  163. $projects[$project_name]['ignored'] = TRUE;
  164. $projects[$project_name]['status'] = UPDATE_UNKNOWN;
  165. }
  166. }
  167. }
  168. drupal_alter('prod_monitor_project_data', $id, $projects, $available);
  169. // Check if we need to flag a security update warning.
  170. // Prepare object to store generated data to DB.
  171. $modules = new stdClass();
  172. $modules->id = $id;
  173. // Assume there are no updates.
  174. $modules->updates = 1;
  175. // Check final status of each project.
  176. foreach ($projects as $project => $project_info) {
  177. switch ($project_info['status']) {
  178. case UPDATE_NOT_SECURE:
  179. case UPDATE_NOT_SUPPORTED:
  180. case UPDATE_REVOKED:
  181. $modules->updates = 3;
  182. // Stop the foreach loop as well.
  183. break 2;
  184. case UPDATE_NOT_CURRENT:
  185. if ($modules->updates < 2) {
  186. $modules->updates = 2;
  187. }
  188. break;
  189. }
  190. }
  191. $result = drupal_write_record('prod_monitor_site_modules', $modules, array('id'));
  192. if (!$result) {
  193. watchdog('prod_monitor', 'Could not update module security status for %link', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR);
  194. }
  195. return $projects;
  196. }