| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * plugin.php |
| 5 | * |
| 6 | * This file provides the framework for a plugin architecture. |
| 7 | * |
| 8 | * Documentation on how to write plugins might show up some time. |
| 9 | * |
| 10 | * @copyright © 1999-2007 The SquirrelMail Project Team |
| 11 | * @license http://opensource.org/licenses/gpl-license.php GNU Public License |
| 12 | * @version $Id$ |
| 13 | * @package squirrelmail |
| 14 | */ |
| 15 | |
| 16 | /** |
| 17 | * This function adds a plugin. |
| 18 | * @param string $name Internal plugin name (ie. delete_move_next) |
| 19 | * @return void |
| 20 | */ |
| 21 | function use_plugin ($name) { |
| 22 | if (file_exists(SM_PATH . "plugins/$name/setup.php")) { |
| 23 | include_once(SM_PATH . "plugins/$name/setup.php"); |
| 24 | |
| 25 | /** |
| 26 | * As of SM 1.5.2, plugin hook registration is statically |
| 27 | * accomplished using the configuration utility (config/conf.pl). |
| 28 | * And this code is deprecated (but let's keep it until |
| 29 | * the new registration system is proven). |
| 30 | * |
| 31 | */ |
| 32 | //$function = "squirrelmail_plugin_init_$name"; |
| 33 | //if (function_exists($function)) { |
| 34 | // $function(); |
| 35 | //} |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * This function executes a plugin hook. |
| 41 | * |
| 42 | * It includes an arbitrary return value that is managed by |
| 43 | * all plugins on the same hook and returned to the core hook |
| 44 | * location. |
| 45 | * |
| 46 | * The desired format of the return value should be defined |
| 47 | * by the context in which the hook is called. |
| 48 | * |
| 49 | * Note that the master return value for this hook is passed |
| 50 | * to each plugin after the main argument(s) value/array as a |
| 51 | * convenience only - to show what the current return value is |
| 52 | * even though it is liable to be changed by other plugins. |
| 53 | * |
| 54 | * If any plugin on this hook wants to modify the $args |
| 55 | * plugin parameter, it simply has to use call-by-reference |
| 56 | * syntax in the hook function that it has registered for the |
| 57 | * current hook. Note that this is in addition to (entirely |
| 58 | * independent of) the return value for this hook. |
| 59 | * |
| 60 | * @param string $name Name of hook being executed |
| 61 | * @param mixed $args A single value or an array of arguments |
| 62 | * that are to be passed to all plugins |
| 63 | * operating off the hook being called. |
| 64 | * Note that this argument is passed by |
| 65 | * reference thus it is liable to be |
| 66 | * changed after the hook completes. |
| 67 | * |
| 68 | * @return mixed The return value that is managed by the plugins |
| 69 | * on the current hook. |
| 70 | * |
| 71 | */ |
| 72 | function do_hook($name, &$args) { |
| 73 | |
| 74 | global $squirrelmail_plugin_hooks, $currentHookName; |
| 75 | $currentHookName = $name; |
| 76 | $ret = NULL; |
| 77 | |
| 78 | if (isset($squirrelmail_plugin_hooks[$name]) |
| 79 | && is_array($squirrelmail_plugin_hooks[$name])) { |
| 80 | foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) { |
| 81 | use_plugin($plugin_name); |
| 82 | if (function_exists($function)) { |
| 83 | $ret = $function($args, $ret); |
| 84 | |
| 85 | // each plugin can call additional hooks, so need |
| 86 | // to make sure the current hook name is accurate |
| 87 | // again after each plugin has finished |
| 88 | // |
| 89 | $currentHookName = $name; |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | $currentHookName = ''; |
| 95 | return $ret; |
| 96 | |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * This function executes a hook that allows for an arbitrary |
| 101 | * return value from each plugin that will be merged into one |
| 102 | * array (or one string if all return values are strings) and |
| 103 | * returned to the core hook location. |
| 104 | * |
| 105 | * Note that unlike PHP's array_merge function, matching array keys |
| 106 | * will not overwrite each other, instead, values under such keys |
| 107 | * will be concatenated if they are both strings, or merged if they |
| 108 | * are arrays (in the same (non-overwrite) manner recursively). |
| 109 | * |
| 110 | * Plugins returning non-arrays (strings, objects, etc) will have |
| 111 | * their output added to the end of the ultimate return array, |
| 112 | * unless ALL values returned are strings, in which case one string |
| 113 | * with all returned strings concatenated together is returned |
| 114 | * (unless $force_array is TRUE). |
| 115 | * |
| 116 | * If any plugin on this hook wants to modify the $args |
| 117 | * plugin parameter, it simply has to use call-by-reference |
| 118 | * syntax in the hook function that it has registered for the |
| 119 | * current hook. Note that this is in addition to (entirely |
| 120 | * independent of) the return value for this hook. |
| 121 | * |
| 122 | * @param string $name Name of hook being executed |
| 123 | * @param mixed $args A single value or an array of arguments |
| 124 | * that are to be passed to all plugins |
| 125 | * operating off the hook being called. |
| 126 | * Note that this argument is passed by |
| 127 | * reference thus it is liable to be |
| 128 | * changed after the hook completes. |
| 129 | * @param boolean $force_array When TRUE, guarantees the return |
| 130 | * value will ALWAYS be an array, |
| 131 | * (simple strings will be forced |
| 132 | * into a one-element array). |
| 133 | * When FALSE, behavior is as |
| 134 | * described above (OPTIONAL; |
| 135 | * default behavior is to return |
| 136 | * mixed - array or string). |
| 137 | * |
| 138 | * @return mixed the merged return arrays or strings of each |
| 139 | * plugin on this hook. |
| 140 | * |
| 141 | */ |
| 142 | function concat_hook_function($name, &$args, $force_array=FALSE) { |
| 143 | |
| 144 | global $squirrelmail_plugin_hooks, $currentHookName; |
| 145 | $currentHookName = $name; |
| 146 | $ret = ''; |
| 147 | |
| 148 | if (isset($squirrelmail_plugin_hooks[$name]) |
| 149 | && is_array($squirrelmail_plugin_hooks[$name])) { |
| 150 | foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) { |
| 151 | use_plugin($plugin_name); |
| 152 | if (function_exists($function)) { |
| 153 | $plugin_ret = $function($args); |
| 154 | if (!empty($plugin_ret)) { |
| 155 | $ret = sqm_array_merge($ret, $plugin_ret); |
| 156 | } |
| 157 | |
| 158 | // each plugin can call additional hooks, so need |
| 159 | // to make sure the current hook name is accurate |
| 160 | // again after each plugin has finished |
| 161 | // |
| 162 | $currentHookName = $name; |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | if ($force_array && is_string($ret)) { |
| 168 | $ret = array($ret); |
| 169 | } |
| 170 | |
| 171 | $currentHookName = ''; |
| 172 | return $ret; |
| 173 | |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * This function is used for hooks which are to return true or |
| 178 | * false. If $priority is > 0, any one or more trues will override |
| 179 | * any falses. If $priority < 0, then one or more falses will |
| 180 | * override any trues. |
| 181 | * Priority 0 means majority rules. Ties will be broken with $tie |
| 182 | * |
| 183 | * If any plugin on this hook wants to modify the $args |
| 184 | * plugin parameter, it simply has to use call-by-reference |
| 185 | * syntax in the hook function that it has registered for the |
| 186 | * current hook. Note that this is in addition to (entirely |
| 187 | * independent of) the return value for this hook. |
| 188 | * |
| 189 | * @param string $name The hook name |
| 190 | * @param mixed $args A single value or an array of arguments |
| 191 | * that are to be passed to all plugins |
| 192 | * operating off the hook being called. |
| 193 | * Note that this argument is passed by |
| 194 | * reference thus it is liable to be |
| 195 | * changed after the hook completes. |
| 196 | * @param int $priority See explanation above |
| 197 | * @param boolean $tie See explanation above |
| 198 | * |
| 199 | * @return boolean The result of the function |
| 200 | * |
| 201 | */ |
| 202 | function boolean_hook_function($name, &$args, $priority=0, $tie=false) { |
| 203 | |
| 204 | global $squirrelmail_plugin_hooks, $currentHookName; |
| 205 | $yea = 0; |
| 206 | $nay = 0; |
| 207 | $ret = $tie; |
| 208 | |
| 209 | if (isset($squirrelmail_plugin_hooks[$name]) && |
| 210 | is_array($squirrelmail_plugin_hooks[$name])) { |
| 211 | |
| 212 | /* Loop over the plugins that registered the hook */ |
| 213 | $currentHookName = $name; |
| 214 | foreach ($squirrelmail_plugin_hooks[$name] as $plugin_name => $function) { |
| 215 | use_plugin($plugin_name); |
| 216 | if (function_exists($function)) { |
| 217 | $ret = $function($args); |
| 218 | if ($ret) { |
| 219 | $yea++; |
| 220 | } else { |
| 221 | $nay++; |
| 222 | } |
| 223 | |
| 224 | // each plugin can call additional hooks, so need |
| 225 | // to make sure the current hook name is accurate |
| 226 | // again after each plugin has finished |
| 227 | // |
| 228 | $currentHookName = $name; |
| 229 | } |
| 230 | } |
| 231 | $currentHookName = ''; |
| 232 | |
| 233 | /* Examine the aftermath and assign the return value appropriately */ |
| 234 | if (($priority > 0) && ($yea)) { |
| 235 | $ret = true; |
| 236 | } elseif (($priority < 0) && ($nay)) { |
| 237 | $ret = false; |
| 238 | } elseif ($yea > $nay) { |
| 239 | $ret = true; |
| 240 | } elseif ($nay > $yea) { |
| 241 | $ret = false; |
| 242 | } else { |
| 243 | // There's a tie, no action needed. |
| 244 | } |
| 245 | return $ret; |
| 246 | } |
| 247 | // If the code gets here, there was a problem - no hooks, etc. |
| 248 | return NULL; |
| 249 | |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * Do not use, use checkForJavascript() instead. |
| 254 | * |
| 255 | * This function checks whether the user's USER_AGENT is known to |
| 256 | * be broken. If so, returns true and the plugin is invisible to the |
| 257 | * offending browser. |
| 258 | * *** THIS IS A TEST FOR JAVASCRIPT SUPPORT *** |
| 259 | * |
| 260 | * @return bool whether this browser properly supports JavaScript |
| 261 | * @deprecated use checkForJavascript() since 1.5.1 |
| 262 | */ |
| 263 | function soupNazi(){ |
| 264 | return !checkForJavascript(); |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Check if plugin is enabled |
| 269 | * @param string $plugin_name plugin name |
| 270 | * @since 1.5.1 |
| 271 | * @return boolean |
| 272 | */ |
| 273 | function is_plugin_enabled($plugin_name) { |
| 274 | global $plugins; |
| 275 | |
| 276 | /** |
| 277 | * check if variable is empty. if var is not set, php empty |
| 278 | * returns true without error notice. |
| 279 | * |
| 280 | * then check if it is an array |
| 281 | */ |
| 282 | if (empty($plugins) || ! is_array($plugins)) |
| 283 | return false; |
| 284 | |
| 285 | if ( in_array($plugin_name,$plugins) ) { |
| 286 | return true; |
| 287 | } else { |
| 288 | return false; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Get a plugin's version. |
| 294 | * |
| 295 | * Determines and returns a plugin's version. |
| 296 | * |
| 297 | * By default, the desired plugin must be currently |
| 298 | * activated, and if it is not, this function will |
| 299 | * return FALSE. By overriding the default value |
| 300 | * of $force_inclusion, this function will attempt |
| 301 | * to grab versioning information from the given |
| 302 | * plugin even if it is not activated (plugin still |
| 303 | * has to be unpackaged and set in place in the |
| 304 | * plugins directory). Use with care - some plugins |
| 305 | * might break SquirrelMail when this is used. |
| 306 | * |
| 307 | * By turning on the $do_parse argument, the version |
| 308 | * string will be parsed by SquirrelMail into a |
| 309 | * SquirrelMail-compatible version string (such as |
| 310 | * "1.2.3") if it is not already. |
| 311 | * |
| 312 | * Note that this assumes plugin versioning is |
| 313 | * consistently applied in the same fashion that |
| 314 | * SquirrelMail versions are, with the exception that |
| 315 | * an applicable SquirrelMail version may be appended |
| 316 | * to the version number (which will be ignored herein). |
| 317 | * That is, plugin version number schemes are expected |
| 318 | * in the following format: 1.2.3, or 1.2.3-1.4.0. |
| 319 | * |
| 320 | * Any characters after the third version number |
| 321 | * indicating things such as beta or release candidate |
| 322 | * versions are discarded, so formats such as the |
| 323 | * following will also work, although extra information |
| 324 | * about beta versions can possibly confuse the desired |
| 325 | * results of the version check: 1.2.3-beta4, 1.2.3.RC2, |
| 326 | * and so forth. |
| 327 | * |
| 328 | * @since 1.5.2 |
| 329 | * |
| 330 | * @param string plugin_name name of the plugin to |
| 331 | * check; must precisely |
| 332 | * match the plugin |
| 333 | * directory name |
| 334 | * @param bool force_inclusion try to get version info |
| 335 | * for plugins not activated? |
| 336 | * (default FALSE) |
| 337 | * @param bool do_parse return the plugin version |
| 338 | * in SquirrelMail-compatible |
| 339 | * format (default FALSE) |
| 340 | * |
| 341 | * @return mixed The plugin version string if found, otherwise, |
| 342 | * boolean FALSE is returned indicating that no |
| 343 | * version information could be found for the plugin. |
| 344 | * |
| 345 | */ |
| 346 | function get_plugin_version($plugin_name, $force_inclusion = FALSE, $do_parse = FALSE) |
| 347 | { |
| 348 | |
| 349 | $info_function = $plugin_name . '_info'; |
| 350 | $version_function = $plugin_name . '_version'; |
| 351 | $plugin_info = array(); |
| 352 | $plugin_version = FALSE; |
| 353 | |
| 354 | |
| 355 | // first attempt to find the plugin info function, wherein |
| 356 | // the plugin version should be available |
| 357 | // |
| 358 | if (function_exists($info_function)) |
| 359 | $plugin_info = $info_function(); |
| 360 | else if ($force_inclusion |
| 361 | && file_exists(SM_PATH . 'plugins/' . $plugin_name . '/setup.php')) |
| 362 | { |
| 363 | include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php'); |
| 364 | if (function_exists($info_function)) |
| 365 | $plugin_info = $info_function(); |
| 366 | } |
| 367 | if (!empty($plugin_info['version'])) |
| 368 | $plugin_version = $plugin_info['version']; |
| 369 | |
| 370 | |
| 371 | // otherwise, look for older version function |
| 372 | // |
| 373 | if (!$plugin_version && function_exists($version_function)) |
| 374 | $plugin_version = $version_function(); |
| 375 | |
| 376 | |
| 377 | if ($plugin_version && $do_parse) |
| 378 | { |
| 379 | |
| 380 | // massage version number into something we understand |
| 381 | // |
| 382 | // the first regexp strips everything and anything that follows |
| 383 | // the first occurance of a non-digit (or non decimal point), so |
| 384 | // beware that putting letters in the middle of a version string |
| 385 | // will effectively truncate the version string right there (but |
| 386 | // this also just helps remove the SquirrelMail version part off |
| 387 | // of versions such as "1.2.3-1.4.4") |
| 388 | // |
| 389 | // the second regexp just strips out non-digits/non-decimal points |
| 390 | // (and might be redundant(?)) |
| 391 | // |
| 392 | // the regexps are wrapped in a trim that makes sure the version |
| 393 | // does not start or end with a decimal point |
| 394 | // |
| 395 | $plugin_version = trim(preg_replace(array('/[^0-9.]+.*$/', '/[^0-9.]/'), |
| 396 | '', $plugin_version), |
| 397 | '.'); |
| 398 | |
| 399 | } |
| 400 | |
| 401 | return $plugin_version; |
| 402 | |
| 403 | } |
| 404 | |
| 405 | /** |
| 406 | * Check a plugin's version. |
| 407 | * |
| 408 | * Returns TRUE if the given plugin is installed, |
| 409 | * activated and is at minimum version $a.$b.$c. |
| 410 | * If any one of those conditions fails, FALSE |
| 411 | * will be returned (careful of plugins that are |
| 412 | * sufficiently versioned but are not activated). |
| 413 | * |
| 414 | * By overriding the default value of $force_inclusion, |
| 415 | * this function will attempt to grab versioning |
| 416 | * information from the given plugin even if it |
| 417 | * is not activated (the plugin still has to be |
| 418 | * unpackaged and set in place in the plugins |
| 419 | * directory). Use with care - some plugins |
| 420 | * might break SquirrelMail when this is used. |
| 421 | * |
| 422 | * Note that this function assumes plugin |
| 423 | * versioning is consistently applied in the same |
| 424 | * fashion that SquirrelMail versions are, with the |
| 425 | * exception that an applicable SquirrelMail |
| 426 | * version may be appended to the version number |
| 427 | * (which will be ignored herein). That is, plugin |
| 428 | * version number schemes are expected in the following |
| 429 | * format: 1.2.3, or 1.2.3-1.4.0. |
| 430 | * |
| 431 | * Any characters after the third number indicating |
| 432 | * things such as beta or release candidate versions |
| 433 | * are discarded, so formats such as the following |
| 434 | * will also work, although extra information about |
| 435 | * beta versions can possibly confuse the desired results |
| 436 | * of the version check: 1.2.3-beta4, 1.2.3.RC2, and so forth. |
| 437 | * |
| 438 | * @since 1.5.2 |
| 439 | * |
| 440 | * @param string plugin_name name of the plugin to |
| 441 | * check; must precisely |
| 442 | * match the plugin |
| 443 | * directory name |
| 444 | * @param int a major version number |
| 445 | * @param int b minor version number |
| 446 | * @param int c release number |
| 447 | * @param bool force_inclusion try to get version info |
| 448 | * for plugins not activated? |
| 449 | * (default FALSE) |
| 450 | * |
| 451 | * @return bool |
| 452 | * |
| 453 | */ |
| 454 | function check_plugin_version($plugin_name, |
| 455 | $a = 0, $b = 0, $c = 0, |
| 456 | $force_inclusion = FALSE) |
| 457 | { |
| 458 | |
| 459 | $plugin_version = get_plugin_version($plugin_name, $force_inclusion, TRUE); |
| 460 | if (!$plugin_version) return FALSE; |
| 461 | |
| 462 | |
| 463 | // split the version string into sections delimited by |
| 464 | // decimal points, and make sure we have three sections |
| 465 | // |
| 466 | $plugin_version = explode('.', $plugin_version); |
| 467 | if (!isset($plugin_version[0])) $plugin_version[0] = 0; |
| 468 | if (!isset($plugin_version[1])) $plugin_version[1] = 0; |
| 469 | if (!isset($plugin_version[2])) $plugin_version[2] = 0; |
| 470 | // sm_print_r($plugin_version); |
| 471 | |
| 472 | |
| 473 | // now test the version number |
| 474 | // |
| 475 | if ($plugin_version[0] < $a || |
| 476 | ($plugin_version[0] == $a && $plugin_version[1] < $b) || |
| 477 | ($plugin_version[0] == $a && $plugin_version[1] == $b && $plugin_version[2] < $c)) |
| 478 | return FALSE; |
| 479 | |
| 480 | |
| 481 | return TRUE; |
| 482 | |
| 483 | } |
| 484 | |