root/trunk/wifidog-auth/wifidog/classes/Dependency.php @ 1287

Revision 1287, 29.1 KB (checked in by benoitg, 7 years ago)
  • Dependencies installation: Improve layout, add embryo of unified install sytem (currently works for tarballs). Improve error output.
  • Add dependencies for future OpenId support
  • AbstractDb?: Make sure to throw an exception, avoiding a blank page when trying to access the index page on a auth server that hasn't been setup. Patch by Mathieu Bruno.
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5// +-------------------------------------------------------------------+
6// | WiFiDog Authentication Server                                     |
7// | =============================                                     |
8// |                                                                   |
9// | The WiFiDog Authentication Server is part of the WiFiDog captive  |
10// | portal suite.                                                     |
11// +-------------------------------------------------------------------+
12// | PHP version 5 required.                                           |
13// +-------------------------------------------------------------------+
14// | Homepage:     http://www.wifidog.org/                             |
15// | Source Forge: http://sourceforge.net/projects/wifidog/            |
16// +-------------------------------------------------------------------+
17// | This program is free software; you can redistribute it and/or     |
18// | modify it under the terms of the GNU General Public License as    |
19// | published by the Free Software Foundation; either version 2 of    |
20// | the License, or (at your option) any later version.               |
21// |                                                                   |
22// | This program is distributed in the hope that it will be useful,   |
23// | but WITHOUT ANY WARRANTY; without even the implied warranty of    |
24// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     |
25// | GNU General Public License for more details.                      |
26// |                                                                   |
27// | You should have received a copy of the GNU General Public License |
28// | along with this program; if not, contact:                         |
29// |                                                                   |
30// | Free Software Foundation           Voice:  +1-617-542-5942        |
31// | 59 Temple Place - Suite 330        Fax:    +1-617-542-2652        |
32// | Boston, MA  02111-1307,  USA       gnu@gnu.org                    |
33// |                                                                   |
34// +-------------------------------------------------------------------+
35
36/**
37 * @package    WiFiDogAuthServer
38 * @author     Philippe April
39 * @author     Max Horváth <max.horvath@freenet.de>
40 * @author     Benoit Grégoire <bock@step.polymtl.ca>
41 * @copyright  2005-2007 Philippe April
42 * @copyright  2005-2007 Max Horváth, Horvath Web Consulting
43 * @copyright  2006-2007 Benoit Grégoire, Technologies Coeus inc.
44 * @version    Subversion $Id$
45 * @link       http://www.wifidog.org/
46 */
47
48 // Detect Gettext support
49 if (!function_exists('gettext')) {
50     /**
51      * Load Locale class if Gettext support is not available
52      */
53      require_once ('classes/Locale.php');
54 }
55
56 require_once ('classes/Utils.php');
57       define('OPENID_PATH', WIFIDOG_ABS_FILE_PATH.'lib/php-openid-2.0.0-rc2/');
58 /**
59  * This class checks the existence of components required by WiFiDog.
60  * Note that it implicitely depends on the defines in include/path_defines_base.php
61  *
62  * @package    WiFiDogAuthServer
63  * @author     Philippe April
64  * @author     Max Horváth <max.horvath@freenet.de>
65  * @author     Benoit Grégoire <bock@step.polymtl.ca>
66  * @copyright  2005-2007 Philippe April
67  * @copyright  2005-2007 Max Horváth, Horvath Web Consulting
68  * @copyright  2006-2007 Benoit Grégoire, Technologies Coeus inc.
69  */
70  class Dependency
71  {
72      /**
73       * List of components used by WiFiDog
74       *
75       * @var array
76       */
77
78       private static $_components = array(
79       /* PHP extensions (mandatory) */
80       'mbstring' => array (
81       'mandatory' => 1,
82       "type" => "phpExtension",
83       'description' => 'Required for core auth-server and RSS support'
84       ),
85       'session' => array (
86       'mandatory' => 1,
87       "type" => "phpExtension",
88       'description' => 'Required for core auth-server'
89       ),
90       'pgsql' => array (
91       'mandatory' => 1,
92       "type" => "phpExtension",
93       'description' => 'Required for auth-server to connect to Postgresql database'
94       ),
95
96       /* PHP extensions (optional) */
97       'gettext' => array (
98       "type" => "phpExtension",
99       'description' => 'Almost essential: Without gettext, the auth-server will still work, but you will loose internationalization'
100       ),
101       'dom' => array (
102       "type" => "phpExtension",
103       'description' => 'Required to export the list of HotSpots as a RSS feed and for the geocoders'
104       ),
105       'libxml' => array (
106       "type" => "phpExtension",
107       'description' => 'Required to export the list of HotSpots as a RSS feed and for the geocoders'
108       ),
109       'mcrypt' => array (
110       "type" => "phpExtension",
111       'description' => 'Required by the optional Radius Authenticator'
112       ),
113       'mhash' => array (
114       "type" => "phpExtension",
115       'description' => 'Required by the optional Radius Authenticator'
116       ),
117       'xmlrpc' => array (
118       "type" => "phpExtension",
119       'description' => 'Required by the optional Radius Authenticator'
120       ),
121       "ldap" => array (
122       "type" => "phpExtension",
123       'description' => "Required by the optional LDAP Authenticator"
124       ),
125       'xml' => array (
126       "type" => "phpExtension",
127       'description' => 'Required for RSS support'
128       ),
129       'curl' => array (
130       "type" => "phpExtension",
131       'description' => 'Allows faster RSS support and required if you want to use Phlickr'
132       ),
133       'gd' => array (
134       "type" => "phpExtension",
135       'description' => 'Required if you want to generate graphical statistics'
136       ),
137       'xsl' => array (
138       "type" => "phpExtension",
139       'description' => 'Required if you want to generate a node list using XSLT stylesheets'
140       ),
141       /* Pecl libraries */
142       "radius" => array (
143       "type" => "peclStandard",
144       'description' => "Required by the optional Radius Authenticator"
145       ),
146
147       /* Locally installed libraries */
148       "Smarty" => array (
149       "type" => "localLib",
150       "detectFiles" => "lib/smarty/Smarty.class.php",
151       'description' => "Required for all parts of wifidog",
152       'website' => "http://smarty.php.net/"
153       ),
154       "FCKeditor" => array (
155       "type" => "localLib",
156       "detectFiles" => "lib/FCKeditor/fckeditor.php",
157       'description' => "Required by content type FCKEditor (WYSIWYG HTML)",
158       'website' => "http://www.fckeditor.net/"
159       ),
160       "FPDF" => array (
161       "type" => "localLib",
162       "detectFiles" => "lib/fpdf/fpdf.php",
163       'description' => "Required if you want to be able to export the node list as a PDF file",
164       'website' => "http://www.fpdf.org/"
165       ),
166       "php-openid" => array (
167       "type" => "localLib",
168       "detectFiles" => "lib/php-openid-2.0.0-rc2/CHANGELOG",
169       'description' => "Required for OpenID support (both as a consumer and Identity provider)",
170       'website' => "http://www.openidenabled.com/php-openid/",
171       'installSourceUrl' => "http://openidenabled.com/files/php-openid/packages/php-openid-2.0.0-rc2.tar.bz2",
172       'installMethod' => "tarball",
173       'installDestination' => "/"
174       ),
175       'gmp' => array (
176       "type" => "phpExtension",
177       'description' => 'Required for good OpenID support (otherwise, BCmath will be used, which is MUCH slower)'
178       ),
179       'BCmath' => array (
180       "type" => "phpExtension",
181       'description' => 'Required for OpenID support, but ONLY if GMP above is not available'
182       ),
183       /* PEAR libraries */
184       'Auth_RADIUS' => array (
185       "type" => "pearStandard",
186       "detectFiles" => "Auth/RADIUS.php",
187       'description' => "Required by the optional Radius Authenticator"
188       ),
189       'Crypt_CHAP' => array (
190       "type" => "pearStandard",
191       "detectFiles" => "Crypt/CHAP.php",
192       'description' => "Required by the optional Radius Authenticator"
193       ),
194       "Cache" => array (
195       "type" => "pearStandard",
196       "detectFiles" => "Cache/Lite.php",
197       'description' => "Required if you want to turn on the experimental USE_CACHE_LITE in config.php"
198       ),
199       "HTML_Safe" => array (
200       "type" => "pearStandard",
201       "detectFiles" => "HTML/Safe.php",
202       'description' => "Optional for content type Langstring (and subtypes) to better strip out dangerous HTML"
203       ),
204       "Image_Graph" => array (
205       "type" => "pearStandard",
206       "detectFiles" => "Image/Graph.php",
207       'description' => "Required if you want to generate graphical statistics"
208       ),
209       "Image_Canvas" => array (
210       "type" => "pearStandard",
211       "detectFiles" => "Image/Canvas.php",
212       'description' => "Required if you want to generate graphical statistics"
213       ),
214       "Image_Color" => array (
215       "type" => "pearStandard",
216       "detectFiles" => "Image/Color.php",
217       'description' => "Required if you want to generate graphical statistics"
218       ),
219
220       /* PHP extensions (custom installations) */
221       "Phlickr" => array (
222       "type" => "pearCustom",
223       "detectFiles" => "Phlickr/Api.php",
224       'description' => "Required by content type FlickrPhotostream",
225       'website' => "http://drewish.com/projects/phlickr/",
226       'installSourceUrl' => "http://superb-east.dl.sourceforge.net/sourceforge/phlickr/Phlickr-0.2.7.tgz"
227       ),
228       );
229
230       /**
231        * Object cache for the object factory (getObject())
232        */
233        private static $instanceArray = array();
234
235        private $_id;
236
237        /**
238         * Constructor
239         */
240         private function __construct($component) {
241             $this->_id = $component;
242         }
243
244         /**
245          * Get the entire array of Dependency
246          *
247          * @return boolean Returns whether the file has been found or not.
248          */
249          public static function getDependency()
250          {
251              $retval = array();
252
253              foreach (self::$_components as $component_key=>$component_info) {
254                  $retval[] = self::getObject($component_key);
255              }
256
257              return $retval;
258          }
259
260          /** Use PHP internal functions to execute a command
261            @return: Return value of the command*/
262          function execVerbose($command, & $output, & $return_var, &$errMsg = null) {
263              $errMsg .= "Executing: $command <br/>";
264              exec($command.'  2>&1', $output, $return_var);
265              if ($return_var != 0)
266              $errMsg .= "<p style='color:red'><em>Error:</em>  Command did not complete successfully  (returned $return_var): <br/>\n";
267              else
268              $errMsg .= "<p style='color:green'><em>Command completed successfully</em>  (returned $return_var): <br/>\n";
269
270              if (($return_var != 0) && $output) {
271                  foreach ($output as $output_line)
272                  $errMsg .= " $output_line <br/>\n";
273              }
274              $errMsg .= "</p>\n";
275              return $return_var;
276          }
277
278          /**
279           * Checks if a file exists, including checking in the include path
280           *
281           * @param string $file Path or name of a file
282           *
283           * @return boolean Returns whether the file has been found or not.
284           *
285           * @author Aidan Lister <aidan@php.net>
286           * @link http://aidanlister.com/repos/v/function.file_exists_incpath.php
287           */
288           public static function file_exists_incpath($file)
289           {
290               $_paths = explode(PATH_SEPARATOR, get_include_path());
291
292               foreach ($_paths as $_path) {
293                   // Formulate the absolute path
294                   $_fullPath = $_path . DIRECTORY_SEPARATOR . $file;
295
296                   // Check it
297                   if (file_exists($_fullPath)) {
298                       return $_fullPath;
299                   }
300               }
301
302               return false;
303           }
304
305           /**
306            * Checks if a component is available.
307            *
308            * This function checks, if a specific component is available to be used
309            * by Wifidog.
310            *
311            * @param string $component Name of component to be checked.
312            * @param string $errmsg    Reference of a string which would contain an
313            *                          error message.
314            *
315            * @return boolean Returns whether the component has been found or not.
316            */
317            public static function check($component, &$errmsg = null)
318            {
319                // Init values
320                $returnValue = false;
321
322                // Check, if the requested component can be found.
323                if (isset(self::$_components[$component])) {
324                    // What are we checking for?
325                    if (self::$_components[$component]["type"] == "phpExtension" || self::$_components[$component]["type"] == "peclStandard") {
326                        // Warning: extension_loaded(string) is case sensitive
327                        $returnValue = extension_loaded($component);
328                    }
329                    else if (self::$_components[$component]["type"] == "localLib") {
330                        if (is_array(self::$_components[$component]["detectFiles"])) {
331                            $_singleReturns = true;
332                            foreach (self::$_components[$component]["detectFiles"] as $_fileNames) {
333                                $filePath = WIFIDOG_ABS_FILE_PATH . $_fileNames;
334
335                                if (!file_exists($filePath)) {
336                                    echo "TEST";
337                                    $_singleReturns = false;
338                                    // The component has NOT been found. Return error message.
339                                    $errmsg .= sprintf(_("File %s not found"), $filePath);
340                                    break;
341                                }
342                            }
343
344                            $returnValue = $_singleReturns;
345                        }
346                        else {
347                            $filePath = WIFIDOG_ABS_FILE_PATH . self::$_components[$component]["detectFiles"];
348
349                            if (file_exists($filePath)) {
350                                // The component has been found.
351                                $returnValue = true;
352                            }
353                            else {
354                                // The component has NOT been found. Return error message.
355                                $errmsg .= sprintf(_("File %s not found"), $filePath);
356                            }
357                        }
358                    }
359                    else if (self::$_components[$component]["type"] == "pearStandard" || self::$_components[$component]["type"] == "pearCustom") {
360                        if (is_array(self::$_components[$component]["detectFiles"])) {
361                            $_singleReturns = true;
362
363                            foreach (self::$_components[$component]["detectFiles"] as $_fileNames) {
364                                // We need to use a custom file_exists to also check in the include path
365                                if (!self::file_exists_incpath($_fileNames)) {
366                                    $_singleReturns = false;
367
368                                    // The component has NOT been found. Return error message.
369                                    $errmsg .= sprintf(_("File %s not found in %s"), $_fileNames, get_include_path());
370                                }
371                            }
372
373                            $returnValue = $_singleReturns;
374                        } else {
375                            // We need to use a custom file_exists to also check in the include path
376                            if (self::file_exists_incpath(self::$_components[$component]["detectFiles"])) {
377                                // The component has been found.
378                                $returnValue = true;
379                            }
380                            else {
381
382                                // The component has NOT been found. Return error message.
383                                $errmsg .= sprintf(_("File %s not found in %s"), self::$_components[$component]["detectFiles"], get_include_path());
384                            }
385                        }
386                    }
387                    else {
388                        throw new Exception(sprintf("Unknown component type: %s", self::$_components[$component]["type"]));
389                    }
390                } else {
391                    // The requested component has not been defined in this class.
392                    throw new Exception("Component not found");
393                }
394
395                return $returnValue;
396            }
397
398            /** Use PHP internal functions to download a file */
399            static public function downloadFile($remoteURL, $localPath) {
400                set_time_limit(1500); // 25 minutes timeout
401                return copy($remoteURL, $localPath);
402            }
403            /**
404             * Get a UI to install the component
405             *
406             * @return html markup.
407             */
408             public function getInstallUI()
409             {
410                 // Init values
411                 $html = false;
412
413                 // Check, if the requested component can be found.
414                 if (self::check($this->getId())) {
415                     //Component already installed
416                 }
417                 else {
418                     // What are we checking for?
419                     $type = $this->getType();
420                     switch ($type) {
421                         case "phpExtension":
422                             $html .= sprintf(_("To install this standard PHP extension, look for a package with a similar name in your distribution's package manager.  Ex: For Debian based distributions, you may try 'sudo apt-get install php5-%s'"), $this->getId());
423
424                             break;
425                         case "localLib":
426                             $name = $this->getId().'_install';
427                             $html .= sprintf(_("<input type='submit' name='%s' value='Install %s'/>"), $name,$this->getId());
428
429                             break;
430                             //case "pearStandard":
431                             //    break;
432                         case "pearCustom":
433                             if($this->getInstallSourceUrl()) {
434                                 $installSource=$this->getInstallSourceUrl();
435                             }
436                             else {
437                                 $installSource=sprintf(_("url_to_the_tarball (Sorry, i couldn't find the source for %s in installSourceUrl)"), $this->getId());
438                             }
439                             $html .= sprintf(_("To install this custom PEAR extension, use 'sudo pear install %s'"), $installSource);
440                             break;
441                         default:
442                             $html .= sprintf(_("Sorry, I don't know how to install a %s extension"), $type);
443                     }
444                 }
445                 return $html;
446             }
447
448             /**
449              * Get a UI to install the component
450              *
451              * @return true if something was processed.
452              */
453              public function processInstallUI(&$errMsg=null)
454              {
455
456                  $retval = false;
457                  $name = $this->getId().'_install';
458                  if(!empty($_REQUEST[$name]))
459                  {
460                      $this->install($errMsg);
461                  }
462                   
463                  return $retval;
464              }
465              /**
466               * Retreives the id of the object
467               *
468               * @return The id, a string
469               */
470               public function getId() {
471                   return $this->_id;
472               }
473
474               /**
475                * Get an instance of the object
476                *
477                * @param string $id The object id
478                *
479                * @return mixed The Content object, or null if there was an error
480                *               (an exception is also thrown)
481                *
482                * @see GenericObject
483                * @static
484                * @access public
485                */
486                public static function &getObject($id)
487                {
488                    if(!isset(self::$instanceArray[$id])) {
489                        self::$instanceArray[$id] = new self($id);
490                    }
491
492                    return self::$instanceArray[$id];
493                }
494
495                /**
496                 * Get website URL for the dependency (if available)
497                 *
498                 * @return URL or null
499                 */
500                 public function getWebsiteURL()
501                 {
502                     $retval = null;
503
504                     if(self::$_components[$this->_id]["type"] == "phpExtension") {
505                         $retval = "http://www.php.net/" . $this->_id . "/";
506                     } else if(self::$_components[$this->_id]["type"] == "pearStandard") {
507                         $retval = "http://pear.php.net/package/" . $this->_id . "/";
508                     } else if(self::$_components[$this->_id]["type"] == "peclStandard") {
509                         $retval = "http://pecl.php.net/package/" . $this->_id . "/";
510                     } else {
511                         if(!empty(self::$_components[$this->_id]['website'])) {
512                             $retval = self::$_components[$this->_id]['website'];
513                         }
514                     }
515
516                     return $retval;
517                 }
518
519                 /**
520                  * Get the description of the dependency (if available)
521                  *
522                  * @return String or null
523                  */
524                  public function getDescription()
525                  {
526                      $retval = null;
527
528                      if(!empty(self::$_components[$this->_id]['description'])) {
529                          $retval = self::$_components[$this->_id]['description'];
530                      }
531
532                      return $retval;
533                  }
534
535                  /**
536                   * Get the source URL where the package can be downloaded.  It's meaning depends on the install method (for example, it may be a svn source)
537                   *
538                   * @return String or null
539                   */
540                   public function getInstallSourceUrl()
541                   {
542                       $retval = null;
543                       if(!empty(self::$_components[$this->_id]['installSourceUrl'])) {
544                           $retval = self::$_components[$this->_id]['installSourceUrl'];
545                       }
546                       return $retval;
547                   }
548                   /**
549                    * Get the install method for this dependency (only for those that can be directly installed by the auth server)                  *
550                    * @return String or null
551                    */
552                    public function getInstallMethod()
553                    {
554                        $retval = null;
555                        if(!empty(self::$_components[$this->_id]['installMethod'])) {
556                            $retval = self::$_components[$this->_id]['installMethod'];
557                        }
558                        return $retval;
559                    }
560
561                    /**
562                     * Get the install destination.  Interpretation depends on the install method.  Usisally the parameter to be passed to tar or SVN                 *
563                     * @return String or null
564                     */
565                     public function getInstallDestination()
566                     {
567                         $retval = null;
568                         if(!empty(self::$_components[$this->_id]['installDestination'])) {
569                             $retval = self::$_components[$this->_id]['installDestination'];
570                         }
571                         return $retval;
572                     }
573                     /**
574                      * Get the type of the dependency
575                      *
576                      * @return String
577                      */
578                      public function getType()
579                      {
580                          return self::$_components[$this->_id]['type'];
581                      }
582
583                      /**
584                       * Get the type of the dependency
585                       *
586                       * @return String
587                       */
588                       public function isMandatory()
589                       {
590                           $retval = null;
591
592                           if(!empty(self::$_components[$this->_id]['mandatory'])) {
593                               $retval = true;
594                           }
595
596                           return $retval;
597                       }
598
599                       public function install(&$errorMsg = null){
600                           $installSourceUrl = $this->getInstallSourceUrl();
601                           $installDestinationPathOrig = $this->getInstallDestination();
602                           if(!$installSourceUrl || !$installDestinationPathOrig) {
603                               $errorMsg .= "<em style=\"color:red\">Error:</em>Either the install source or destination path is missing<br/>\n";
604                           }
605                           else {
606                               $installDestinationPath = WIFIDOG_ABS_FILE_PATH . "lib/";$installDestinationPathOrig.
607                               $installMethod = $this->getInstallMethod();
608                               switch($installMethod) {
609                                   case "tarball":
610                                       $downloadPath = WIFIDOG_ABS_FILE_PATH . "tmp/";
611                                       chdir($downloadPath);
612                                       $filename_array = preg_split("/\//", $installSourceUrl);
613                                       $filename = array_pop($filename_array);
614
615
616                                       if (!file_exists($downloadPath . $filename)){
617                                           $errorMsg .= "Downloading tarball ($installSourceUrl) : ";
618                                           //execVerbose("wget \"$phlickr_full_url\" 2>&1", $output, $return);
619                                           self::downloadFile($installSourceUrl, $downloadPath . $filename);
620
621                                           if (!file_exists($downloadPath . $filename)) { # Error occured, print output of wget
622                                               $errorMsg .= sprintf("<em style=\"color:red\">Error:</em> Unable to download $installSourceUrl to $destinationPath<br/>\n");
623                                               return false;
624                                           }
625                                           else {
626                                               $errorMsg .= "OK<br/>";
627                                           }
628                                       }
629                                       else {
630                                           $errorMsg .= "Tarball $filename already present<br/>\n";
631                                       }
632                                       chdir($installDestinationPath);
633                                       //pretty_print_r($installDestinationPath);
634                                       if(preg_match("/(.tgz$)|(.tar.gz$)/",$filename)) {
635                                           $errorMsg .= "Archive is in gzip format<br/>";
636                                           $params = "-zxf";
637                                       }
638                                       else if(preg_match("/\.bz2$/",$filename)) {
639                                           $errorMsg .= "Archive is in bz2 format<br/>";
640                                                                                      $params = "-jxf";
641                                       }
642                                       else {
643                                           $errorMsg .= "Unable to determine the archive format from the filemname<br/>";
644                                           return;
645                                       }
646                                       $errorMsg .= "Uncompressing : ";
647                                       $execRetval = self::execVerbose("tar $params ".$downloadPath . $filename, $output, $return, $errorMsg);
648                                       if($execRetval==0) {
649                                           $errorMsg .= "OK<br/>";
650                                       }
651                                       else {
652                                           $errorMsg .= "<em style=\"color:red\">Decompression failed</em><br/>";
653                                           return;
654                                       }
655                                       break;
656                                   default:
657                                       $errorMsg .= "Unknown install method $installMethod<br/>";
658                               }//End switch
659                           }
660                       }
661
662
663  }
664
665  /*
666   * Local variables:
667   * tab-width: 4
668   * c-basic-offset: 4
669   * c-hanging-comment-ender-p: nil
670   * End:
671   */
Note: See TracBrowser for help on using the browser.