[0004216] Print Work Orders: database error fixed when voided WO is in selected range.
[fa-stable.git] / includes / packages.inc
index 70b11c15f61e6fd8042c21ba057961755ca0b8d6..54d16abf5b4bf31cd15ff29f8b048a6acd0b9c94 100644 (file)
     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
 ***********************************************************************/
 include_once($path_to_root. "/includes/archive.inc");
+include_once($path_to_root. "/includes/remote_url.inc");
+include_once($path_to_root. "/includes/hooks.inc");
 
 define('PKG_CACHE_PATH', $path_to_root.'/modules/_cache');
 define('PUBKEY_PATH', $path_to_root);
-define('REPO_URL', "$repository/$FA_repo_version");
-/*
-$pkg_data_basedir = array(
-       'language'      => '/lang/', 
-//     'module'        => '/', 
-       'extension'     => '/modules/', 
-//     'plugin'        => '/modules/',
-       'theme'         => '/themes/');
-*/
 //
-// FrontAccounting package class       
+// FrontAccounting package class
 //
 class package extends gzip_file {
-       function package($filename, $basedir=null)
+
+       function __construct($filename, $basedir=null)
        {
                global $path_to_root;
 
@@ -37,7 +31,7 @@ class package extends gzip_file {
                        } else
                        mkdir($basedir);
                }
-               $this->archive($filename);
+               parent::__construct($filename);
                $this->set_options(array('basedir'=> $basedir));
                $this->options['type'] = "pkg";
        }
