Source for file plugin.php

Documentation is available at plugin.php

  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-2020 The SquirrelMail Project Team
  11.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  12.  * @version $Id: plugin.php 14845 2020-01-07 08:09:34Z pdontthink $
  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[$nameas $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[$nameas $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[$nameas $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.  
  364.       /* --- Old code, keeping just in case... problem with it is, for example,
  365.          if it is used, but later we are checking if the same plugin is
  366.          activated (because it SHOULD be), this code having run will possibly 
  367.          create a false positive. 
  368.       include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  369.       if (function_exists($info_function))
  370.          $plugin_info = $info_function();
  371.       --- */
  372.  
  373.       // so what we need to do is process this plugin without
  374.       // it polluting our environment
  375.       //
  376.       // we *could* just use the above code, which is more of a
  377.       // sure thing than some regular expressions, and then test
  378.       // the contents of the $plugins array to see if this plugin
  379.       // is actually activated, and that might be good enough, but
  380.       // for now, we'll use the following approach, because of two
  381.       // concerns: other plugins and other templates might force
  382.       // the inclusion of a plugin (which SHOULD also add it to 
  383.       // the $plugins array, but am not 100% sure at this time (FIXME)),
  384.       // and because the regexps below should work just fine with
  385.       // any resonably formatted plugin setup file.
  386.       //
  387.       // read the target plugin's setup.php file into a string,
  388.       // then use a regular expression to try to find the version...
  389.       // this of course can break if plugin authors do funny things
  390.       // with their file formatting
  391.       //
  392.       $setup_file '';
  393.       $file_contents file(SM_PATH 'plugins/' $plugin_name '/setup.php');
  394.       foreach ($file_contents as $line)
  395.          $setup_file .= $line;
  396.  
  397.  
  398.       // this regexp grabs a version number from a standard 
  399.       // <plugin>_info() function
  400.       //
  401.       if (preg_match('/[\'"]version[\'"]\s*=>\s*[\'"](.+?)[\'"]/is'$setup_file$matches))
  402.          $plugin_info array('version' => $matches[1]);
  403.  
  404.  
  405.       // this regexp grabs a version number from a standard 
  406.       // (deprecated) <plugin>_version() function
  407.       //
  408.       else if (preg_match('/function\s+.*?' $plugin_name '_version.*?\(.*?\).*?\{.*?return\s+[\'"](.+?)[\'"]/is'$setup_file$matches))
  409.          $plugin_info array('version' => $matches[1]);
  410.  
  411.    }
  412.    if (!empty($plugin_info['version']))
  413.       $plugin_version $plugin_info['version'];
  414.  
  415.  
  416.    // otherwise, look for older version function
  417.    //
  418.    if (!$plugin_version && function_exists($version_function))
  419.        $plugin_version $version_function();
  420.  
  421.  
  422.    if ($plugin_version && $do_parse)
  423.    {
  424.  
  425.       // massage version number into something we understand
  426.       //
  427.       // the first regexp strips everything and anything that follows
  428.       // the first occurance of a non-digit (or non decimal point), so
  429.       // beware that putting letters in the middle of a version string
  430.       // will effectively truncate the version string right there (but
  431.       // this also just helps remove the SquirrelMail version part off
  432.       // of versions such as "1.2.3-1.4.4")
  433.       //
  434.       // the second regexp just strips out non-digits/non-decimal points
  435.       // (and might be redundant(?))
  436.       //
  437.       // the regexps are wrapped in a trim that makes sure the version
  438.       // does not start or end with a decimal point
  439.       //
  440.       $plugin_version trim(preg_replace(array('/[^0-9.]+.*$/''/[^0-9.]/'),
  441.                                           ''$plugin_version),
  442.                              '.');
  443.  
  444.    }
  445.  
  446.    return $plugin_version;
  447.  
  448. }
  449.  
  450. /**
  451.   * Check a plugin's version.
  452.   *
  453.   * Returns TRUE if the given plugin is installed,
  454.   * activated and is at minimum version $a.$b.$c.
  455.   * If any one of those conditions fails, FALSE
  456.   * will be returned (careful of plugins that are
  457.   * sufficiently versioned but are not activated).
  458.   *
  459.   * By overriding the default value of $force_inclusion,
  460.   * this function will attempt to grab versioning
  461.   * information from the given plugin even if it
  462.   * is not activated (the plugin still has to be
  463.   * unpackaged and set in place in the plugins
  464.   * directory).  Use with care - some plugins
  465.   * might break SquirrelMail when this is used.
  466.   *
  467.   * Note that this function assumes plugin
  468.   * versioning is consistently applied in the same
  469.   * fashion that SquirrelMail versions are, with the
  470.   * exception that an applicable SquirrelMail
  471.   * version may be appended to the version number
  472.   * (which will be ignored herein).  That is, plugin
  473.   * version number schemes are expected in the following
  474.   * format:  1.2.3, or 1.2.3-1.4.0.
  475.   *
  476.   * Any characters after the third number indicating
  477.   * things such as beta or release candidate versions
  478.   * are discarded, so formats such as the following
  479.   * will also work, although extra information about
  480.   * beta versions can possibly confuse the desired results
  481.   * of the version check:  1.2.3-beta4, 1.2.3.RC2, and so forth.
  482.   * 
  483.   * @since 1.5.2
  484.   *
  485.   * @param string plugin_name   Name of the plugin to
  486.   *                              check; must precisely
  487.   *                              match the plugin
  488.   *                              directory name
  489.   * @param int  a               Major version number
  490.   * @param int  b               Minor version number
  491.   * @param int  c               Release number
  492.   * @param bool force_inclusion Try to get version info
  493.   *                              for plugins not activated?
  494.   *                              (default FALSE)
  495.   *
  496.   * @return bool 
  497.   *
  498.   */
  499. function check_plugin_version($plugin_name
  500.                               $a 0$b 0$c 0
  501.                               $force_inclusion FALSE)
  502. {
  503.  
  504.    $plugin_version get_plugin_version($plugin_name$force_inclusionTRUE);
  505.    if (!$plugin_versionreturn FALSE;
  506.  
  507.  
  508.    // split the version string into sections delimited by 
  509.    // decimal points, and make sure we have three sections
  510.    //
  511.    $plugin_version explode('.'$plugin_version);
  512.    if (!isset($plugin_version[0])) $plugin_version[00;
  513.    if (!isset($plugin_version[1])) $plugin_version[10;
  514.    if (!isset($plugin_version[2])) $plugin_version[20;
  515. //   sm_print_r($plugin_version);
  516.  
  517.  
  518.    // now test the version number
  519.    //
  520.    if ($plugin_version[0$a ||
  521.       ($plugin_version[0== $a && $plugin_version[1$b||
  522.       ($plugin_version[0== $a && $plugin_version[1== $b && $plugin_version[2$c))
  523.          return FALSE;
  524.  
  525.  
  526.    return TRUE;
  527.  
  528. }
  529.  
  530. /**
  531.   * Get a certain plugin requirement.
  532.   *
  533.   * Attempts to find the given plugin requirement value
  534.   * in the given plugin's informational array, and returns
  535.   * it or NULL if it was not found.
  536.   *
  537.   * Some plugins have different values for the same
  538.   * requirement depending on the SquirrelMail version,
  539.   * and this function is smart enough to take that into
  540.   * account.
  541.   *
  542.   * By default, the desired plugin must be currently
  543.   * activated, and if it is not, this function will
  544.   * return NULL.  By overriding the default value
  545.   * of $force_inclusion, this function will attempt
  546.   * to grab requirement information from the given
  547.   * plugin even if it is not activated (plugin still
  548.   * has to be unpackaged and set in place in the
  549.   * plugins directory).  Use with care - some plugins
  550.   * might break SquirrelMail when this is used.
  551.   * 
  552.   * @since 1.5.2
  553.   *
  554.   * @param string  $plugin_name         Name of the plugin to
  555.   *                                      check; must precisely
  556.   *                                      match the plugin
  557.   *                                      directory name
  558.   * @param string  $requirement         The desired requirement name
  559.   * @param boolean $ignore_incompatible When TRUE, version incompatibility
  560.   *                                      information will NOT be returned
  561.   *                                      if found; when FALSE, it will be
  562.   *                                      (OPTIONAL; default TRUE)
  563.   * @param boolean $force_inclusion     Try to get requirement info
  564.   *                                      for plugins not activated?
  565.   *                                      (OPTIONAL; default FALSE)
  566.   *
  567.   * @return mixed NULL is returned if the plugin could not be
  568.   *                found or does not include the given requirement,
  569.   *                the constant SQ_INCOMPATIBLE is returned if the
  570.   *                given plugin is entirely incompatible with the
  571.   *                current SquirrelMail version (unless
  572.   *                $ignore_incompatible is TRUE), otherwise the
  573.   *                value of the requirement is returned, whatever
  574.   *                that may be (varies per requirement type).
  575.   *
  576.   */
  577. function get_plugin_requirement($plugin_name$requirement
  578.                                 $ignore_incompatible TRUE,
  579.                                 $force_inclusion FALSE)
  580. {
  581.  
  582.    $info_function $plugin_name '_info';
  583.    $plugin_info array();
  584.    $requirement_value NULL;
  585.  
  586.  
  587.    // first attempt to find the plugin info function, wherein
  588.    // the plugin requirements should be available
  589.    //
  590.    if (function_exists($info_function))
  591.       $plugin_info $info_function();
  592.    else if ($force_inclusion 
  593.     && file_exists(SM_PATH 'plugins/' $plugin_name '/setup.php'))
  594.    {
  595.  
  596.       /* --- Old code, keeping just in case... problem with it is, for example,
  597.          if it is used, but later we are checking if the same plugin is
  598.          activated (because it SHOULD be), this code having run will possibly
  599.          create a false positive.
  600.       include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
  601.       if (function_exists($info_function))
  602.          $plugin_info = $info_function();
  603.       --- */
  604.  
  605.       // so what we need to do is process this plugin without
  606.       // it polluting our environment
  607.       //
  608.       // we *could* just use the above code, which is more of a
  609.       // sure thing than a regular expression, and then test
  610.       // the contents of the $plugins array to see if this plugin
  611.       // is actually activated, and that might be good enough, but
  612.       // for now, we'll use the following approach, because of two
  613.       // concerns: other plugins and other templates might force
  614.       // the inclusion of a plugin (which SHOULD also add it to
  615.       // the $plugins array, but am not 100% sure at this time (FIXME)),
  616.       // and because the regexp below should work just fine with
  617.       // any resonably formatted plugin setup file.
  618.       //
  619.       // read the target plugin's setup.php file into a string,
  620.       // then use a regular expression to try to find the needed
  621.       // requirement information...
  622.       // this of course can break if plugin authors do funny things
  623.       // with their file formatting
  624.       //
  625.       $setup_file '';
  626.       $file_contents file(SM_PATH 'plugins/' $plugin_name '/setup.php');
  627.       foreach ($file_contents as $line
  628.          $setup_file .= $line;
  629.  
  630.  
  631.       // this regexp grabs the full plugin info array from a standard 
  632.       // <plugin>_info() function... determining the end of the info 
  633.       // array can fail, but if authors end the array with ");\n"
  634.       // (without quotes), then it should work well, especially because 
  635.       // newlines shouldn't be found inside the array after any ");" 
  636.       // (without quotes)
  637.       //
  638.       if (preg_match('/function\s+.*?' $plugin_name '_info.*?\(.*?\).*?\{.*?(array.+?\)\s*;)\s*' "\n" '/is'$setup_file$matches))
  639.          eval('$plugin_info = ' $matches[1]);
  640.  
  641.    }
  642.  
  643.  
  644.    // attempt to get the requirement from the "global" scope 
  645.    // of the plugin information array
  646.    //
  647.    if (isset($plugin_info[$requirement])
  648.     && !is_null($plugin_info[$requirement]))
  649.       $requirement_value $plugin_info[$requirement];
  650.  
  651.  
  652.    // now, if there is a series of per-version requirements, 
  653.    // check there too
  654.    //
  655.    if (!empty($plugin_info['per_version_requirements']
  656.     && is_array($plugin_info['per_version_requirements']))
  657.    {
  658.  
  659.       // iterate through requirements, where keys are version
  660.       // numbers -- tricky part is knowing the difference between
  661.       // more than one version for which the current SM installation
  662.       // passes the check_sm_version() test... we want the highest one
  663.       //
  664.       $requirement_value_override NULL;
  665.       $highest_version_array array();
  666.       foreach ($plugin_info['per_version_requirements'as $version => $requirement_overrides)
  667.       {
  668.  
  669.          $version_array explode('.'$version);
  670.          if (sizeof($version_array!= 3continue;
  671.  
  672.          $a $version_array[0];
  673.          $b $version_array[1];
  674.          $c $version_array[2];
  675.  
  676.          // complicated way to say we are interested in these overrides
  677.          // if the version is applicable to us and if the overrides include
  678.          // the requirement we are looking for, or if the plugin is not
  679.          // compatible with this version of SquirrelMail (unless we are
  680.          // told to ignore such)
  681.          // 
  682.          if (check_sm_version($a$b$c
  683.           && ((!$ignore_incompatible
  684.             && (!empty($requirement_overrides[SQ_INCOMPATIBLE]
  685.              || $requirement_overrides === SQ_INCOMPATIBLE))
  686.            || (is_array($requirement_overrides)
  687.             && isset($requirement_overrides[$requirement])
  688.             && !is_null($requirement_overrides[$requirement]))))
  689.          {
  690.  
  691.             if (empty($highest_version_array)
  692.              || $highest_version_array[0$a
  693.              || ($highest_version_array[0== $a
  694.              && $highest_version_array[1$b)
  695.              || ($highest_version_array[0== $a 
  696.              && $highest_version_array[1== $b 
  697.              && $highest_version_array[2$c))
  698.             {
  699.                $highest_version_array $version_array;
  700.                if (!empty($requirement_overrides[SQ_INCOMPATIBLE])
  701.                 || $requirement_overrides === SQ_INCOMPATIBLE)
  702.                   $requirement_value_override SQ_INCOMPATIBLE;
  703.                else
  704.                   $requirement_value_override $requirement_overrides[$requirement];
  705.             }
  706.  
  707.          }
  708.  
  709.       }
  710.  
  711.       // now grab override if one is available
  712.       //
  713.       if (!is_null($requirement_value_override))
  714.          $requirement_value $requirement_value_override;
  715.  
  716.    }
  717.  
  718.    return $requirement_value;
  719.  
  720. }
  721.  
  722. /**
  723.   * Get a plugin's other plugin dependencies.
  724.   *
  725.   * Determines and returns all the other plugins
  726.   * that a given plugin requires, as well as the
  727.   * minimum version numbers of the required plugins
  728.   * and whether or not they need to be activated.
  729.   *
  730.   * By default, the desired plugin must be currently
  731.   * activated, and if it is not, this function will
  732.   * return FALSE.  By overriding the default value
  733.   * of $force_inclusion, this function will attempt
  734.   * to grab dependency information from the given
  735.   * plugin even if it is not activated (plugin still
  736.   * has to be unpackaged and set in place in the
  737.   * plugins directory).  Use with care - some plugins
  738.   * might break SquirrelMail when this is used.
  739.   * 
  740.   * By turning on the $do_parse argument (it is on by
  741.   * default), the version string for each required
  742.   * plugin will be parsed by SquirrelMail into a
  743.   * SquirrelMail-compatible version string (such as
  744.   * "1.2.3") if it is not already.  See notes about
  745.   * version formatting under the get_plugin_version()
  746.   * function documentation.
  747.   *
  748.   * @since 1.5.2
  749.   *
  750.   * @param string plugin_name   name of the plugin to
  751.   *                              check; must precisely
  752.   *                              match the plugin
  753.   *                              directory name
  754.   * @param bool force_inclusion try to get version info
  755.   *                              for plugins not activated?
  756.   *                              (default FALSE)
  757.   * @param bool do_parse        return the version numbers
  758.   *                              for required plugins in
  759.   *                              SquirrelMail-compatible
  760.   *                              format (default FALSE)
  761.   *
  762.   * @return mixed Boolean FALSE is returned if the plugin
  763.   *                could not be found or does not indicate
  764.   *                whether it has other plugin dependencies,
  765.   *                the constant SQ_INCOMPATIBLE is returned if
  766.   *                the given plugin is entirely incompatible
  767.   *                with the current SquirrelMail version,
  768.   *                otherwise an array is returned where keys
  769.   *                are the names of required plugin
  770.   *                dependencies, and values are arrays again,
  771.   *                where at least the following keys (and
  772.   *                corresponding values) will be available:
  773.   *                'version' - value is the minimum version
  774.   *                required for that plugin (the format of
  775.   *                which might vary per the value of $do_parse
  776.   *                as well as if the plugin requires a SquirrelMail
  777.   *                core plugin, in which case it is "CORE" or
  778.   *                "CORE:1.5.2" or similar, or, if the plugin is
  779.   *                actually incompatible (not required) with this
  780.   *                one, the constant SQ_INCOMPATIBLE will be found
  781.   *                here), 'activate' - value is boolean: TRUE
  782.   *                indicates that the plugin must also be activated,
  783.   *                FALSE means that it only needs to be present,
  784.   *                but does not need to be activated.  Note that
  785.   *                the return value might be an empty array,
  786.   *                indicating that the plugin has no dependencies.
  787.   *
  788.   */
  789. function get_plugin_dependencies($plugin_name$force_inclusion FALSE
  790.                                  $do_parse TRUE)
  791. {
  792.  
  793.    $plugin_dependencies get_plugin_requirement($plugin_name
  794.                                                  'required_plugins'
  795.                                                  FALSE,
  796.                                                  $force_inclusion);
  797.  
  798.    // the plugin is simply incompatible, no need to continue here
  799.    //
  800.    if ($plugin_dependencies === SQ_INCOMPATIBLE)
  801.       return $plugin_dependencies;
  802.  
  803.  
  804.    // not an array of requirements?  wrong format, just return FALSE
  805.    //
  806.    if (!is_array($plugin_dependencies))
  807.       return FALSE;
  808.  
  809.  
  810.    // make sure everything is in order...
  811.    //
  812.    if (!empty($plugin_dependencies))
  813.    {
  814.  
  815.       $new_plugin_dependencies array();
  816.       foreach ($plugin_dependencies as $plugin_name => $plugin_requirements)
  817.       {
  818.  
  819.          // if $plugin_requirements isn't an array, this is old-style,
  820.          // where only the version number was given...
  821.          //
  822.          if (is_string($plugin_requirements))
  823.             $plugin_requirements array('version' => $plugin_requirements,
  824.                                          'activate' => FALSE);
  825.  
  826.  
  827.          // trap badly formatted requirements arrays that don't have
  828.          // needed info
  829.          //
  830.          if (!is_array($plugin_requirements
  831.           || !isset($plugin_requirements['version']))
  832.             continue;
  833.          if (!isset($plugin_requirements['activate']))
  834.             $plugin_requirements['activate'FALSE;
  835.  
  836.  
  837.          // parse version into something we understand?
  838.          //
  839.          if ($do_parse && $plugin_requirements['version'!= SQ_INCOMPATIBLE)
  840.          {
  841.  
  842.             // massage version number into something we understand
  843.             //
  844.             // the first regexp strips everything and anything that follows
  845.             // the first occurance of a non-digit (or non decimal point), so
  846.             // beware that putting letters in the middle of a version string
  847.             // will effectively truncate the version string right there (but
  848.             // this also just helps remove the SquirrelMail version part off
  849.             // of versions such as "1.2.3-1.4.4")
  850.             //
  851.             // the second regexp just strips out non-digits/non-decimal points
  852.             // (and might be redundant(?))
  853.             //
  854.             // the regexps are wrapped in a trim that makes sure the version
  855.             // does not start or end with a decimal point
  856.             //
  857.             if (strpos(strtoupper($plugin_requirements['version'])'CORE'=== 0)
  858.             {
  859.                if (strpos($plugin_requirements['version']':'=== FALSE)
  860.                   $plugin_requirements['version''CORE';
  861.                else
  862.                   $plugin_requirements['version']
  863.                      = 'CORE:' trim(preg_replace(array('/[^0-9.]+.*$/''/[^0-9.]/')
  864.                                          ''substr($plugin_requirements['version']strpos($plugin_requirements['version']':'1))
  865.                                          '.');
  866.             }
  867.             else
  868.                $plugin_requirements['version']
  869.                   = trim(preg_replace(array('/[^0-9.]+.*$/''/[^0-9.]/')
  870.                                       ''$plugin_requirements['version'])
  871.                                       '.');
  872.  
  873.          }
  874.  
  875.          $new_plugin_dependencies[$plugin_name$plugin_requirements;
  876.  
  877.       }
  878.  
  879.       $plugin_dependencies $new_plugin_dependencies;
  880.  
  881.    }
  882.  
  883.    return $plugin_dependencies;
  884.  
  885. }
  886.  
  887. /**
  888.   * Check a plugin's other plugin dependencies.
  889.   *
  890.   * Determines whether or not all of the given
  891.   * plugin's required plugins are installed and
  892.   * up to the proper version, and if they are
  893.   * activated if required.
  894.   *
  895.   * By default, the desired plugin must be currently
  896.   * activated, and if it is not, this function will
  897.   * return FALSE.  By overriding the default value
  898.   * of $force_inclusion, this function will attempt
  899.   * to grab dependency information from the given
  900.   * plugin even if it is not activated (plugin still
  901.   * has to be unpackaged and set in place in the
  902.   * plugins directory).  Use with care - some plugins
  903.   * might break SquirrelMail when this is used.
  904.   * 
  905.   * NOTE that if a plugin does not report whether or
  906.   * not it has other plugin dependencies, this function
  907.   * will return TRUE, although that is possibly incorrect
  908.   * or misleading.
  909.   *
  910.   * @since 1.5.2
  911.   *
  912.   * @param string plugin_name   name of the plugin to
  913.   *                              check; must precisely
  914.   *                              match the plugin
  915.   *                              directory name
  916.   * @param bool force_inclusion try to get version info
  917.   *                              for plugins not activated?
  918.   *                              (default FALSE)
  919.   *
  920.   * @return mixed Boolean TRUE if all of the plugin's
  921.   *                required plugins are correctly installed,
  922.   *                the constant SQ_INCOMPATIBLE is returned if
  923.   *                the given plugin is entirely incompatible
  924.   *                with the current SquirrelMail version,
  925.   *                otherwise an array of the required plugins
  926.   *                that are either not installed or not up to
  927.   *                the minimum required version.  The array is
  928.   *                keyed by plugin name where values are arrays
  929.   *                again, where at least the following keys (and
  930.   *                corresponding values) will be available:
  931.   *                'version' - value is the minimum version
  932.   *                required for that plugin (in printable, non-
  933.   *                parsed format) or the constant SQ_INCOMPATIBLE,
  934.   *                which indicates that the plugin is actually
  935.   *                incompatible (not required), 'activate' - value
  936.   *                is boolean: TRUE indicates that the plugin must
  937.   *                also be activated, FALSE means that it only needs
  938.   *                to be present, but does not need to be activated.
  939.   *
  940.   */
  941. function check_plugin_dependencies($plugin_name$force_inclusion FALSE)
  942. {
  943.  
  944.    $dependencies get_plugin_dependencies($plugin_name$force_inclusion);
  945.    if (!$dependenciesreturn TRUE;
  946.    if ($dependencies === SQ_INCOMPATIBLEreturn $dependencies;
  947.    $missing_or_bad array();
  948.  
  949.    foreach ($dependencies as $depend_name => $depend_requirements)
  950.    {
  951.  
  952.       // check for core plugins first
  953.       //
  954.       if (strpos(strtoupper($depend_requirements['version'])'CORE'=== 0)
  955.       {
  956.  
  957.          // see if the plugin is in the core (just check if the directory exists)
  958.          //
  959.          if (!file_exists(SM_PATH 'plugins/' $depend_name))
  960.             $missing_or_bad[$depend_name$depend_requirements;
  961.  
  962.  
  963.          // check if it is activated if need be
  964.          //
  965.          else if ($depend_requirements['activate'&& !is_plugin_enabled($depend_name))
  966.             $missing_or_bad[$depend_name$depend_requirements;
  967.  
  968.  
  969.          // check if this is the right core version if one is given
  970.          // (note this is pretty useless - a plugin should specify
  971.          // whether or not it itself is compatible with this version
  972.          // of SM in the first place)
  973.          //
  974.          else if (strpos($depend_requirements['version']':'!== FALSE)
  975.          {
  976.             $version explode('.'substr($depend_requirements['version']strpos($depend_requirements['version']':'1)3);
  977.             $version[0intval($version[0]);
  978.             if (isset($version[1])) $version[1intval($version[1]);
  979.             else $version[10;
  980.             if (isset($version[2])) $version[2intval($version[2]);
  981.             else $version[20;
  982.  
  983.             if (!check_sm_version($version[0]$version[1]$version[2]))
  984.                $missing_or_bad[$depend_name$depend_requirements;
  985.          }
  986.  
  987.          continue;
  988.  
  989.       }
  990.  
  991.       // if the plugin is actually incompatible; check that it
  992.       // is not activated
  993.       //
  994.       if ($depend_requirements['version'== SQ_INCOMPATIBLE)
  995.       {
  996.  
  997.          if (is_plugin_enabled($depend_name))
  998.             $missing_or_bad[$depend_name$depend_requirements;
  999.  
  1000.          continue;
  1001.  
  1002.       }
  1003.  
  1004.       // check for normal plugins
  1005.       //
  1006.       $version explode('.'$depend_requirements['version']3);
  1007.       $version[0intval($version[0]);
  1008.       if (isset($version[1])) $version[1intval($version[1]);
  1009.       else $version[10;
  1010.       if (isset($version[2])) $version[2intval($version[2]);
  1011.       else $version[20;
  1012.  
  1013.       $force_dependency_inclusion !$depend_requirements['activate'];
  1014.  
  1015.       if (!check_plugin_version($depend_name$version[0]$version[1]
  1016.                                 $version[2]$force_dependency_inclusion))
  1017.          $missing_or_bad[$depend_name$depend_requirements;
  1018.    }
  1019.  
  1020.    if (empty($missing_or_bad)) return TRUE;
  1021.  
  1022.  
  1023.    // get non-parsed required versions
  1024.    //
  1025.    $non_parsed_dependencies get_plugin_dependencies($plugin_name
  1026.                                                       $force_inclusion
  1027.                                                       FALSE);
  1028.    $return_array array();
  1029.    foreach ($missing_or_bad as $depend_name => $ignore)
  1030.       $return_array[$depend_name$non_parsed_dependencies[$depend_name];
  1031.  
  1032.    return $return_array;
  1033.  
  1034. }

Documentation generated on Mon, 13 Jan 2020 04:23:22 +0100 by phpDocumentor 1.4.3