PageRenderTime 2ms CodeModel.GetById 34ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

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

#
PHP | 943 lines | 263 code | 111 blank | 569 comment | 84 complexity | fa3a07d0e90358c052bbc4065f99401e MD5 | raw file
  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 &copy; 1999-2007 The SquirrelMail Project Team
 11 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 12 * @version $Id: plugin.php 12400 2007-05-17 16:36:13Z kink $
 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 */
 21function 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 */
 72function 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 */
142function 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 */
202function 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 */
263function 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 */
273function 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  */
346function 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  */
499function 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_inclusion, TRUE);
505   if (!$plugin_version) return 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[0] = 0;
513   if (!isset($plugin_version[1])) $plugin_version[1] = 0;
514   if (!isset($plugin_version[2])) $plugin_version[2] = 0;
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 bool force_inclusion Try to get requirement info
560  *                             for plugins not activated?
561  *                             (default FALSE)
562  *
563  * @return mixed NULL is returned if the plugin could not be 
564  *               found or does not include the given requirement,
565  *               the constant SQ_INCOMPATIBLE is returned if the
566  *               given plugin is entirely incompatible with the
567  *               current SquirrelMail version, otherwise the 
568  *               value of the requirement is returned, whatever 
569  *               that may be (varies per requirement type).
570  *
571  */
572function get_plugin_requirement($plugin_name, $requirement, 
573                                $force_inclusion = FALSE)
574{
575
576   $info_function = $plugin_name . '_info';
577   $plugin_info = array();
578   $requirement_value = NULL;
579
580
581   // first attempt to find the plugin info function, wherein
582   // the plugin requirements should be available
583   //
584   if (function_exists($info_function))
585      $plugin_info = $info_function();
586   else if ($force_inclusion 
587    && file_exists(SM_PATH . 'plugins/' . $plugin_name . '/setup.php'))
588   {
589
590      /* --- Old code, keeping just in case... problem with it is, for example,
591         if it is used, but later we are checking if the same plugin is
592         activated (because it SHOULD be), this code having run will possibly
593         create a false positive.
594      include_once(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
595      if (function_exists($info_function))
596         $plugin_info = $info_function();
597      --- */
598
599      // so what we need to do is process this plugin without
600      // it polluting our environment
601      //
602      // we *could* just use the above code, which is more of a
603      // sure thing than a regular expression, and then test
604      // the contents of the $plugins array to see if this plugin
605      // is actually activated, and that might be good enough, but
606      // for now, we'll use the following approach, because of two
607      // concerns: other plugins and other templates might force
608      // the inclusion of a plugin (which SHOULD also add it to
609      // the $plugins array, but am not 100% sure at this time (FIXME)),
610      // and because the regexp below should work just fine with
611      // any resonably formatted plugin setup file.
612      //
613      // read the target plugin's setup.php file into a string,
614      // then use a regular expression to try to find the needed
615      // requirement information...
616      // this of course can break if plugin authors do funny things
617      // with their file formatting
618      //
619      $setup_file = '';
620      $file_contents = file(SM_PATH . 'plugins/' . $plugin_name . '/setup.php');
621      foreach ($file_contents as $line) 
622         $setup_file .= $line;
623
624
625      // this regexp grabs the full plugin info array from a standard 
626      // <plugin>_info() function... determining the end of the info 
627      // array can fail, but if authors end the array with ");\n"
628      // (without quotes), then it should work well, especially because 
629      // newlines shouldn't be found inside the array after any ");" 
630      // (without quotes)
631      //
632      if (preg_match('/function\s+.*?' . $plugin_name . '_info.*?\(.*?\).*?\{.*?(array.+?\)\s*;)\s*' . "\n" . '/is', $setup_file, $matches))
633         eval('$plugin_info = ' . $matches[1]);
634
635   }
636
637
638   // attempt to get the requirement from the "global" scope 
639   // of the plugin information array
640   //
641   if (isset($plugin_info[$requirement])
642    && !is_null($plugin_info[$requirement]))
643      $requirement_value = $plugin_info[$requirement];
644
645
646   // now, if there is a series of per-version requirements, 
647   // check there too
648   //
649   if (!empty($plugin_info['per_version_requirements']) 
650    && is_array($plugin_info['per_version_requirements']))
651   {
652
653      // iterate through requirements, where keys are version
654      // numbers -- tricky part is knowing the difference between
655      // more than one version for which the current SM installation
656      // passes the check_sm_version() test... we want the highest one
657      //
658      $requirement_value_override = NULL;
659      $highest_version_array = array();
660      foreach ($plugin_info['per_version_requirements'] as $version => $requirement_overrides)
661      {
662
663         $version_array = explode('.', $version);
664         if (sizeof($version_array) != 3) continue;
665
666         $a = $version_array[0];
667         $b = $version_array[1];
668         $c = $version_array[2];
669
670         if (check_sm_version($a, $b, $c) 
671          && ( !empty($requirement_overrides[SQ_INCOMPATIBLE]) 
672          || (isset($requirement_overrides[$requirement])
673          && !is_null($requirement_overrides[$requirement]))))
674         {
675
676            if (empty($highest_version_array)
677             || $highest_version_array[0] < $a
678             || ($highest_version_array[0] == $a
679             && $highest_version_array[1] < $b)
680             || ($highest_version_array[0] == $a 
681             && $highest_version_array[1] == $b 
682             && $highest_version_array[2] < $c))
683            {
684               $highest_version_array = $version_array;
685               if (!empty($requirement_overrides[SQ_INCOMPATIBLE]))
686                  $requirement_value_override = SQ_INCOMPATIBLE;
687               else
688                  $requirement_value_override = $requirement_overrides[$requirement];
689            }
690
691         }
692
693      }
694
695      // now grab override if one is available
696      //
697      if (!is_null($requirement_value_override))
698         $requirement_value = $requirement_value_override;
699
700   }
701
702   return $requirement_value;
703
704}
705
706/**
707  * Get a plugin's other plugin dependencies.
708  *
709  * Determines and returns all the other plugins
710  * that a given plugin requires, as well as the
711  * minimum version numbers of the required plugins
712  * and whether or not they need to be activated.
713  *
714  * By default, the desired plugin must be currently 
715  * activated, and if it is not, this function will 
716  * return FALSE.  By overriding the default value 
717  * of $force_inclusion, this function will attempt 
718  * to grab dependency information from the given 
719  * plugin even if it is not activated (plugin still 
720  * has to be unpackaged and set in place in the 
721  * plugins directory).  Use with care - some plugins
722  * might break SquirrelMail when this is used.
723  * 
724  * By turning on the $do_parse argument (it is on by
725  * default), the version string for each required 
726  * plugin will be parsed by SquirrelMail into a 
727  * SquirrelMail-compatible version string (such as 
728  * "1.2.3") if it is not already.  See notes about 
729  * version formatting under the get_plugin_version()
730  * function documentation.
731  *
732  * @since 1.5.2
733  *
734  * @param string plugin_name   name of the plugin to
735  *                             check; must precisely
736  *                             match the plugin
737  *                             directory name
738  * @param bool force_inclusion try to get version info
739  *                             for plugins not activated?
740  *                             (default FALSE)
741  * @param bool do_parse        return the version numbers
742  *                             for required plugins in
743  *                             SquirrelMail-compatible
744  *                             format (default FALSE)
745  *
746  * @return mixed Boolean FALSE is returned if the plugin
747  *               could not be found or does not indicate
748  *               whether it has other plugin dependencies, 
749  *               the constant SQ_INCOMPATIBLE is returned if 
750  *               the given plugin is entirely incompatible 
751  *               with the current SquirrelMail version, 
752  *               otherwise an array is returned where keys 
753  *               are the names of required plugin 
754  *               dependencies, and values are arrays again, 
755  *               where at least the following keys (and 
756  *               corresponding values) will be available: 
757  *               'version' - value is the minimum version 
758  *               required for that plugin (the format of 
759  *               which might vary per the value of $do_parse), 
760  *               'activate' - value is boolean: TRUE indicates 
761  *               that the plugin must also be activated, FALSE 
762  *               means that it only needs to be present, but 
763  *               does not need to be activated.  Note that 
764  *               the return value might be an empty array, 
765  *               indicating that the plugin has no dependencies.
766  *
767  */
768function get_plugin_dependencies($plugin_name, $force_inclusion = FALSE, 
769                                 $do_parse = TRUE)
770{
771
772   $plugin_dependencies = get_plugin_requirement($plugin_name, 
773                                                 'required_plugins', 
774                                                 $force_inclusion);
775
776   // the plugin is simply incompatible, no need to continue here
777   //
778   if ($plugin_dependencies === SQ_INCOMPATIBLE)
779      return $plugin_dependencies;
780
781
782   // not an array of requirements?  wrong format, just return FALSE
783   //
784   if (!is_array($plugin_dependencies))
785      return FALSE;
786
787
788   // make sure everything is in order...
789   //
790   if (!empty($plugin_dependencies))
791   {
792
793      $new_plugin_dependencies = array();
794      foreach ($plugin_dependencies as $plugin_name => $plugin_requirements)
795      {
796
797         // if $plugin_requirements isn't an array, this is old-style,
798         // where only the version number was given...
799         //
800         if (is_string($plugin_requirements))
801            $plugin_requirements = array('version' => $plugin_requirements,
802                                         'activate' => FALSE);
803
804
805         // trap badly formatted requirements arrays that don't have
806         // needed info
807         //
808         if (!is_array($plugin_requirements) 
809          || !isset($plugin_requirements['version']))
810            continue;
811         if (!isset($plugin_requirements['activate']))
812            $plugin_requirements['activate'] = FALSE;
813
814
815         // parse version into something we understand?
816         //
817         if ($do_parse)
818         {
819
820            // massage version number into something we understand
821            //
822            // the first regexp strips everything and anything that follows
823            // the first occurance of a non-digit (or non decimal point), so
824            // beware that putting letters in the middle of a version string
825            // will effectively truncate the version string right there (but
826            // this also just helps remove the SquirrelMail version part off
827            // of versions such as "1.2.3-1.4.4")
828            //
829            // the second regexp just strips out non-digits/non-decimal points
830            // (and might be redundant(?))
831            //
832            // the regexps are wrapped in a trim that makes sure the version
833            // does not start or end with a decimal point
834            //
835            $plugin_requirements['version']
836               = trim(preg_replace(array('/[^0-9.]+.*$/', '/[^0-9.]/'), 
837                                   '', $plugin_requirements['version']), 
838                                   '.');
839
840         }
841
842         $new_plugin_dependencies[$plugin_name] = $plugin_requirements;
843
844      }
845
846      $plugin_dependencies = $new_plugin_dependencies;
847
848   }
849
850   return $plugin_dependencies;
851
852}
853
854/**
855  * Check a plugin's other plugin dependencies.
856  *
857  * Determines whether or not all of the given
858  * plugin's required plugins are installed and
859  * up to the proper version, and if they are 
860  * activated if required.
861  *
862  * By default, the desired plugin must be currently 
863  * activated, and if it is not, this function will 
864  * return FALSE.  By overriding the default value 
865  * of $force_inclusion, this function will attempt 
866  * to grab dependency information from the given 
867  * plugin even if it is not activated (plugin still 
868  * has to be unpackaged and set in place in the 
869  * plugins directory).  Use with care - some plugins
870  * might break SquirrelMail when this is used.
871  * 
872  * NOTE that if a plugin does not report whether or
873  * not it has other plugin dependencies, this function
874  * will return TRUE, although that is possibly incorrect
875  * or misleading.
876  *
877  * @since 1.5.2
878  *
879  * @param string plugin_name   name of the plugin to
880  *                             check; must precisely
881  *                             match the plugin
882  *                             directory name
883  * @param bool force_inclusion try to get version info
884  *                             for plugins not activated?
885  *                             (default FALSE)
886  *
887  * @return mixed Boolean TRUE if all of the plugin's 
888  *               required plugins are correctly installed,
889  *               the constant SQ_INCOMPATIBLE is returned if 
890  *               the given plugin is entirely incompatible 
891  *               with the current SquirrelMail version, 
892  *               otherwise an array of the required plugins
893  *               that are either not installed or not up to
894  *               the minimum required version.  The array is
895  *               keyed by plugin name where values are arrays
896  *               again, where at least the following keys (and 
897  *               corresponding values) will be available: 
898  *               'version' - value is the minimum version 
899  *               required for that plugin (in printable, non-
900  *               parsed format), 'activate' - value is boolean: 
901  *               TRUE indicates that the plugin must also be 
902  *               activated, FALSE means that it only needs to 
903  *               be present, but does not need to be activated.  
904  *
905  */
906function check_plugin_dependencies($plugin_name, $force_inclusion = FALSE)
907{
908
909   $dependencies = get_plugin_dependencies($plugin_name, $force_inclusion);
910   if (!$dependencies) return TRUE;
911   if ($dependencies === SQ_INCOMPATIBLE) return $dependencies;
912   $missing_or_bad = array();
913
914   foreach ($dependencies as $depend_name => $depend_requirements)
915   {
916      $version = explode('.', $depend_requirements['version'], 3);
917      $version[0] = intval($version[0]);
918      $version[1] = intval($version[1]);
919      $version[2] = intval($version[2]);
920
921      $force_dependency_inclusion = !$depend_requirements['activate'];
922
923      if (!check_plugin_version($depend_name, $version[0], $version[1], 
924                                $version[2], $force_dependency_inclusion))
925         $missing_or_bad[$depend_name] = $depend_requirements;
926   }
927
928   if (empty($missing_or_bad)) return TRUE;
929
930
931   // get non-parsed required versions
932   //
933   $non_parsed_dependencies = get_plugin_dependencies($plugin_name, 
934                                                      $force_inclusion, 
935                                                      FALSE);
936   $return_array = array();
937   foreach ($missing_or_bad as $depend_name => $ignore)
938      $return_array[$depend_name] = $non_parsed_dependencies[$depend_name];
939
940   return $return_array;
941
942}
943