@@ -62,11 +56,6 @@ class package extends gzip_file {
                $cachepath = $this->options['basedir'];
                $ctrl = get_control_file("$cachepath/_init/config");
 
-//             $targetdir = $pkg_data_basedir[$ctrl['Type']];
-//             if ($targetdir)
-//                     $targetdir = $path_to_root.$targetdir.$ctrl['Package'];
-//             else
-//                     $targetdir = $path_to_root;
                $targetdir = $path_to_root.'/'.$ctrl['InstallPath'];
 
                if (!is_dir($targetdir))
@@ -84,7 +73,8 @@ class package extends gzip_file {
        
                $dpackage->extract_files(); //install package in target directory
 
-               $success &= $this->support('install');
+               $install = hook_invoke($ctrl['Package'], 'install_extension', $dummy);
+               $success &= $install===null || $install;
                $success &= count($dpackage->error) == 0;
                $this->error = array_merge($this->error, $dpackage->error);
                return $success;
@@ -100,11 +90,7 @@ class package extends gzip_file {
 
                $cachepath = $this->options['basedir'];
                $ctrl = get_control_file("$cachepath/_init/config");
-//             $targetdir = $pkg_data_basedir[$ctrl['Type']];
-//             if ($targetdir)
-//                     $targetdir = $path_to_root.$targetdir.$ctrl['Package'];
-//             else
-//                     $targetdir = $path_to_root;
+
                $targetdir = $path_to_root.'/'.$ctrl['InstallPath'];
 
                $dpackage = new package("$cachepath/_data", $targetdir);
@@ -113,7 +99,13 @@ class package extends gzip_file {
 
                $success &= copy_files($flist, "$cachepath/_back", $targetdir, true);
 
-               $success &= $this->support('uninstall');
+               if (strpos($ctrl['InstallPath'], 'modules/') === 0) { // flush module directory
+                       flush_dir($targetdir, true);
+                       rmdir($targetdir);
+               }
+
+               $uninstall = hook_invoke($ctrl['Package'], 'uninstall_extension', $dummy);
+               $success &= $uninstall===null || $uninstall;
 
                return $success;
        }
@@ -125,25 +117,6 @@ class package extends gzip_file {
                return true;
        }
 
-       //
-       //      Call special function defined in package install class
-       //
-       function support($name, $params = null)
-       {
-               $cachepath = $this->options['basedir'];
-               if (file_exists("$cachepath/_init/init.php")) {
-                       include("$cachepath/_init/init.php");
-                       if (method_exists($installer, $name)) {
-                               set_include_path("$cachepath/_init".PATH_SEPARATOR.get_include_path());
-//                             $old = getcwd();
-//                             chdir("$cachepath/_init");
-                               $ret = $installer->$name($params);
-//                             chdir($old);
-                               return $ret;
-                       }
-               }
-               return true;
-       }
 }
 //
 // Changes field value read from control file (single, or multiline) into 
@@ -153,8 +126,8 @@ function ufmt_property($key, $value)
 {
        // indexes used in output arrays
        $sub_fields = array(
-               'MenuTabs' => array('url', 'access', 'tab_id', 'title', 'section'),
-               'MenuEntries' => array('url', 'access', 'tab_id', 'title'),
+//             'MenuTabs' => array('url', 'access', 'tab_id', 'title', 'section'),
+//             'MenuEntries' => array('url', 'access', 'tab_id', 'title'),
        );
        if (!isset($sub_fields[$key]))
                return $value==='' ? null : $value;
@@ -198,7 +171,7 @@ function get_control_file($file, $index = false) {
        $line = '';
        do {
                $line = rtrim($line);
-               if (@ctype_space($line[0])) { // continuation of multiline property
+               if ($line && ctype_space($line[0])) { // continuation of multiline property
                        if (strlen(ltrim($line))) {
                                if ($value !== '' && !is_array($value))
                                        $value = array($value);
@@ -214,7 +187,7 @@ function get_control_file($file, $index = false) {
                                if ($index !== true) {
                                        if ($index === false) break;
                                        if (!isset($pkg[$index])) {
-                                               display_error(_("No key field '$index' in file '$file'"));
+                                               display_error(sprintf(_("No key field '%s' in file '%s'"), $index, $file));
                                                return null;
                                        }
                                        $repo[$pkg[$index]] = $pkg;
@@ -224,7 +197,6 @@ function get_control_file($file, $index = false) {
                        $pkg = array(); 
                        $key = null; $value = '';
                        continue;
-//             } elseif (preg_match('/([\w-]*):\s*(.*)/', $line, $m)) {
                } elseif (preg_match('/([^:]*):\s*(.*)/', $line, $m)) {
                        $key = $m[1]; $value = $m[2];
                        if (!strlen($key)) {
@@ -264,8 +236,7 @@ function save_control_file($fname, $list, $zip=false)
                                        }
                                }
                                // array elements on subsequent lines starting with white space
-                               $value = //(count($value) ? "\n " :'').
-                                       implode("\n ", $value);
+                               $value = implode("\n ", $value);
                        }
                        $zip ? gzwrite($file, "$key: $value\n") : fwrite($file, "$key: $value\n");
                }
@@ -298,38 +269,58 @@ function pkg_prop($pkg, $property, $lang=false)
 //
 function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey=null, $download=true) {
 
-       global $path_to_root, $repository, $FA_repo_version;
+       global $path_to_root, $repo_auth;
+
+       $repo =  (isset($repo_auth['scheme']) ? $repo_auth['scheme'] : 'http://')
+                       .(isset($repo_auth['login'])  ? $repo_auth['login'].':' : '')
+                       .(isset($repo_auth['pass'])   ? $repo_auth['pass'].'@' : '')
+                       .(isset($repo_auth['host'])   ? $repo_auth['host'].'/' : '')
+                       .(isset($repo_auth['path'])   ? $repo_auth['path'].'/' : '')
+                       .$repo_auth['branch'];
 
        // first download local copy of repo release file
        // and check remote signature with local copy of public key
        //
        $loclist = PKG_CACHE_PATH.'/Release.gz';
-       
-       if ($type!=null && !is_array($type)) {
+       $target_dir = $download==true ? VARLIB_PATH."/" : $download;
+
+       if (isset($type) && !is_array($type)) {
                $type = array($type);
        }
        $refresh = true;
        do{
                if (!file_exists($loclist)) {
-                       copy(REPO_URL.'/Release.gz', $loclist);
+                       if (!url_copy($repo.'/Release.gz', $loclist))
+                       {
+                               display_error(_("Cannot download repo index file." ));
+                               return null;
+                       }
                        $refresh = false;
                }
-               $sig = file_get_contents(REPO_URL.'/Release.sig', 'rb');
+               $sig = url_get_contents($repo.'/Release.sig');
                $data = file_get_contents($loclist);
                $cert = file_get_contents(PUBKEY_PATH.'/FA.pem');
-               if (!openssl_verify($data, $sig, $cert)) {
-                       if ($refresh)
-                               @unlink($loclist);
-                       else {
+               if (!function_exists('openssl_verify')) {
+                       display_error(_("OpenSSL have to be available on your server to use extension repository system."));
+                       return null;
+               }       
+               if (openssl_verify($data, $sig, $cert) <= 0) {
+                       if ($refresh) {
+                               if (!@unlink($loclist))
+                               {
+                                       display_error(sprintf(_("Cannot delete outdated '%s' file."), $loclist));
+                                       return null;
+                               }
+                       } else {
                                display_error(_('Release file in repository is invalid, or public key is outdated.'));
                                return null;
                        }
                } else
                        $refresh = false;
+
        } while($refresh);
 
        $Release = get_control_file($loclist, 'Filename');
-
        // download and check all indexes containing given package types
        // then complete package list or seek for pkg
        $Packages = array();
@@ -337,21 +328,27 @@ function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey
                if ($type && !count(array_intersect(explode(' ', $parms['Type']), $type))) {
                        unset($Release[$fname]); continue; // no packages of selected type in this index
                }
-               if ($Release[$fname]['Version'] != $FA_repo_version) {
+               if ($Release[$fname]['Version'] != $repo_auth['branch']) {
                        display_warning(_('Repository version does not match application version.')); // ?
                }
-               $remoteindex = REPO_URL.'/'.$fname;
+               $remoteindex = $repo.'/'.$fname;
                $locindex = PKG_CACHE_PATH.'/'.$fname;
                $refresh = true;
                do{
                        if (!file_exists($locindex)) { 
-                               copy($remoteindex, $locindex);
+                               if (!url_copy($remoteindex, $locindex)) {
+                                       display_error(sprintf(_("Cannot download '%s' file." ), $fname));
+                                       return null;
+                               }
                                $refresh = false;
                        }
                        if ($parms['SHA1sum'] != sha1_file($locindex)) {        // check subdir index consistency
-                               if ($refresh)
-                                       @unlink($locindex);
-                               else {
+                               if ($refresh) {
+                                       if (!@unlink($locindex)) {
+                                               display_error(sprintf(_("Cannot delete outdated '%s' file."), $locindex));
+                                               return null;
+                                       }
+                               } else {
                                        display_error(sprintf( _("Security alert: broken index file in repository '%s'. Please inform repository administrator about this issue."),
                                                $fname));
                                        return null;
@@ -363,8 +360,8 @@ function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey
                 // scan subdir list and select packages of given type
                $pkglist = get_control_file($locindex, 'Package');
                foreach($pkglist as $name => $pkg) {
-                       $pkgfullname = REPO_URL.'/'.$parms['Path']."/".$pkg['Filename'].'.pkg';
-                       if ($type==null || in_array($pkg['Type'], $type)) {
+                       $pkgfullname = $repo.'/'.$parms['Path']."/".$pkg['Filename'].'.pkg';
+                       if (!isset($type) || in_array($pkg['Type'], $type)) {
                                if (empty($filter))
                                        $p = $pkg;
                                else {
@@ -380,8 +377,11 @@ function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey
                                } elseif ($pkgname == $pkg['Package']) {
                                        //download package to temp directory
                                        if ($download) {
-                                               $locname = "$path_to_root/tmp/".$pkg['Filename'].'.pkg';
-                                               copy($pkgfullname, $locname);
+                                               $locname = $target_dir.$pkg['Filename'].'.pkg';
+                                               if (!url_copy($pkgfullname, $locname)) {
+                                                       display_error(sprintf(_("Cannot download '%s' file." ), $pkgfullname));
+                                                       return null;
+                                               }
                                                 // checking sha1 hash is expensive proces, so chekc the package
                                                 // consistency just before downloading
                                                if ($pkg['SHA1sum'] != sha1_file($locname)) {
@@ -395,14 +395,13 @@ function get_pkg_or_list($type = null, $pkgname = null, $filter=array(), $outkey
                        }
                }
        }
-       
+
        return $Packages;
 }
 
 function get_package($pkgname, $type = null)
 {
-       $all = get_pkg_or_list($type, $pkgname);
-       $pkg = array_search_value($all, $pkgname, 'Package');
+       return get_pkg_or_list($type, $pkgname);
 }
 /*
        Returns full name of installed package, or null if package is not installed.
@@ -463,56 +462,87 @@ function get_languages_list()
                $list = array_search_keys($l['code'], $pkgs, 'code');   // get all packages with this code
                foreach ($list as $name) {
                        if ($l['encoding'] == $pkgs[$name]['encoding']) {       // if the same encoding
-                               $pkgs[$name]['version'] = $l['version'];                // set installed version
+                               $pkgs[$name]['version'] = @$l['version'];               // set installed version
                                $pkgs[$name]['local_id'] = $id;         // index in installed_languages
                                continue 2;
                        }
                }
                $l['local_id'] = $id;
-               if (!isset($pkgs[$l['package']]) || $l['package'] == '')
+               if (!isset($l['package']) || $l['package'] == '' || !isset($pkgs[$l['package']]))
                        $pkgs[] = $l;
                else
                        $pkgs[$l['package']] = array_merge($pkgs[$l['package']], $l);
        }
-       ksort($pkgs);
+       if ($pkgs)
+               ksort($pkgs);
        return $pkgs;
 }
 //---------------------------------------------------------------------------------------
 //
-//     Return merged list of available and installed extensions in inform of local 
+//     Return merged list of available and installed extensions as a local 
 // configuration array supplemented with installed versions information.
 //
-function get_extensions_list()
+function get_extensions_list($type = null)
 {
-       $pkgs = get_pkg_or_list('extension', null, array(
+       global $path_to_root;
+
+       if (isset($type) || !is_array($type)) {
+               $type = array($type);
+       }
+
+       $pkgs = get_pkg_or_list($type, null, array(
                                'Package' => 'package',
                                'Version' => 'available',
                                'Name' => 'name',
                                'Description' => 'Descr',
                                'Type' => 'type',
-                               'DefaultStatus'=> 'active',
-                               'MenuTabs' => 'tabs',
-                               'MenuEntries' => 'entries',
-                               'AccessExtensions' => 'acc_file',
+                               'DefaultStatus' => 'active',
+//                             'MenuTabs' => 'tabs',
+//                             'MenuEntries' => 'entries',
+                               'Encoding' => 'encoding',
+//                             'AccessExtensions' => 'acc_file',
                                'InstallPath' => 'path'
                        ));
 
+       // lookup for local extensions
+       $path = $path_to_root.'/modules/';
+       $loc = array();
+       $moddir = opendir($path);
+
+       while(false != ($fname = readdir($moddir)))
+       {
+               if(!in_array($fname, array('.','..','CVS','_cache')) && is_dir($path.$fname))
+               {
+                       if (!isset($pkgs[$fname]))
+                               $pkgs[$fname] = array(
+                                       'package' => $fname,
+                                       'name' => $fname,
+                                       'version' => '',
+                                       'available' => '',
+                                       'type' => 'extension',
+                                       'path' => 'modules/'.$fname,
+                                       'active' => false
+                                       );
+               }
+       }
+
        // add/update extensions already installed
        // 
-       $local = get_company_extensions();
-       foreach($local as $extno => $ext) {
-               if ($ext['type'] == 'theme') continue;
+       $installed = get_company_extensions();
+       foreach($installed as $extno => $ext) {
+               if (!in_array($ext['type'], $type)) continue;
                $ext['local_id'] = $extno;
-               if (!isset($pkgs[$ext['package']]) || $ext['package'] == '')
-                       $pkgs[] = $ext;
-               else
+//             if (!isset($pkgs[$ext['package']]) || $ext['package'] == '')
+//                     $pkgs[] = $ext;
+//             else
                        $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
        }
-       ksort($pkgs);
+       if ($pkgs)
+               ksort($pkgs);
        return $pkgs;
 }
 //
-// Return merged list of available and installed extensions in inform of local
+// Return merged list of available and installed extensions as a local
 // configuration array supplemented with installed versions information.
 //
 function get_themes_list()
@@ -529,14 +559,48 @@ function get_themes_list()
        $local = get_company_extensions();
        
        foreach($local as $extno => $ext) {
-               if (isset($pkgs[$ext['package']])) {
+               if (isset($pkgs[@$ext['package']])) {
                        $ext['local_id'] = $extno;
                        $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
                }
        }
        // TODO: Add other themes from themes directory
-       
-       ksort($pkgs);
+       if ($pkgs)
+               ksort($pkgs);
+       return $pkgs;
+}
+//---------------------------------------------------------------------------------------
+//
+//     Return merged list of available and installed COAs as a local 
+// configuration array supplemented with installed versions information.
+//
+function get_charts_list()
+{
+       $pkgs = get_pkg_or_list('chart', null, array(
+                               'Package' => 'package',
+                               'Version' => 'available',
+                               'Name' => 'name',
+                               'Description' => 'Descr',
+                               'Type' => 'type',
+                               'InstallPath' => 'path',
+                               'Encoding' => 'encoding',
+                               'SqlScript' => 'sql'
+                       ));
+
+       // add/update default charts
+       // 
+       $local = get_company_extensions();
+
+       foreach($local as $extno => $ext) {
+               if ($ext['type'] != 'chart') continue;
+               $ext['local_id'] = $extno;
+               if (!isset($pkgs[$ext['package']]) || $ext['package'] == '')
+                       $pkgs[] = $ext;
+               else
+                       $pkgs[$ext['package']] = array_merge($pkgs[$ext['package']], $ext);
+       }
+       if ($pkgs)
+               ksort($pkgs);
        return $pkgs;
 }
 //---------------------------------------------------------------------------------------------
@@ -553,12 +617,12 @@ function install_language($pkg_name)
                if ($i === null)
                        $i = count($installed_languages);
                else {  // remove another already installed package for this language 
-                       $old_pkg = $installed_languages[$i]['package'];
+                       $old_pkg = @$installed_languages[$i]['package'];
                        if ($old_pkg && ($pkg['Package'] != $old_pkg))
                                uninstall_package($old_pkg);
                }
 
-               $package = new package("$path_to_root/tmp/".$pkg['Filename'].'.pkg');
+               $package = new package(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
                if ($package->install()) {
                        $lang = array(
                                'name' => $pkg['Name'],
@@ -572,53 +636,72 @@ function install_language($pkg_name)
                                $lang['rtl'] = true;
                        $installed_languages[$i] = $lang;
                        write_lang($installed_languages);
-                       unlink("$path_to_root/tmp/".$pkg['Filename'].'.pkg');
+                       unlink(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
                        $Ajax->activate('lang_tbl');
+               } else {
+                       display_error(implode('<br>', $package->error));
+                       return false;
                }
-
+       } else {
+               display_error(sprintf(_("Package '%s' not found."), $pkg_name));
+               return false;
        }
-
+       return true;
 }
 //---------------------------------------------------------------------------------------------
 //     Install/update extension or theme package from repository
 //
 function install_extension($pkg_name)
 {
-       global $path_to_root, $next_extension_id, $Ajax;
+       global $path_to_root, $installed_extensions, $next_extension_id, $Ajax, $db_connections;
        
-       $pkg = get_pkg_or_list(array('extension', 'theme'), $pkg_name);
+       $pkg = get_pkg_or_list(array('extension', 'theme', 'chart'), $pkg_name);
        if ($pkg) {
-               $package = new package("$path_to_root/tmp/".$pkg['Filename'].'.pkg');
+               $package = new package(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
                $local_exts = get_company_extensions();
                if ($package->install()) {
                        $ext_id = array_search_key($pkg['Package'], $local_exts, 'package');
                        if ($ext_id === null)
                                $ext_id = $next_extension_id++;
+                       else {  // remove another already installed package for this language 
+                               $old_pkg = $installed_extensions[$ext_id]['package'];
+                               if ($old_pkg)
+                                       uninstall_package($old_pkg);
+                       }
                        $ext = array(
                                'name' => $pkg['Name'],
                                'package' => $pkg['Package'],
                                'version' => $pkg['Version'],
                                'type' => $pkg['Type'],
-                               'active' => true,
+                               'active' => @$pkg['DefaultStatus'] == 'active' ? true : false,
                                'path' => $pkg['InstallPath'],
-//                             'tabs' => $pkg['MenuTabs'],
-//                             'entries' => $pkg['MenuEntries'],
-//                             'acc_file' => @$pkg['AccessExtensions'],
                        );
-                       if (isset($pkg['MenuTabs']))
-                               $ext['tabs'] = $pkg['MenuTabs'];
-                       if (isset($pkg['MenuEntries']))
-                               $ext['entries'] = $pkg['MenuEntries'];
-                       if (isset($pkg['AccessExtensions']))
-                               $ext['acc_file'] = $pkg['AccessExtensions'];
+                       if (isset($pkg['SqlScript']))
+                               $ext['sql'] = $pkg['SqlScript'];
+
                        $local_exts[$ext_id] = $ext;
-                       update_extensions($local_exts);
-                       unlink("$path_to_root/tmp/".$pkg['Filename'].'.pkg');
+                       $ret = update_extensions($local_exts);
+
+                       if (($ext['active'] == true) && file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
+                       {
+                               // we need to include the new hooks file to activate extension
+                               include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
+                               foreach($db_connections as $comp => $db)
+                                       activate_hooks($ext['package'], $comp);
+                       }
+
+                       unlink(VARLIB_PATH."/".$pkg['Filename'].'.pkg');
                        $Ajax->activate('ext_tbl');
+                       return $ret;
                } else {
                        display_error(implode('<br>', $package->error));
+                       return false;
                }
+       } else {
+               display_error(sprintf(_("Package '%s' not found."), $pkg_name));
+               return false;
        }
+       return true;
 }
 /*
        Returns true if newer package version is available
@@ -644,4 +727,20 @@ function get_package_info($pkg, $type=null, $filter=array(), $outkey=null, $down
        return get_pkg_or_list($type, $pkg, $filter, null, false);
 }
 
-?>
\ No newline at end of file
+/*
+       Check basic extension source compatibility.
+*/
+function check_src_ext_version($ext_v)
+{
+    global $src_version;
+    if ($ext_v != '-') {
+        $compat_levels = 2;    // current policy is keeping compatibility on major version level.
+        $app = explode('.', substr($src_version, 0, strspn($src_version, "0123456789.")));
+        $pkg = explode('.', substr($ext_v, 0, strspn($ext_v, "0123456789.")));
+
+        for ($i=0; $i < min($compat_levels, count($app)); $i++)
+            if ($pkg[$i] < $app[$i])
+                return false;
+    }
+    return true;
+}