PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/GSoC-config/squirrelmail/functions/plugin.php

#
PHP | 943 lines | 263 code | 111 blank | 569 comment | 84 complexity | fa3a07d0e90358c052bbc4065f99401e MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0
  1. <?php
  2. /**
  3. * plugin.php
  4. *
  5. * This file provides the framework for a plugin architecture.
  6. *
  7. * Documentation on how to write plugins might show up some time.
  8. *
  9. * @copyright &copy; 1999-2007 The SquirrelMail Project Team
  10. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  11. * @version $Id: plugin.php 12400 2007-05-17 16:36:13Z kink $
  12. * @package squirrelmail
  13. */
  14. /**
  15. * This function adds a plugin.
  16. * @param string $name Internal plugin name (ie. delete_move_next)
  17. * @return void
  18. */
  19. function use_plugin ($name) {
  20. if (file_exists(SM_PATH . "plugins/$name/setup.php")) {
  21. include_once(SM_PATH . "plugins/$name/setup.php");
  22. /**
  23. * As of SM 1.5.2, plugin hook registration is statically
  24. * accomplished using the configuration utility (config/conf.pl).
  25. * And this code is deprecated (but let's keep it until
  26. * the new registration system is proven).
  27. *
  28. */
  29. //$function = "squirrelmail_plugin_init_$name";
  30. //if (function_exists($function)) {
  31. // $function();
  32. //}
  33. }
  34. }
  35. /**
  36. * This function executes a plugin hook.
  37. *
  38. * It includes an arbitrary return value that is managed by
  39. * all plugins on the same hook and returned to the core hook
  40. * location.
  41. *
  42. * The desired format of the return value should be defined
  43. * by the context in which the hook is called.
  44. *
  45. * Note that the master return value for this hook is passed
  46. * to each plugin after the main argument(s) value/array as a
  47. * convenience only - to show what the current return value is
  48. * even though it is liable to be changed by other plugins.
  49. *
  50. * If any plugin on this hook wants to modify the $args
  51. * plugin parameter, it simply has to use call-by-reference
  52. * syntax in the hook function that it has registered for the
  53. * current hook. Note that this is in addition to (entirely
  54. * independent of) the return value for this hook.
  55. *
  56. * @param string $name Name of hook being executed
  57. * @param mixed $args A single value or an array of arguments
  58. * that are to be passed to all plugins
  59. * operating off the hook being called.
  60. * Note that this argument is passed by
  61. * reference thus it is liable to be
  62. * changed after the hook completes.
  63. *
  64. * @return mixed The return value that is managed by the plugins
  65. * on the current hook.
  66. *
  67. */
  68. function do_hook($name, &$args) {
  69. global $squirrelmail_plugin_hooks, $currentHookName;
  70. $currentHookName = $name;
  71. $ret = NULL;
  72. if (isset($squirrelmail_plugin_hooks[$name])
  73. && is_array($squirrelmail_plugin_hooks[$name])) {
  74. foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) {
  75. use_plugin($plugin_name);
  76. if (function_exists($function)) {
  77. $ret = $function($args, $ret);
  78. // each plugin can call additional hooks, so need
  79. // to make sure the current hook name is accurate
  80. // again after each plugin has finished
  81. //
  82. $currentHookName = $name;
  83. }
  84. }
  85. }
  86. $currentHookName = '';
  87. return $ret;
  88. }
  89. /**
  90. * This function executes a hook that allows for an arbitrary
  91. * return value from each plugin that will be merged into one
  92. * array (or one string if all return values are strings) and
  93. * returned to the core hook location.
  94. *
  95. * Note that unlike PHP's array_merge function, matching array keys
  96. * will not overwrite each other, instead, values under such keys
  97. * will be concatenated if they are both strings, or merged if they
  98. * are arrays (in the same (non-overwrite) manner recursively).
  99. *
  100. * Plugins returning non-arrays (strings, objects, etc) will have
  101. * their output added to the end of the ultimate return array,
  102. * unless ALL values returned are strings, in which case one string
  103. * with all returned strings concatenated together is returned
  104. * (unless $force_array is TRUE).
  105. *
  106. * If any plugin on this hook wants to modify the $args
  107. * plugin parameter, it simply has to use call-by-reference
  108. * syntax in the hook function that it has registered for the
  109. * current hook. Note that this is in addition to (entirely
  110. * independent of) the return value for this hook.
  111. *
  112. * @param string $name Name of hook being executed
  113. * @param mixed $args A single value or an array of arguments
  114. * that are to be passed to all plugins
  115. * operating off the hook being called.
  116. * Note that this argument is passed by
  117. * reference thus it is liable to be
  118. * changed after the hook completes.
  119. * @param boolean $force_array When TRUE, guarantees the return
  120. * value will ALWAYS be an array,
  121. * (simple strings will be forced
  122. * into a one-element array).
  123. * When FALSE, behavior is as
  124. * described above (OPTIONAL;
  125. * default behavior is to return
  126. * mixed - array or string).
  127. *
  128. * @return mixed the merged return arrays or strings of each
  129. * plugin on this hook.
  130. *
  131. */
  132. function concat_hook_function($name, &$args, $force_array=FALSE) {
  133. global $squirrelmail_plugin_hooks, $currentHookName;
  134. $currentHookName = $name;
  135. $ret = '';
  136. if (isset($squirrelmail_plugin_hooks[$name])
  137. && is_array($squirrelmail_plugin_hooks[$name])) {
  138. foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) {
  139. use_plugin($plugin_name);
  140. if (function_exists($function)) {
  141. $plugin_ret = $function($args);
  142. if (!empty($plugin_ret)) {
  143. $ret = sqm_array_merge($ret, $plugin_ret);
  144. }
  145. // each plugin can call additional hooks, so need
  146. // to make sure the current hook name is accurate
  147. // again after each plugin has finished
  148. //
  149. $currentHookName = $name;
  150. }
  151. }
  152. }
  153. if ($force_array && is_string($ret)) {
  154. $ret = array($ret);
  155. }
  156. $currentHookName = '';
  157. return $ret;
  158. }
  159. /**
  160. * This function is used for hooks which are to return true or
  161. * false. If $priority is > 0, any one or more trues will override
  162. * any falses. If $priority < 0, then one or more falses will
  163. * override any trues.
  164. * Priority 0 means majority rules. Ties will be broken with $tie
  165. *
  166. * If any plugin on this hook wants to modify the $args
  167. * plugin parameter, it simply has to use call-by-reference
  168. * syntax in the hook function that it has registered for the
  169. * current hook. Note that this is in addition to (entirely
  170. * independent of) the return value for this hook.
  171. *
  172. * @param string $name The hook name
  173. * @param mixed $args A single value or an array of arguments
  174. * that are to be passed to all plugins
  175. * operating off the hook being called.
  176. * Note that this argument is passed by
  177. * reference thus it is liable to be
  178. * changed after the hook completes.
  179. * @param int $priority See explanation above
  180. * @param boolean $tie See explanation above
  181. *
  182. * @return boolean The result of the function
  183. *
  184. */
  185. function boolean_hook_function($name, &$args, $priority=0, $tie=false) {
  186. global $squirrelmail_plugin_hooks, $currentHookName;
  187. $yea = 0;
  188. $nay = 0;
  189. $ret = $tie;
  190. if (isset($squirrelmail_plugin_hooks[$name]) &&
  191. is_array($squirrelmail_plugin_hooks[$name])) {
  192. /* Loop over the plugins that registered the hook */
  193. $currentHookName = $name;
  194. foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) {
  195. use_plugin($plugin_name);
  196. if (function_exists($function)) {
  197. $ret = $function($args);
  198. if ($ret) {
  199. $yea++;
  200. } else {
  201. $nay++;
  202. }
  203. // each plugin can call additional hooks, so need
  204. // to make sure the current hook name is accurate
  205. // again after each plugin has finished
  206. //
  207. $currentHookName = $name;
  208. }
  209. }
  210. $currentHookName = '';
  211. /* Examine the aftermath and assign the return value appropriately */
  212. if (($priority > 0) && ($yea)) {
  213. $ret = true;
  214. } elseif (($priority < 0) && ($nay)) {
  215. $ret = false;
  216. } elseif ($yea > $nay) {
  217. $ret = true;
  218. } elseif ($nay > $yea) {
  219. $ret = false;
  220. } else {
  221. // There's a tie, no action needed.
  222. }
  223. return $ret;
  224. }
  225. // If the code gets here, there was a problem - no hooks, etc.
  226. return NULL;
  227. }
  228. /**
  229. * Do not use, use checkForJavascript() instead.
  230. *
  231. * This function checks whether the user's USER_AGENT is known to
  232. * be broken. If so, returns true and the plugin is invisible to the
  233. * offending browser.
  234. * *** THIS IS A TEST FOR JAVASCRIPT SUPPORT ***
  235. *
  236. * @return bool whether this browser properly supports JavaScript
  237. * @deprecated use checkForJavascript() since 1.5.1
  238. */
  239. function soupNazi(){
  240. return !checkForJavascript();
  241. }
  242. /**
  243. * Check if plugin is enabled
  244. * @param string $plugin_name plugin name
  245. * @since 1.5.1
  246. * @return boolean
  247. */
  248. function is_plugin_enabled($plugin_name) {
  249. global $plugins;
  250. /**
  251. * check if variable is empty. if var is not set, php empty
  252. * returns true without error notice.
  253. *
  254. * then check if it is an array
  255. */
  256. if (empty($plugins) || ! is_array($plugins))
  257. return false;
  258. if ( in_array($plugin_name,$plugins) ) {
  259. return true;
  260. } else {
  261. return false;
  262. }
  263. }
  264. /**
  265. * Get a plugin's version.
  266. *
  267. * Determines and returns a plugin's version.
  268. *
  269. * By default, the desired plugin must be currently
  270. * activated, and if it is not, this function will
  271. * return FALSE. By overriding the default value
  272. * of $force_inclusion, this function will attempt
  273. * to grab versioning information from the given
  274. * plugin even if it is not activated (plugin still
  275. * has to be unpackaged and set in place in the
  276. * plugins directory). Use with care - some plugins
  277. * might break SquirrelMail when this is used.
  278. *
  279. * By turning on the $do_parse argument, the version
  280. * string will be parsed by SquirrelMail into a
  281. * SquirrelMail-compatible version string (such as
  282. * "1.2.3") if it is not already.
  283. *
  284. * Note that this assumes plugin versioning is
  285. * consistently applied in the same fashion that
  286. * SquirrelMail versions are, with the exception that
  287. * an applicable SquirrelMail version may be appended
  288. * to the version number (which will be ignored herein).
  289. * That is, plugin version number schemes are expected
  290. * in the following format: 1.2.3, or 1.2.3-1.4.0.
  291. *
  292. * Any characters after the third version number
  293. * indicating things such as beta or release candidate
  294. * versions are discarded, so formats such as the
  295. * following will also work, although extra information
  296. * about beta versions can possibly confuse the desired
  297. * results of the version check: 1.2.3-beta4, 1.2.3.RC2,
  298. * and so forth.
  299. *
  300. * @since 1.5.2
  301. *
  302. * @param string plugin_name name of the plugin to
  303. * check; must precisely
  304. * match the plugin
  305. * directory name
  306. * @param bool force_inclusion try to get version info
  307. * for plugins not activated?
  308. * (default FALSE)
  309. * @param bool do_parse return the plugin version
  310. * in SquirrelMail-compatible
  311. * format (default FALSE)
  312. *
  313. * @return mixed The plugin version string if found, otherwise,
  314. * boolean FALSE is returned indicating that no
  315. * version information could be found for the plugin.
  316. *
  317. */
  318. function get_plugin_version($plugin_name, $force_inclusion = FALSE, $do_parse = FALSE)
  319. {
  320. $info_function = $plugin_name . '_info';
  321. $version_function = $plugin_name . '_version';
  322. $plugin_info = array();
  323. $plugin_version = FALSE;
  324. // first attempt to find the plugin info function, wherein
  325. // the plugin version should be available
  326. //
  327. if (function_exists($info_function))
  328. $plugin_info = $info_function();
  329. else if ($force_inclusion
  330. && file_exists(SM_PATH . 'plugins/' . $plugin_name . '/setup.php'))
  331. {
  332. /* --- Old code, keeping just in case... problem with it is, for example,
  333. if it is used, but later we are checking if the same plugin is
  334. activated (because it SHOULD be), this code having run will possibly
  335. create a false positive.
  336. include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  337. if (function_exists($info_function))
  338. $plugin_info = $info_function();
  339. --- */
  340. // so what we need to do is process this plugin without
  341. // it polluting our environment
  342. //
  343. // we *could* just use the above code, which is more of a
  344. // sure thing than some regular expressions, and then test
  345. // the contents of the $plugins array to see if this plugin
  346. // is actually activated, and that might be good enough, but
  347. // for now, we'll use the following approach, because of two
  348. // concerns: other plugins and other templates might force
  349. // the inclusion of a plugin (which SHOULD also add it to
  350. // the $plugins array, but am not 100% sure at this time (FIXME)),
  351. // and because the regexps below should work just fine with
  352. // any resonably formatted plugin setup file.
  353. //
  354. // read the target plugin's setup.php file into a string,
  355. // then use a regular expression to try to find the version...
  356. // this of course can break if plugin authors do funny things
  357. // with their file formatting
  358. //
  359. $setup_file = '';
  360. $file_contents = file(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  361. foreach ($file_contents as $line)
  362. $setup_file .= $line;
  363. // this regexp grabs a version number from a standard
  364. // <plugin>_info() function
  365. //
  366. if (preg_match('/[\'"]version[\'"]\s*=>\s*[\'"](.+?)[\'"]/is', $setup_file, $matches))
  367. $plugin_info = array('version' => $matches[1]);
  368. // this regexp grabs a version number from a standard
  369. // (deprecated) <plugin>_version() function
  370. //
  371. else if (preg_match('/function\s+.*?' . $plugin_name . '_version.*?\(.*?\).*?\{.*?return\s+[\'"](.+?)[\'"]/is', $setup_file, $matches))
  372. $plugin_info = array('version' => $matches[1]);
  373. }
  374. if (!empty($plugin_info['version']))
  375. $plugin_version = $plugin_info['version'];
  376. // otherwise, look for older version function
  377. //
  378. if (!$plugin_version && function_exists($version_function))
  379. $plugin_version = $version_function();
  380. if ($plugin_version && $do_parse)
  381. {
  382. // massage version number into something we understand
  383. //
  384. // the first regexp strips everything and anything that follows
  385. // the first occurance of a non-digit (or non decimal point), so
  386. // beware that putting letters in the middle of a version string
  387. // will effectively truncate the version string right there (but
  388. // this also just helps remove the SquirrelMail version part off
  389. // of versions such as "1.2.3-1.4.4")
  390. //
  391. // the second regexp just strips out non-digits/non-decimal points
  392. // (and might be redundant(?))
  393. //
  394. // the regexps are wrapped in a trim that makes sure the version
  395. // does not start or end with a decimal point
  396. //
  397. $plugin_version = trim(preg_replace(array('/[^0-9.]+.*$/', '/[^0-9.]/'),
  398. '', $plugin_version),
  399. '.');
  400. }
  401. return $plugin_version;
  402. }
  403. /**
  404. * Check a plugin's version.
  405. *
  406. * Returns TRUE if the given plugin is installed,
  407. * activated and is at minimum version $a.$b.$c.
  408. * If any one of those conditions fails, FALSE
  409. * will be returned (careful of plugins that are
  410. * sufficiently versioned but are not activated).
  411. *
  412. * By overriding the default value of $force_inclusion,
  413. * this function will attempt to grab versioning
  414. * information from the given plugin even if it
  415. * is not activated (the plugin still has to be
  416. * unpackaged and set in place in the plugins
  417. * directory). Use with care - some plugins
  418. * might break SquirrelMail when this is used.
  419. *
  420. * Note that this function assumes plugin
  421. * versioning is consistently applied in the same
  422. * fashion that SquirrelMail versions are, with the
  423. * exception that an applicable SquirrelMail
  424. * version may be appended to the version number
  425. * (which will be ignored herein). That is, plugin
  426. * version number schemes are expected in the following
  427. * format: 1.2.3, or 1.2.3-1.4.0.
  428. *
  429. * Any characters after the third number indicating
  430. * things such as beta or release candidate versions
  431. * are discarded, so formats such as the following
  432. * will also work, although extra information about
  433. * beta versions can possibly confuse the desired results
  434. * of the version check: 1.2.3-beta4, 1.2.3.RC2, and so forth.
  435. *
  436. * @since 1.5.2
  437. *
  438. * @param string plugin_name Name of the plugin to
  439. * check; must precisely
  440. * match the plugin
  441. * directory name
  442. * @param int a Major version number
  443. * @param int b Minor version number
  444. * @param int c Release number
  445. * @param bool force_inclusion Try to get version info
  446. * for plugins not activated?
  447. * (default FALSE)
  448. *
  449. * @return bool
  450. *
  451. */
  452. function check_plugin_version($plugin_name,
  453. $a = 0, $b = 0, $c = 0,
  454. $force_inclusion = FALSE)
  455. {
  456. $plugin_version = get_plugin_version($plugin_name, $force_inclusion, TRUE);
  457. if (!$plugin_version) return FALSE;
  458. // split the version string into sections delimited by
  459. // decimal points, and make sure we have three sections
  460. //
  461. $plugin_version = explode('.', $plugin_version);
  462. if (!isset($plugin_version[0])) $plugin_version[0] = 0;
  463. if (!isset($plugin_version[1])) $plugin_version[1] = 0;
  464. if (!isset($plugin_version[2])) $plugin_version[2] = 0;
  465. // sm_print_r($plugin_version);
  466. // now test the version number
  467. //
  468. if ($plugin_version[0] < $a ||
  469. ($plugin_version[0] == $a && $plugin_version[1] < $b) ||
  470. ($plugin_version[0] == $a && $plugin_version[1] == $b && $plugin_version[2] < $c))
  471. return FALSE;
  472. return TRUE;
  473. }
  474. /**
  475. * Get a certain plugin requirement.
  476. *
  477. * Attempts to find the given plugin requirement value
  478. * in the given plugin's informational array, and returns
  479. * it or NULL if it was not found.
  480. *
  481. * Some plugins have different values for the same
  482. * requirement depending on the SquirrelMail version,
  483. * and this function is smart enough to take that into
  484. * account.
  485. *
  486. * By default, the desired plugin must be currently
  487. * activated, and if it is not, this function will
  488. * return NULL. By overriding the default value
  489. * of $force_inclusion, this function will attempt
  490. * to grab requirement information from the given
  491. * plugin even if it is not activated (plugin still
  492. * has to be unpackaged and set in place in the
  493. * plugins directory). Use with care - some plugins
  494. * might break SquirrelMail when this is used.
  495. *
  496. * @since 1.5.2
  497. *
  498. * @param string plugin_name Name of the plugin to
  499. * check; must precisely
  500. * match the plugin
  501. * directory name
  502. * @param string requirement The desired requirement name
  503. * @param bool force_inclusion Try to get requirement info
  504. * for plugins not activated?
  505. * (default FALSE)
  506. *
  507. * @return mixed NULL is returned if the plugin could not be
  508. * found or does not include the given requirement,
  509. * the constant SQ_INCOMPATIBLE is returned if the
  510. * given plugin is entirely incompatible with the
  511. * current SquirrelMail version, otherwise the
  512. * value of the requirement is returned, whatever
  513. * that may be (varies per requirement type).
  514. *
  515. */
  516. function get_plugin_requirement($plugin_name, $requirement,
  517. $force_inclusion = FALSE)
  518. {
  519. $info_function = $plugin_name . '_info';
  520. $plugin_info = array();
  521. $requirement_value = NULL;
  522. // first attempt to find the plugin info function, wherein
  523. // the plugin requirements should be available
  524. //
  525. if (function_exists($info_function))
  526. $plugin_info = $info_function();
  527. else if ($force_inclusion
  528. && file_exists(SM_PATH . 'plugins/' . $plugin_name . '/setup.php'))
  529. {
  530. /* --- Old code, keeping just in case... problem with it is, for example,
  531. if it is used, but later we are checking if the same plugin is
  532. activated (because it SHOULD be), this code having run will possibly
  533. create a false positive.
  534. include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  535. if (function_exists($info_function))
  536. $plugin_info = $info_function();
  537. --- */
  538. // so what we need to do is process this plugin without
  539. // it polluting our environment
  540. //
  541. // we *could* just use the above code, which is more of a
  542. // sure thing than a regular expression, and then test
  543. // the contents of the $plugins array to see if this plugin
  544. // is actually activated, and that might be good enough, but
  545. // for now, we'll use the following approach, because of two
  546. // concerns: other plugins and other templates might force
  547. // the inclusion of a plugin (which SHOULD also add it to
  548. // the $plugins array, but am not 100% sure at this time (FIXME)),
  549. // and because the regexp below should work just fine with
  550. // any resonably formatted plugin setup file.
  551. //
  552. // read the target plugin's setup.php file into a string,
  553. // then use a regular expression to try to find the needed
  554. // requirement information...
  555. // this of course can break if plugin authors do funny things
  556. // with their file formatting
  557. //
  558. $setup_file = '';
  559. $file_contents = file(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  560. foreach ($file_contents as $line)
  561. $setup_file .= $line;
  562. // this regexp grabs the full plugin info array from a standard
  563. // <plugin>_info() function... determining the end of the info
  564. // array can fail, but if authors end the array with ");\n"
  565. // (without quotes), then it should work well, especially because
  566. // newlines shouldn't be found inside the array after any ");"
  567. // (without quotes)
  568. //
  569. if (preg_match('/function\s+.*?' . $plugin_name . '_info.*?\(.*?\).*?\{.*?(array.+?\)\s*;)\s*' . "\n" . '/is', $setup_file, $matches))
  570. eval('$plugin_info = ' . $matches[1]);
  571. }
  572. // attempt to get the requirement from the "global" scope
  573. // of the plugin information array
  574. //
  575. if (isset($plugin_info[$requirement])
  576. && !is_null($plugin_info[$requirement]))
  577. $requirement_value = $plugin_info[$requirement];
  578. // now, if there is a series of per-version requirements,
  579. // check there too
  580. //
  581. if (!empty($plugin_info['per_version_requirements'])
  582. && is_array($plugin_info['per_version_requirements']))
  583. {
  584. // iterate through requirements, where keys are version
  585. // numbers -- tricky part is knowing the difference between
  586. // more than one version for which the current SM installation
  587. // passes the check_sm_version() test... we want the highest one
  588. //
  589. $requirement_value_override = NULL;
  590. $highest_version_array = array();
  591. foreach ($plugin_info['per_version_requirements'] as $version => $requirement_overrides)
  592. {
  593. $version_array = explode('.', $version);
  594. if (sizeof($version_array) != 3) continue;
  595. $a = $version_array[0];
  596. $b = $version_array[1];
  597. $c = $version_array[2];
  598. if (check_sm_version($a, $b, $c)
  599. && ( !empty($requirement_overrides[SQ_INCOMPATIBLE])
  600. || (isset($requirement_overrides[$requirement])
  601. && !is_null($requirement_overrides[$requirement]))))
  602. {
  603. if (empty($highest_version_array)
  604. || $highest_version_array[0] < $a
  605. || ($highest_version_array[0] == $a
  606. && $highest_version_array[1] < $b)
  607. || ($highest_version_array[0] == $a
  608. && $highest_version_array[1] == $b
  609. && $highest_version_array[2] < $c))
  610. {
  611. $highest_version_array = $version_array;
  612. if (!empty($requirement_overrides[SQ_INCOMPATIBLE]))
  613. $requirement_value_override = SQ_INCOMPATIBLE;
  614. else
  615. $requirement_value_override = $requirement_overrides[$requirement];
  616. }
  617. }
  618. }
  619. // now grab override if one is available
  620. //
  621. if (!is_null($requirement_value_override))
  622. $requirement_value = $requirement_value_override;
  623. }
  624. return $requirement_value;
  625. }
  626. /**
  627. * Get a plugin's other plugin dependencies.
  628. *
  629. * Determines and returns all the other plugins
  630. * that a given plugin requires, as well as the
  631. * minimum version numbers of the required plugins
  632. * and whether or not they need to be activated.
  633. *
  634. * By default, the desired plugin must be currently
  635. * activated, and if it is not, this function will
  636. * return FALSE. By overriding the default value
  637. * of $force_inclusion, this function will attempt
  638. * to grab dependency information from the given
  639. * plugin even if it is not activated (plugin still
  640. * has to be unpackaged and set in place in the
  641. * plugins directory). Use with care - some plugins
  642. * might break SquirrelMail when this is used.
  643. *
  644. * By turning on the $do_parse argument (it is on by
  645. * default), the version string for each required
  646. * plugin will be parsed by SquirrelMail into a
  647. * SquirrelMail-compatible version string (such as
  648. * "1.2.3") if it is not already. See notes about
  649. * version formatting under the get_plugin_version()
  650. * function documentation.
  651. *
  652. * @since 1.5.2
  653. *
  654. * @param string plugin_name name of the plugin to
  655. * check; must precisely
  656. * match the plugin
  657. * directory name
  658. * @param bool force_inclusion try to get version info
  659. * for plugins not activated?
  660. * (default FALSE)
  661. * @param bool do_parse return the version numbers
  662. * for required plugins in
  663. * SquirrelMail-compatible
  664. * format (default FALSE)
  665. *
  666. * @return mixed Boolean FALSE is returned if the plugin
  667. * could not be found or does not indicate
  668. * whether it has other plugin dependencies,
  669. * the constant SQ_INCOMPATIBLE is returned if
  670. * the given plugin is entirely incompatible
  671. * with the current SquirrelMail version,
  672. * otherwise an array is returned where keys
  673. * are the names of required plugin
  674. * dependencies, and values are arrays again,
  675. * where at least the following keys (and
  676. * corresponding values) will be available:
  677. * 'version' - value is the minimum version
  678. * required for that plugin (the format of
  679. * which might vary per the value of $do_parse),
  680. * 'activate' - value is boolean: TRUE indicates
  681. * that the plugin must also be activated, FALSE
  682. * means that it only needs to be present, but
  683. * does not need to be activated. Note that
  684. * the return value might be an empty array,
  685. * indicating that the plugin has no dependencies.
  686. *
  687. */
  688. function get_plugin_dependencies($plugin_name, $force_inclusion = FALSE,
  689. $do_parse = TRUE)
  690. {
  691. $plugin_dependencies = get_plugin_requirement($plugin_name,
  692. 'required_plugins',
  693. $force_inclusion);
  694. // the plugin is simply incompatible, no need to continue here
  695. //
  696. if ($plugin_dependencies === SQ_INCOMPATIBLE)
  697. return $plugin_dependencies;
  698. // not an array of requirements? wrong format, just return FALSE
  699. //
  700. if (!is_array($plugin_dependencies))
  701. return FALSE;
  702. // make sure everything is in order...
  703. //
  704. if (!empty($plugin_dependencies))
  705. {
  706. $new_plugin_dependencies = array();
  707. foreach ($plugin_dependencies as $plugin_name => $plugin_requirements)
  708. {
  709. // if $plugin_requirements isn't an array, this is old-style,
  710. // where only the version number was given...
  711. //
  712. if (is_string($plugin_requirements))
  713. $plugin_requirements = array('version' => $plugin_requirements,
  714. 'activate' => FALSE);
  715. // trap badly formatted requirements arrays that don't have
  716. // needed info
  717. //
  718. if (!is_array($plugin_requirements)
  719. || !isset($plugin_requirements['version']))
  720. continue;
  721. if (!isset($plugin_requirements['activate']))
  722. $plugin_requirements['activate'] = FALSE;
  723. // parse version into something we understand?
  724. //
  725. if ($do_parse)
  726. {
  727. // massage version number into something we understand
  728. //
  729. // the first regexp strips everything and anything that follows
  730. // the first occurance of a non-digit (or non decimal point), so
  731. // beware that putting letters in the middle of a version string
  732. // will effectively truncate the version string right there (but
  733. // this also just helps remove the SquirrelMail version part off
  734. // of versions such as "1.2.3-1.4.4")
  735. //
  736. // the second regexp just strips out non-digits/non-decimal points
  737. // (and might be redundant(?))
  738. //
  739. // the regexps are wrapped in a trim that makes sure the version
  740. // does not start or end with a decimal point
  741. //
  742. $plugin_requirements['version']
  743. = trim(preg_replace(array('/[^0-9.]+.*$/', '/[^0-9.]/'),
  744. '', $plugin_requirements['version']),
  745. '.');
  746. }
  747. $new_plugin_dependencies[$plugin_name] = $plugin_requirements;
  748. }
  749. $plugin_dependencies = $new_plugin_dependencies;
  750. }
  751. return $plugin_dependencies;
  752. }
  753. /**
  754. * Check a plugin's other plugin dependencies.
  755. *
  756. * Determines whether or not all of the given
  757. * plugin's required plugins are installed and
  758. * up to the proper version, and if they are
  759. * activated if required.
  760. *
  761. * By default, the desired plugin must be currently
  762. * activated, and if it is not, this function will
  763. * return FALSE. By overriding the default value
  764. * of $force_inclusion, this function will attempt
  765. * to grab dependency information from the given
  766. * plugin even if it is not activated (plugin still
  767. * has to be unpackaged and set in place in the
  768. * plugins directory). Use with care - some plugins
  769. * might break SquirrelMail when this is used.
  770. *
  771. * NOTE that if a plugin does not report whether or
  772. * not it has other plugin dependencies, this function
  773. * will return TRUE, although that is possibly incorrect
  774. * or misleading.
  775. *
  776. * @since 1.5.2
  777. *
  778. * @param string plugin_name name of the plugin to
  779. * check; must precisely
  780. * match the plugin
  781. * directory name
  782. * @param bool force_inclusion try to get version info
  783. * for plugins not activated?
  784. * (default FALSE)
  785. *
  786. * @return mixed Boolean TRUE if all of the plugin's
  787. * required plugins are correctly installed,
  788. * the constant SQ_INCOMPATIBLE is returned if
  789. * the given plugin is entirely incompatible
  790. * with the current SquirrelMail version,
  791. * otherwise an array of the required plugins
  792. * that are either not installed or not up to
  793. * the minimum required version. The array is
  794. * keyed by plugin name where values are arrays
  795. * again, where at least the following keys (and
  796. * corresponding values) will be available:
  797. * 'version' - value is the minimum version
  798. * required for that plugin (in printable, non-
  799. * parsed format), 'activate' - value is boolean:
  800. * TRUE indicates that the plugin must also be
  801. * activated, FALSE means that it only needs to
  802. * be present, but does not need to be activated.
  803. *
  804. */
  805. function check_plugin_dependencies($plugin_name, $force_inclusion = FALSE)
  806. {
  807. $dependencies = get_plugin_dependencies($plugin_name, $force_inclusion);
  808. if (!$dependencies) return TRUE;
  809. if ($dependencies === SQ_INCOMPATIBLE) return $dependencies;
  810. $missing_or_bad = array();
  811. foreach ($dependencies as $depend_name => $depend_requirements)
  812. {
  813. $version = explode('.', $depend_requirements['version'], 3);
  814. $version[0] = intval($version[0]);
  815. $version[1] = intval($version[1]);
  816. $version[2] = intval($version[2]);
  817. $force_dependency_inclusion = !$depend_requirements['activate'];
  818. if (!check_plugin_version($depend_name, $version[0], $version[1],
  819. $version[2], $force_dependency_inclusion))
  820. $missing_or_bad[$depend_name] = $depend_requirements;
  821. }
  822. if (empty($missing_or_bad)) return TRUE;
  823. // get non-parsed required versions
  824. //
  825. $non_parsed_dependencies = get_plugin_dependencies($plugin_name,
  826. $force_inclusion,
  827. FALSE);
  828. $return_array = array();
  829. foreach ($missing_or_bad as $depend_name => $ignore)
  830. $return_array[$depend_name] = $non_parsed_dependencies[$depend_name];
  831. return $return_array;
  832. }