source: branches/2.1/admin/include/plugins.class.php @ 8240

Last change on this file since 8240 was 8240, checked in by plg, 13 years ago

merge r8238 from trunk to branch 2.1

feature 2048 removed (and was never released): no data are sent anonymously to piwigo.org for statistics purpose

  • Property svn:eol-style set to LF
File size: 17.1 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based picture gallery                                  |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2010 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24class plugins
25{
26  var $fs_plugins = array();
27  var $db_plugins_by_id = array();
28  var $server_plugins = array();
29
30  /**
31   * Initialize $fs_plugins and $db_plugins_by_id
32  */
33  function plugins()
34  {
35    $this->get_fs_plugins();
36
37    foreach (get_db_plugins() as $db_plugin)
38    {
39      $this->db_plugins_by_id[$db_plugin['id']] = $db_plugin;
40    }
41  }
42
43  /**
44   * Set tabsheet for plugins pages.
45   * @param string selected page.
46   */
47  function set_tabsheet($selected)
48  {
49    include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
50
51    $link = get_root_url().'admin.php?page=';
52
53    $tabsheet = new tabsheet();
54    $tabsheet->add('plugins_list', l10n('Plugin list'), $link.'plugins_list');
55    $tabsheet->add('plugins_update', l10n('Check for updates'), $link.'plugins_update');
56    $tabsheet->add('plugins_new', l10n('Other plugins'), $link.'plugins_new');
57    $tabsheet->select($selected);
58    $tabsheet->assign();
59  }
60
61 /**
62   * Perform requested actions
63  *  @param string - action
64  * @param string - plugin id
65  * @param array - errors
66  */
67  function perform_action($action, $plugin_id)
68  {
69    if (isset($this->db_plugins_by_id[$plugin_id]))
70    {
71      $crt_db_plugin = $this->db_plugins_by_id[$plugin_id];
72    }
73    $file_to_include = PHPWG_PLUGINS_PATH . $plugin_id . '/maintain.inc.php';
74
75    $errors = array();
76
77    switch ($action)
78    {
79      case 'install':
80        if (!empty($crt_db_plugin))
81        {
82          array_push($errors, 'CANNOT INSTALL - ALREADY INSTALLED');
83          break;
84        }
85        if (!isset($this->fs_plugins[$plugin_id]))
86        {
87          array_push($errors, 'CANNOT INSTALL - NO SUCH PLUGIN');
88          break;
89        }
90        if (file_exists($file_to_include))
91        {
92          include_once($file_to_include);
93          if (function_exists('plugin_install'))
94          {
95            plugin_install($plugin_id, $this->fs_plugins[$plugin_id]['version'], $errors);
96          }
97        }
98        if (empty($errors))
99        {
100          $query = '
101INSERT INTO ' . PLUGINS_TABLE . ' (id,version) VALUES (\''
102. $plugin_id . '\',\'' . $this->fs_plugins[$plugin_id]['version'] . '\'
103)';
104          pwg_query($query);
105        }
106        break;
107
108      case 'activate':
109        if (!isset($crt_db_plugin))
110        {
111          array_push($errors, 'CANNOT ACTIVATE - NOT INSTALLED');
112          break;
113        }
114        if ($crt_db_plugin['state'] != 'inactive')
115        {
116          array_push($errors, 'invalid current state ' . $crt_db_plugin['state']);
117          break;
118        }
119        if (file_exists($file_to_include))
120        {
121          include_once($file_to_include);
122          if (function_exists('plugin_activate'))
123          {
124            plugin_activate($plugin_id, $crt_db_plugin['version'], $errors);
125          }
126        }
127        if (empty($errors))
128        {
129          $query = '
130UPDATE ' . PLUGINS_TABLE . '
131SET state=\'active\', version=\''.$this->fs_plugins[$plugin_id]['version'].'\'
132WHERE id=\'' . $plugin_id . '\'';
133          pwg_query($query);
134        }
135        break;
136
137      case 'deactivate':
138        if (!isset($crt_db_plugin))
139        {
140          die ('CANNOT DEACTIVATE - NOT INSTALLED');
141        }
142        if ($crt_db_plugin['state'] != 'active')
143        {
144          die('invalid current state ' . $crt_db_plugin['state']);
145        }
146        $query = '
147UPDATE ' . PLUGINS_TABLE . ' SET state=\'inactive\' WHERE id=\'' . $plugin_id . '\'';
148        pwg_query($query);
149        if (file_exists($file_to_include))
150        {
151          include_once($file_to_include);
152          if (function_exists('plugin_deactivate'))
153          {
154            plugin_deactivate($plugin_id);
155          }
156        }
157        break;
158
159      case 'uninstall':
160        if (!isset($crt_db_plugin))
161        {
162          die ('CANNOT UNINSTALL - NOT INSTALLED');
163        }
164        $query = '
165DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\'';
166        pwg_query($query);
167        if (file_exists($file_to_include))
168        {
169          include_once($file_to_include);
170          if (function_exists('plugin_uninstall'))
171          {
172            plugin_uninstall($plugin_id);
173          }
174        }
175        break;
176
177      case 'delete':
178        if (!empty($crt_db_plugin))
179        {
180          array_push($errors, 'CANNOT DELETE - PLUGIN IS INSTALLED');
181          break;
182        }
183        if (!isset($this->fs_plugins[$plugin_id]))
184        {
185          array_push($errors, 'CANNOT DELETE - NO SUCH PLUGIN');
186          break;
187        }
188        if (!$this->deltree(PHPWG_PLUGINS_PATH . $plugin_id))
189        {
190          $this->send_to_trash(PHPWG_PLUGINS_PATH . $plugin_id);
191        }
192        break;
193    }
194    return $errors;
195  }
196
197  /**
198  *  Get plugins defined in the plugin directory
199  */ 
200  function get_fs_plugins()
201  {
202    $dir = opendir(PHPWG_PLUGINS_PATH);
203    while ($file = readdir($dir))
204    {
205      if ($file!='.' and $file!='..')
206      {
207        $path = PHPWG_PLUGINS_PATH.$file;
208        if (is_dir($path) and !is_link($path)
209            and preg_match('/^[a-zA-Z0-9-_]+$/', $file )
210            and file_exists($path.'/main.inc.php')
211            )
212        {
213          $plugin = array(
214              'name'=>$file,
215              'version'=>'0',
216              'uri'=>'',
217              'description'=>'',
218              'author'=>'',
219            );
220          $plg_data = implode( '', file($path.'/main.inc.php') );
221
222          if ( preg_match("|Plugin Name: (.*)|", $plg_data, $val) )
223          {
224            $plugin['name'] = trim( $val[1] );
225          }
226          if (preg_match("|Version: (.*)|", $plg_data, $val))
227          {
228            $plugin['version'] = trim($val[1]);
229          }
230          if ( preg_match("|Plugin URI: (.*)|", $plg_data, $val) )
231          {
232            $plugin['uri'] = trim($val[1]);
233          }
234          if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
235          {
236            $plugin['description'] = trim($desc);
237          }
238          elseif ( preg_match("|Description: (.*)|", $plg_data, $val) )
239          {
240            $plugin['description'] = trim($val[1]);
241          }
242          if ( preg_match("|Author: (.*)|", $plg_data, $val) )
243          {
244            $plugin['author'] = trim($val[1]);
245          }
246          if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
247          {
248            $plugin['author uri'] = trim($val[1]);
249          }
250          if (!empty($plugin['uri']) and strpos($plugin['uri'] , 'extension_view.php?eid='))
251          {
252            list( , $extension) = explode('extension_view.php?eid=', $plugin['uri']);
253            if (is_numeric($extension)) $plugin['extension'] = $extension;
254          }
255          // IMPORTANT SECURITY !
256          $plugin = array_map('htmlspecialchars', $plugin);
257          $this->fs_plugins[$file] = $plugin;
258        }
259      }
260    }
261    closedir($dir);
262  }
263
264  /**
265   * Sort fs_plugins
266   */
267  function sort_fs_plugins($order='name')
268  {
269    switch ($order)
270    {
271      case 'name':
272        uasort($this->fs_plugins, 'name_compare');
273        break;
274      case 'status':
275        $this->sort_plugins_by_state();
276        break;
277      case 'author':
278        uasort($this->fs_plugins, array($this, 'plugin_author_compare'));
279        break;
280      case 'id':
281        uksort($this->fs_plugins, 'strcasecmp');
282        break;
283    }
284  }
285
286  /**
287   * Retrieve PEM server datas to $server_plugins
288   */
289  function get_server_plugins($new=false)
290  {
291    global $user;
292
293    $get_data = array(
294      'category_id' => 12,
295      'format' => 'php',
296    );
297
298    // Retrieve PEM versions
299    $version = PHPWG_VERSION;
300    $versions_to_check = array();
301    $url = PEM_URL . '/api/get_version_list.php';
302    if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
303    {
304      if (!preg_match('/^\d+\.\d+\.\d+/', $version))
305      {
306        $version = $pem_versions[0]['name'];
307      }
308      $branch = substr($version, 0, strrpos($version, '.'));
309      foreach ($pem_versions as $pem_version)
310      {
311        if (strpos($pem_version['name'], $branch) === 0)
312        {
313          $versions_to_check[] = $pem_version['id'];
314        }
315      }
316    }
317    if (empty($versions_to_check))
318    {
319      return false;
320    }
321
322    // Plugins to check
323    $plugins_to_check = array();
324    foreach($this->fs_plugins as $fs_plugin)
325    {
326      if (isset($fs_plugin['extension']))
327      {
328        $plugins_to_check[] = $fs_plugin['extension'];
329      }
330    }
331
332    // Retrieve PEM plugins infos
333    $url = PEM_URL . '/api/get_revision_list.php';
334    $get_data = array_merge($get_data, array(
335      'last_revision_only' => 'true',
336      'version' => implode(',', $versions_to_check),
337      'lang' => substr($user['language'], 0, 2),
338      'get_nb_downloads' => 'true',
339      )
340    );
341
342    if (!empty($plugins_to_check))
343    {
344      if ($new)
345      {
346        $get_data['extension_exclude'] = implode(',', $plugins_to_check);
347      }
348      else
349      {
350        $get_data['extension_include'] = implode(',', $plugins_to_check);
351      }
352    }
353    if (fetchRemote($url, $result, $get_data))
354    {
355      $pem_plugins = @unserialize($result);
356      if (!is_array($pem_plugins))
357      {
358        return false;
359      }
360      foreach ($pem_plugins as $plugin)
361      {
362        $this->server_plugins[$plugin['extension_id']] = $plugin;
363      }
364      return true;
365    }
366    return false;
367  }
368 
369  /**
370   * Sort $server_plugins
371   */
372  function sort_server_plugins($order='date')
373  {
374    switch ($order)
375    {
376      case 'date':
377        krsort($this->server_plugins);
378        break;
379      case 'revision':
380        usort($this->server_plugins, array($this, 'extension_revision_compare'));
381        break;
382      case 'name':
383        uasort($this->server_plugins, array($this, 'extension_name_compare'));
384        break;
385      case 'author':
386        uasort($this->server_plugins, array($this, 'extension_author_compare'));
387        break;
388      case 'downloads':
389        usort($this->server_plugins, array($this, 'extension_downloads_compare'));
390        break;
391    }
392  }
393
394  /**
395   * Extract plugin files from archive
396   * @param string - install or upgrade
397   *  @param string - archive URL
398    * @param string - plugin id or extension id
399   */
400  function extract_plugin_files($action, $revision, $dest)
401  {
402    if ($archive = tempnam( PHPWG_PLUGINS_PATH, 'zip'))
403    {
404      $url = PEM_URL . '/download.php';
405      $get_data = array(
406        'rid' => $revision,
407        'origin' => 'piwigo_'.$action,
408      );
409
410      if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle, $get_data))
411      {
412        fclose($handle);
413        include(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
414        $zip = new PclZip($archive);
415        if ($list = $zip->listContent())
416        {
417          foreach ($list as $file)
418          {
419            // we search main.inc.php in archive
420            if (basename($file['filename']) == 'main.inc.php'
421              and (!isset($main_filepath)
422              or strlen($file['filename']) < strlen($main_filepath)))
423            {
424              $main_filepath = $file['filename'];
425            }
426          }
427          if (isset($main_filepath))
428          {
429            $root = dirname($main_filepath); // main.inc.php path in archive
430            if ($action == 'upgrade')
431            {
432              $extract_path = PHPWG_PLUGINS_PATH . $dest;
433            }
434            else
435            {
436              $extract_path = PHPWG_PLUGINS_PATH
437                  . ($root == '.' ? 'extension_' . $dest : basename($root));
438            }
439            if($result = $zip->extract(PCLZIP_OPT_PATH, $extract_path,
440                                       PCLZIP_OPT_REMOVE_PATH, $root,
441                                       PCLZIP_OPT_REPLACE_NEWER))
442            {
443              foreach ($result as $file)
444              {
445                if ($file['stored_filename'] == $main_filepath)
446                {
447                  $status = $file['status'];
448                  break;
449                }
450              }
451              if (file_exists($extract_path.'/obsolete.list')
452                and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
453                and !empty($old_files))
454              {
455                array_push($old_files, 'obsolete.list');
456                foreach($old_files as $old_file)
457                {
458                  $path = $extract_path.'/'.$old_file;
459                  if (is_file($path))
460                  {
461                    @unlink($path);
462                  }
463                  elseif (is_dir($path))
464                  {
465                    if (!$this->deltree($path))
466                    {
467                      $this->send_to_trash($path);
468                    }
469                  }
470                }
471              }
472            }
473            else $status = 'extract_error';
474          }
475          else $status = 'archive_error';
476        }
477        else $status = 'archive_error';
478      }
479      else $status = 'dl_archive_error';
480    }
481    else $status = 'temp_path_error';
482
483    @unlink($archive);
484    return $status;
485  }
486 
487  /**
488   * delete $path directory
489   * @param string - path
490   */
491  function deltree($path)
492  {
493    if (is_dir($path))
494    {
495      $fh = opendir($path);
496      while ($file = readdir($fh))
497      {
498        if ($file != '.' and $file != '..')
499        {
500          $pathfile = $path . '/' . $file;
501          if (is_dir($pathfile))
502          {
503            $this->deltree($pathfile);
504          }
505          else
506          {
507            @unlink($pathfile);
508          }
509        }
510      }
511      closedir($fh);
512      return @rmdir($path);
513    }
514  }
515
516  /**
517   * send $path to trash directory
518   * @param string - path
519   */
520  function send_to_trash($path)
521  {
522    $trash_path = PHPWG_PLUGINS_PATH . 'trash';
523    if (!is_dir($trash_path))
524    {
525      @mkdir($trash_path);
526      $file = @fopen($trash_path . '/.htaccess', 'w');
527      @fwrite($file, 'deny from all');
528      @fclose($file);
529    }
530    while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
531    {
532      if (!is_dir($r))
533      {
534        @rename($path, $r);
535        break;
536      }
537    }
538  }
539
540  /**
541   * Sort functions
542   */
543  function plugin_version_compare($a, $b)
544  {
545    if (strtolower($a) == 'auto') return false;
546
547    $pattern = array('/([a-z])/ei', '/\.+/', '/\.\Z|\A\./');
548    $replacement = array( "'.'.intval('\\1', 36).'.'", '.', '');
549
550    $array = preg_replace($pattern, $replacement, array($a, $b));
551
552    return version_compare($array[0], $array[1], '>=');
553  }
554
555  function extension_revision_compare($a, $b)
556  {
557    if ($a['revision_date'] < $b['revision_date']) return 1;
558    else return -1;
559  }
560
561  function extension_name_compare($a, $b)
562  {
563    return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
564  }
565
566  function extension_author_compare($a, $b)
567  {
568    $r = strcasecmp($a['author_name'], $b['author_name']);
569    if ($r == 0) return $this->extension_name_compare($a, $b);
570    else return $r;
571  }
572
573  function plugin_author_compare($a, $b)
574  {
575    $r = strcasecmp($a['author'], $b['author']);
576    if ($r == 0) return name_compare($a, $b);
577    else return $r;
578  }
579
580  function extension_downloads_compare($a, $b)
581  {
582    if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1;
583    else return -1;
584  }
585
586  function sort_plugins_by_state()
587  {
588    uasort($this->fs_plugins, 'name_compare');
589
590    $active_plugins = array();
591    $inactive_plugins = array();
592    $not_installed = array();
593
594    foreach($this->fs_plugins as $plugin_id => $plugin)
595    {
596      if (isset($this->db_plugins_by_id[$plugin_id]))
597      {
598        $this->db_plugins_by_id[$plugin_id]['state'] == 'active' ?
599          $active_plugins[$plugin_id] = $plugin : $inactive_plugins[$plugin_id] = $plugin;
600      }
601      else
602      {
603        $not_installed[$plugin_id] = $plugin;
604      }
605    }
606    $this->fs_plugins = $active_plugins + $inactive_plugins + $not_installed;
607  }
608}
609?>
Note: See TracBrowser for help on using the repository browser.