[0004212] Work Order Entry: fixed error when voided WO refence is reused.
[fa-stable.git] / includes / packages.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 include_once($path_to_root. "/includes/archive.inc");
13 include_once($path_to_root. "/includes/remote_url.inc");
14 include_once($path_to_root. "/includes/hooks.inc");
15
16 define('PKG_CACHE_PATH', $path_to_root.'/modules/_cache');
17 define('PUBKEY_PATH', $path_to_root);
18 //
19 // FrontAccounting package class
20 //
21 class package extends gzip_file {
22
23         function __construct($filename, $basedir=null)
24         {
25                 global $path_to_root;
26
27                 if (!$basedir) {
28                         $basedir = PKG_CACHE_PATH.'/'.substr(basename($filename), 0, -4);
29                         if (file_exists($basedir)) {
30 //                              flush_dir($basedir, true); 
31                         } else
32                         mkdir($basedir);
33                 }
34                 parent::__construct($filename);
35                 $this->set_options(array('basedir'=> $basedir));
36                 $this->options['type'] = "pkg";
37         }
38         //
39         //      Used by archive class. Use create_archive() instead.
40         //      
41         function create_pkg() 
42         {
43                 return $this->create_gzip();
44         }
45         //
46         //      Install package and clean temp directory.
47         //
48         function install()
49         {
50                 global $path_to_root;
51                 
52                 $success = true;
53
54                 $this->set_options(array('overwrite' => 1));
55                 $this->extract_files(); // extract package in cache directory
56                 $cachepath = $this->options['basedir'];
57                 $ctrl = get_control_file("$cachepath/_init/config");
58
59                 $targetdir = $path_to_root.'/'.$ctrl['InstallPath'];
60
61                 if (!is_dir($targetdir))
62                         mkdir($targetdir);
63
64                 $dpackage = new package("$cachepath/_data", $targetdir);
65                 $dpackage->set_options(array('overwrite' => 1));
66
67                 $flist = $dpackage->extract_files(true);
68                 if (count($dpackage->error)) {
69                         $this->error = array_merge($this->error, $dpackage->error);
70                         return false;
71                 }
72                 copy_files($flist, $targetdir, "$cachepath/_back");
73         
74                 $dpackage->extract_files(); //install package in target directory
75
76                 $install = hook_invoke($ctrl['Package'], 'install_extension', $dummy);
77                 $success &= $install===null || $install;
78                 $success &= count($dpackage->error) == 0;
79                 $this->error = array_merge($this->error, $dpackage->error);
80                 return $success;
81         }
82         //
83         //      Removing package related sources
84         //
85         function uninstall()
86         {
87                 global $path_to_root;
88
89                 $success = true;
90
91                 $cachepath = $this->options['basedir'];
92                 $ctrl = get_control_file("$cachepath/_init/config");
93
94                 $targetdir = $path_to_root.'/'.$ctrl['InstallPath'];
95
96                 $dpackage = new package("$cachepath/_data", $targetdir);
97
98                 $flist = $dpackage->extract_files(true);
99
100                 $success &= copy_files($flist, "$cachepath/_back", $targetdir, true);
101
102                 if (strpos($ctrl['InstallPath'], 'modules/') === 0) { // flush module directory
103                         flush_dir($targetdir, true);
104                         rmdir($targetdir);
105                 }
106
107                 $uninstall = hook_invoke($ctrl['Package'], 'uninstall_extension', $dummy);
108                 $success &= $uninstall===null || $uninstall;
109
110                 return $success;
111         }
112         //
113         //      Purge all package related configuration and data.
114         //
115         function purge()
116         {
117                 return true;
118         }
119
120 }
121 //
122 // Changes field value read from control file (single, or multiline) into 
123 // arrays of subfields if needed.
124 //
125 function ufmt_property($key, $value)
126 {
127         // indexes used in output arrays
128         $sub_fields = array(
129 //              'MenuTabs' => array('url', 'access', 'tab_id', 'title', 'section'),
130 //              'MenuEntries' => array('url', 'access', 'tab_id', 'title'),
131         );
132         if (!isset($sub_fields[$key]))
133                 return $value==='' ? null : $value;
134
135         $prop = array();
136
137         if (!is_array($value))
138                 $value = array($value);
139         foreach($value as $line) {
140                 $indexes = $sub_fields[$key];
141                 $ret = array();
142                 preg_match_all('/(["])(?:\\\\?+.)*?\1|[^"\s][\S]*/', $line, $match);
143                 foreach($match[0] as $n => $subf) {
144                         if ($match[1][$n])
145                                 $val = strtr(substr($subf, 1, -1),
146                                         array('\\"'=>'"'));
147                 else
148                                 $val = $subf;
149                         if (count($indexes))
150                                 $ret[array_shift($indexes)] = $val;
151                         else
152                                 $ret[] = $val;
153                 }
154                 if (count($ret))
155                         $prop[] = $ret;
156         }
157         return $prop;
158 }
159 //=============================================================================
160 //
161 // Retrieve control file and return as associative array
162 //      $index is name of field used as key in result array, or null for numeric keys
163 //
164 function get_control_file($file, $index = false) {
165
166         $list = gzopen($file, 'rb');
167         if (!$list) return null;
168
169         $repo = $pkg = array();
170         $key = false; $value = '';
171         $line = '';
172         do {
173                 $line = rtrim($line);
174                 if ($line && ctype_space($line[0])) { // continuation of multiline property
175                         if (strlen(ltrim($line))) {
176                                 if ($value !== '' && !is_array($value))
177                                         $value = array($value);
178                                 $value[] = ltrim($line);
179                                 continue;
180                         }
181                 }
182                 if ($key) { // save previous property if any
183                         $pkg[$key] = ufmt_property($key, $value);
184                 }
185                 if (!strlen($line)) { // end of section
186                         if (count($pkg)) {
187                                 if ($index !== true) {
188                                         if ($index === false) break;
189                                         if (!isset($pkg[$index])) {
190                                                 display_error(sprintf(_("No key field '%s' in file '%s'"), $index, $file));
191                                                 return null;
192                                         }
193                                         $repo[$pkg[$index]] = $pkg;
194                                 } else
195                                         $repo[] = $pkg;
196                         }
197                         $pkg = array(); 
198                         $key = null; $value = '';
199                         continue;
200                 } elseif (preg_match('/([^:]*):\s*(.*)/', $line, $m)) {
201                         $key = $m[1]; $value = $m[2];
202                         if (!strlen($key)) {
203                                 display_error("Empty key in line $line");
204                                 return null;
205                         }
206                 } else {
207                         display_error("File parse error in line $line");
208                         return null;
209                 }
210                 
211         } while ((($line = fgets($list))!==false) || $key);
212         fclose($list);
213
214         return $index === false ? $pkg : $repo;
215 }
216 //
217 //      Save configuration data to control file.
218 //
219 function save_control_file($fname, $list, $zip=false) 
220 {
221         $file = $zip ?  gzopen($fname, 'wb') : fopen($fname, 'wb');
222         foreach($list as $section) {
223                 foreach($section as $key => $value) {
224                         if (is_array($value)) { // multiline value
225                                 if (is_array(reset($value))) { // lines have subfields
226                                         foreach($value as $i => $line) {
227                 // Subfields containing white spaces or double quotes are doublequoted 
228                 // with " escaped with backslash.
229                                                 foreach($line as $n => $subfield)
230                                                         if (preg_match('/[\s"]/', $subfield)) {
231                                                                 $value[$i][$n] = 
232                                                                         '"'.strtr($subfield, array('"'=>'\\"')).'"';
233                                                         }
234                                                 // Subfields are separated by white space.
235                                                 $value[$i] = implode(' ', $value[$i]);
236                                         }
237                                 }
238                                 // array elements on subsequent lines starting with white space
239                                 $value = implode("\n ", $value);
240                         }
241                         $zip ? gzwrite($file, "$key: $value\n") : fwrite($file, "$key: $value\n");
242                 }
243                 $zip ? gzwrite($file, "\n"): fwrite($file, "\n");
244         }
245         $zip ? gzclose($file) : fclose($file);
246 }
247 //
248 //      Retrieve text field in localized version or default one 
249 //      when the localized is not avaialable.
250 //
251 function pkg_prop($pkg, $property, $lang=false) 
252 {
253         
254         if ($lang && isset($pkg[$property.'-'.user_language()]))
255                 $prop = @$pkg[$pname];
256         else
257                 $prop = @$pkg[$property];
258
259         return is_array($prop) ? implode("\n ",$prop): $prop;
260 }
261 //
262 //      Retrieve list of packages from repository and return as table ($pkgname==null),
263 //      or find $pkgname package in repository and optionaly download
264 //
265 //      $type is type/s of package
266 //  $filter is optional field selection array in form field=>newkey
267 //              or (0=>field1, 1=>field2...)
268 //  $outkey - field used as a key in package list. If null 'Package' field is used.
269 //
270 function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey=null, $download=true) {
271
272         global $path_to_root, $repo_auth;
273
274         $repo =  (isset($repo_auth['scheme']) ? $repo_auth['scheme'] : 'http://')
275                         .(isset($repo_auth['login'])  ? $repo_auth['login'].':' : '')
276                         .(isset($repo_auth['pass'])   ? $repo_auth['pass'].'@' : '')
277                         .(isset($repo_auth['host'])   ? $repo_auth['host'].'/' : '')
278                         .(isset($repo_auth['path'])   ? $repo_auth['path'].'/' : '')
279                         .$repo_auth['branch'];
280
281         // first download local copy of repo release file
282         // and check remote signature with local copy of public key
283         //
284         $loclist = PKG_CACHE_PATH.'/Release.gz';
285         $target_dir = $download==true ? VARLIB_PATH."/" : $download;
286
287         if (isset($type) && !is_array($type)) {
288                 $type = array($type);
289         }
290         $refresh = true;
291         do{
292                 if (!file_exists($loclist)) {
293                         if (!url_copy($repo.'/Release.gz', $loclist))
294                         {
295                                 display_error(_("Cannot download repo index file." ));
296                                 return null;
297                         }
298                         $refresh = false;
299                 }
300                 $sig = url_get_contents($repo.'/Release.sig');
301                 $data = file_get_contents($loclist);
302                 $cert = file_get_contents(PUBKEY_PATH.'/FA.pem');
303                 if (!function_exists('openssl_verify')) {
304                         display_error(_("OpenSSL have to be available on your server to use extension repository system."));
305                         return null;
306                 }       
307                 if (openssl_verify($data, $sig, $cert) <= 0) {
308                         if ($refresh) {
309                                 if (!@unlink($loclist))
310                                 {
311                                         display_error(sprintf(_("Cannot delete outdated '%s' file."), $loclist));
312                                         return null;
313                                 }
314                         } else {
315                                 display_error(_('Release file in repository is invalid, or public key is outdated.'));
316                                 return null;
317                         }
318                 } else
319                         $refresh = false;
320
321         } while($refresh);
322
323         $Release = get_control_file($loclist, 'Filename');
324         // download and check all indexes containing given package types
325         // then complete package list or seek for pkg
326         $Packages = array();
327         foreach($Release as $fname => $parms) {
328                 if ($type && !count(array_intersect(explode(' ', $parms['Type']), $type))) {
329                         unset($Release[$fname]); continue; // no packages of selected type in this index
330                 }
331                 if ($Release[$fname]['Version'] != $repo_auth['branch']) {
332                         display_warning(_('Repository version does not match application version.')); // ?
333                 }
334                 $remoteindex = $repo.'/'.$fname;
335                 $locindex = PKG_CACHE_PATH.'/'.$fname;
336                 $refresh = true;
337                 do{
338                         if (!file_exists($locindex)) { 
339                                 if (!url_copy($remoteindex, $locindex)) {
340                                         display_error(sprintf(_("Cannot download '%s' file." ), $fname));
341                                         return null;
342                                 }
343                                 $refresh = false;
344                         }
345                         if ($parms['SHA1sum'] != sha1_file($locindex)) {        // check subdir index consistency
346                                 if ($refresh) {
347                                         if (!@unlink($locindex)) {
348                                                 display_error(sprintf(_("Cannot delete outdated '%s' file."), $locindex));
349                                                 return null;
350                                         }
351                                 } else {
352                                         display_error(sprintf( _("Security alert: broken index file in repository '%s'. Please inform repository administrator about this issue."),
353                                                 $fname));
354                                         return null;
355                                 }
356                         } else
357                                 $refresh = false;
358                 } while($refresh);
359                 
360                  // scan subdir list and select packages of given type
361                 $pkglist = get_control_file($locindex, 'Package');
362                 foreach($pkglist as $name => $pkg) {
363                         $pkgfullname = $repo.'/'.$parms['Path']."/".$pkg['Filename'].'.pkg';
364                         if (!isset($type) || in_array($pkg['Type'], $type)) {
365                                 if (empty($filter))
366                                         $p = $pkg;
367                                 else {
368                                         foreach($filter as $field => $key) {
369                                                 if (is_numeric($field))
370                                                         $p[$field] = @$pkg[$field];
371                                                 else
372                                                         $p[$key] = @$pkg[$field];
373                                         }
374                                 }
375                                 if ($pkgname == null) {
376                                         $Packages[$outkey ? $outkey : $name] = $p;
377                                 } elseif ($pkgname == $pkg['Package']) {
378                                         //download package to temp directory
379                                         if ($download) {
380                                                 $locname = $target_dir.$pkg['Filename'].'.pkg';
381                                                 if (!url_copy($pkgfullname, $locname)) {
382                                                         display_error(sprintf(_("Cannot download '%s' file." ), $pkgfullname));
383                                                         return null;
384                                                 }
385                                                  // checking sha1 hash is expensive proces, so chekc the package
386                                                  // consistency just before downloading
387                                                 if ($pkg['SHA1sum'] != sha1_file($locname)) {
388                                                         display_error(sprintf( _("Security alert: broken package '%s' in repository. Please inform repository administrator about this issue."),
389                                                                 $pkgfullname));
390                                                         return null;
391                                                 }
392                                         }
393                                         return $p;
394                                 }
395                         }
396                 }
397         }
398
399         return $Packages;
400 }
401
402 function get_package($pkgname, $type = null)
403 {
404         return get_pkg_or_list($type, $pkgname);
405 }
406 /*
407         Returns full name of installed package, or null if package is not installed.
408 */
409 function installed_package($package)
410 {
411         $cache = opendir(PKG_CACHE_PATH);
412
413         while ($file = @readdir($cache)) {
414                 if (!is_dir(PKG_CACHE_PATH.'/'.$file))
415                         continue;
416                 if (strpos($file, $package.'-') === 0)
417                         return $file;
418         }
419         @closedir($cache);
420
421         return null;
422 }
423 /*
424         Remove package from system
425 */
426 function uninstall_package($name)
427 {
428         $name = installed_package($name);
429         if (!$name) return true; // not installed
430         $pkg = new package($name.'.pkg');
431         $pkg->uninstall();
432         if($name) {
433                 flush_dir(PKG_CACHE_PATH.'/'.$name, true);
434                 rmdir(PKG_CACHE_PATH.'/'.$name);
435         }
436         return count($pkg->error)==0;
437 }
438
439 //---------------------------------------------------------------------------------------
440 //
441 //      Return merged list of available and installed languages in inform of local 
442 // configuration array supplemented with installed versions information.
443 //
444 function get_languages_list()
445 {
446         global $installed_languages;
447         
448         $pkgs = get_pkg_or_list('language', null, array(
449                                 'Package' => 'package',
450                                 'Version' => 'available',
451                                 'Name' => 'name',
452                                 'Language' => 'code',
453                                 'Encoding' => 'encoding',
454                                 'RTLDir' => 'rtl',
455                                 'Description' => 'Descr',
456                                 'InstallPath' => 'path'
457                         ));
458
459         // add/update languages already installed
460         // 
461         foreach($installed_languages as $id => $l) {
462                 $list = array_search_keys($l['code'], $pkgs, 'code');   // get all packages with this code
463                 foreach ($list as $name) {
464                         if ($l['encoding'] == $pkgs[$name]['encoding']) {       // if the same encoding
465                                 $pkgs[$name]['version'] = @$l['version'];               // set installed version
466                                 $pkgs[$name]['local_id'] = $id;         // index in installed_languages
467                                 continue 2;
468                         }
469                 }
470                 $l['local_id'] = $id;
471                 if (!isset($l['package']) || $l['package'] == '' || !isset($pkgs[$l['package']]))
472                         $pkgs[] = $l;
473                 else
474                         $pkgs[$l['package']] = array_merge($pkgs[$l['package']], $l);
475         }
476         if ($pkgs)
477                 ksort($pkgs);
478         return $pkgs;
479 }
480 //---------------------------------------------------------------------------------------
481 //
482 //      Return merged list of available and installed extensions as a local 
483 // configuration array supplemented with installed versions information.
484 //
485 function get_extensions_list($type = null)
486 {
487         global $path_to_root;
488
489         if (isset($type) || !is_array($type)) {
490                 $type = array($type);
491         }
492
493         $pkgs = get_pkg_or_list($type, null, array(
494                                 'Package' => 'package',
495                                 'Version' => 'available',
496                                 'Name' => 'name',
497                                 'Description' => 'Descr',
498                                 'Type' => 'type',
499                                 'DefaultStatus' => 'active',
500 //                              'MenuTabs' => 'tabs',
501 //                              'MenuEntries' => 'entries',
502                                 'Encoding' => 'encoding',
503 //                              'AccessExtensions' => 'acc_file',
504                                 'InstallPath' => 'path'
505                         ));
506
507         // lookup for local extensions
508         $path = $path_to_root.'/modules/';
509         $loc = array();
510         $moddir = opendir($path);
511
512         while(false != ($fname = readdir($moddir)))
513         {
514                 if(!in_array($fname, array('.','..','CVS','_cache')) && is_dir($path.$fname))
515                 {
516                         if (!isset($pkgs[$fname]))
517                                 $pkgs[$fname] = array(
518                                         'package' => $fname,
519                                         'name' => $fname,
520                                         'version' => '',
521                                         'available' => '',
522                                         'type' => 'extension',
523                                         'path' => 'modules/'.$fname,
524                                         'active' => false
525                                         );
526                 }
527         }
528
529         // add/update extensions already installed
530         // 
531         $installed = get_company_extensions();
532         foreach($installed as $extno => $ext) {
533                 if (!in_array($ext['type'], $type)) continue;
534                 $ext['local_id'] = $extno;
535 //              if (!isset($pkgs[$ext['package']]) || $ext['package'] == '')
536 //                      $pkgs[] = $ext;
537 //              else
538                         $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
539         }
540         if ($pkgs)
541                 ksort($pkgs);
542         return $pkgs;
543 }
544 //
545 // Return merged list of available and installed extensions as a local
546 // configuration array supplemented with installed versions information.
547 //
548 function get_themes_list()
549 {
550         $pkgs = get_pkg_or_list('theme', null, array(
551                                 'Package' => 'package',
552                                 'Version' => 'available',
553                                 'Name' => 'name',
554                                 'Description' => 'Descr'
555                         ));
556
557         // add/update extensions already installed
558         // 
559         $local = get_company_extensions();
560         
561         foreach($local as $extno => $ext) {
562                 if (isset($pkgs[@$ext['package']])) {
563                         $ext['local_id'] = $extno;
564                         $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
565                 }
566         }
567         // TODO: Add other themes from themes directory
568         if ($pkgs)
569                 ksort($pkgs);
570         return $pkgs;
571 }
572 //---------------------------------------------------------------------------------------
573 //
574 //      Return merged list of available and installed COAs as a local 
575 // configuration array supplemented with installed versions information.
576 //
577 function get_charts_list()
578 {
579         $pkgs = get_pkg_or_list('chart', null, array(
580                                 'Package' => 'package',
581                                 'Version' => 'available',
582                                 'Name' => 'name',
583                                 'Description' => 'Descr',
584                                 'Type' => 'type',
585                                 'InstallPath' => 'path',
586                                 'Encoding' => 'encoding',
587                                 'SqlScript' => 'sql'
588                         ));
589
590         // add/update default charts
591         // 
592         $local = get_company_extensions();
593
594         foreach($local as $extno => $ext) {
595                 if ($ext['type'] != 'chart') continue;
596                 $ext['local_id'] = $extno;
597                 if (!isset($pkgs[$ext['package']]) || $ext['package'] == '')
598                         $pkgs[] = $ext;
599                 else
600                         $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
601         }
602         if ($pkgs)
603                 ksort($pkgs);
604         return $pkgs;
605 }
606 //---------------------------------------------------------------------------------------------
607 //      Install/update package from repository
608 //
609 function install_language($pkg_name)
610 {
611         global $path_to_root, $installed_languages, $Ajax;
612         
613         $pkg = get_pkg_or_list('language', $pkg_name);
614
615         if ($pkg) {
616                 $i = array_search_key($pkg['Language'], $installed_languages, 'code');
617                 if ($i === null)
618                         $i = count($installed_languages);
619                 else {  // remove another already installed package for this language 
620                         $old_pkg = @$installed_languages[$i]['package'];
621                         if ($old_pkg && ($pkg['Package'] != $old_pkg))
622                                 uninstall_package($old_pkg);
623                 }
624
625                 $package = new package(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
626                 if ($package->install()) {
627                         $lang = array(
628                                 'name' => $pkg['Name'],
629                                 'package' => $pkg['Package'],
630                                 'code' => $pkg['Language'],
631                                 'encoding' => $pkg['Encoding'],
632                                 'version' => $pkg['Version'],
633                                 'path' => $pkg['InstallPath']
634                         );
635                         if ($pkg['RTLDir']=='yes')
636                                 $lang['rtl'] = true;
637                         $installed_languages[$i] = $lang;
638                         write_lang($installed_languages);
639                         unlink(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
640                         $Ajax->activate('lang_tbl');
641                 } else {
642                         display_error(implode('<br>', $package->error));
643                         return false;
644                 }
645         } else {
646                 display_error(sprintf(_("Package '%s' not found."), $pkg_name));
647                 return false;
648         }
649         return true;
650 }
651 //---------------------------------------------------------------------------------------------
652 //      Install/update extension or theme package from repository
653 //
654 function install_extension($pkg_name)
655 {
656         global $path_to_root, $installed_extensions, $next_extension_id, $Ajax, $db_connections;
657         
658         $pkg = get_pkg_or_list(array('extension', 'theme', 'chart'), $pkg_name);
659         if ($pkg) {
660                 $package = new package(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
661                 $local_exts = get_company_extensions();
662                 if ($package->install()) {
663                         $ext_id = array_search_key($pkg['Package'], $local_exts, 'package');
664                         if ($ext_id === null)
665                                 $ext_id = $next_extension_id++;
666                         else {  // remove another already installed package for this language 
667                                 $old_pkg = $installed_extensions[$ext_id]['package'];
668                                 if ($old_pkg)
669                                         uninstall_package($old_pkg);
670                         }
671                         $ext = array(
672                                 'name' => $pkg['Name'],
673                                 'package' => $pkg['Package'],
674                                 'version' => $pkg['Version'],
675                                 'type' => $pkg['Type'],
676                                 'active' => @$pkg['DefaultStatus'] == 'active' ? true : false,
677                                 'path' => $pkg['InstallPath'],
678                         );
679                         if (isset($pkg['SqlScript']))
680                                 $ext['sql'] = $pkg['SqlScript'];
681
682                         $local_exts[$ext_id] = $ext;
683                         $ret = update_extensions($local_exts);
684
685                         if (($ext['active'] == true) && file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
686                         {
687                                 // we need to include the new hooks file to activate extension
688                                 include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
689                                 foreach($db_connections as $comp => $db)
690                                         activate_hooks($ext['package'], $comp);
691                         }
692
693                         unlink(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
694                         $Ajax->activate('ext_tbl');
695                         return $ret;
696                 } else {
697                         display_error(implode('<br>', $package->error));
698                         return false;
699                 }
700         } else {
701                 display_error(sprintf(_("Package '%s' not found."), $pkg_name));
702                 return false;
703         }
704         return true;
705 }
706 /*
707         Returns true if newer package version is available
708 */
709 function check_pkg_upgrade($current, $available)
710 {
711         preg_match_all('/[\d]+/', $available, $aver);
712         if (!count($aver[0]))
713                 return false;
714         preg_match_all('/[\d]+/', $current, $cver);
715         if (!count($cver[0]))
716                 return true;
717         foreach($aver[0] as $n => $ver)
718                 if ($ver>@$cver[0][$n]) 
719                         return true;
720         return false;
721 }
722
723 //
724 //      Returns package info from index file
725 //
726 function get_package_info($pkg, $type=null, $filter=array(), $outkey=null, $download=true) {
727         return get_pkg_or_list($type, $pkg, $filter, null, false);
728 }
729
730 /*
731         Check basic extension source compatibility.
732 */
733 function check_src_ext_version($ext_v)
734 {
735     global $src_version;
736     if ($ext_v != '-') {
737         $compat_levels = 2;    // current policy is keeping compatibility on major version level.
738         $app = explode('.', substr($src_version, 0, strspn($src_version, "0123456789.")));
739         $pkg = explode('.', substr($ext_v, 0, strspn($ext_v, "0123456789.")));
740
741         for ($i=0; $i < min($compat_levels, count($app)); $i++)
742             if ($pkg[$i] < $app[$i])
743                 return false;
744     }
745     return true;
746 }