<?php
//depend on xdelta, xz and bsdiff
/**
 * This class contains all code for running diffget as server in FPM-Mode
 * @author Sebastian Peters
 */
class pw_diffget_server
{
    /**
     * @var bool $no_exit
     */
    private static bool $no_exit;
    /**
     * @return void
     */
    static private function exit($code){
        if(pw_diffget_server::$no_exit){
            throw new Exception("Exit Code: ".$code);
        }
        else{
            exit($code);
        }
    }
    /**
     * @return void
     */
    static public function main($no_exit): void
    {
        pw_diffget_server::$no_exit=$no_exit;
        self::print_copyright();
        set_time_limit(3600 * 24);

        $pkg = $_GET["pkg"];
        $pkg = str_replace("..", ".", $pkg); // Safety
        $pkg_explode = explode("/", $pkg);
        if (count($pkg_explode) != 2) {
            echo "PKG Syntax wrong :(\r\n";
            pw_diffget_server::exit(1);
        }
        $havemd5 = explode(",", $_GET["have_md5"]);
        $needmd5 = $_GET["need_md5"];

        echo "Searching files...\r\n";
        $info = self::get_local_pkg_info($pkg);
        $realhave = array();
        $foundsearch = false;
        $detail_need = array();
        $detail_have = array();
        foreach ($info as $pkginfo) {
            if (substr($pkginfo["PATH"], 0, strlen($pkg)) != $pkg) continue;
            if ($pkginfo["MD5"] == $needmd5 && $foundsearch) {
                echo "Double found??? :(\r\n";
                pw_diffget_server::exit(1);
            }
            if ($pkginfo["MD5"] == $needmd5) {
                $foundsearch = true;
                $detail_need = $pkginfo;
            }
            if (in_array($pkginfo["MD5"], $havemd5)) {
                if (in_array($pkginfo["MD5"], $realhave)) {
                    echo "Double found 2??? :(\r\n";
                    pw_diffget_server::exit(1);
                }
                $realhave[] = $pkginfo["MD5"];
                $detail_have[] = $pkginfo;
            }
        }
        if (!$foundsearch) {
            echo "Dont find needed package :(\r\n";
            pw_diffget_server::exit(1);
        }
        if (count($realhave) < 1) {
            echo "Dont find haved packages :( - Need complete download\r\n";
            pw_diffget_server::exit(1);
        }
        echo "Requested have packages: " . count($havemd5) . " | Found have packages: " . count($realhave) . "\r\n";

        sort($realhave);
        $filepath = "diffs/" . $pkg . "/" . $needmd5 . "/" . join("-", $realhave) . ".diffget.gz";
        $folder = "diffs/" . $pkg . "/" . $needmd5;
        $srvpath = "/var/www/localhost/htdocs/diffget/" . $filepath;
        $srvfolder = "/var/www/localhost/htdocs/diffget/" . $folder;
        $filename = join("-", $realhave) . ".diffget";
        if (!file_exists($srvpath)) {
            $filepath = "diffs/" . $pkg . "/" . $needmd5 . "/" . md5(join("-", $realhave)) . ".diffget.gz";
            $folder = "diffs/" . $pkg . "/" . $needmd5;
            $srvpath = "/var/www/localhost/htdocs/diffget/" . $filepath;
            $srvfolder = "/var/www/localhost/htdocs/diffget/" . $folder;
            $filename = md5(join("-", $realhave)) . ".diffget";
        }

        $md5orderhave = array();
        if (!file_exists($srvpath)) {
            $difftype = "bsdiff";
            $lockfile_pkg = self::get_lockfile_pkg($needmd5);
            $lockfile_glob = self::get_lockfile_glob($needmd5);

            echo "We must create file...\r\n";
            $tmpdir = "/tmp/diffget_srv/" . str_replace("/", "_", $pkg) . "-" . $needmd5 . "-" . md5(join("-", $realhave));
            @mkdir("/tmp/diffget_srv");
            if (file_exists($tmpdir) && is_dir($tmpdir)) {
                self::clean_dir($tmpdir);
            }
            @mkdir($tmpdir);
            chdir($tmpdir);
            copy("/var/cache/binpkgs/" . $detail_need["PATH"], $tmpdir . "/need.gpkg.tar");
            #echo "/var/cache/binpkgs/".$detail_need["PATH"]."\r\n";
            #echo $tmpdir."/need.gpkg.tar\r\n";

            @mkdir("need.gpkg");
            chdir("need.gpkg");
            echo shell_exec("tar --strip-components=1 -xf ../need.gpkg.tar 2>&1");
            $compression = "";
            if (file_exists("image.tar.zst")) {
                echo shell_exec("zstd -d image.tar.zst");
                echo shell_exec("zstd -d metadata.tar.zst");
                $compression = "zstd";
            } elseif (file_exists("image.tar.xz")) {
                echo shell_exec("xz -d -k image.tar.xz");
                echo shell_exec("xz -d -k metadata.tar.xz");
                $compression = "xz";
            } else {
                echo "need - image.tar.zst/.xz not found :(\r\n";
                pw_diffget_server::exit(1);
            }
            copy("image.tar", "../need.image.tar");
            if ($compression == "zstd") copy("image.tar.zst", "../need.orig.image.tar.zst");
            if ($compression == "xz") copy("image.tar.xz", "../need.orig.image.tar.xz");
            copy("metadata.tar", "../need.metadata.tar");
            chdir("..");

            $cnt = 0;
            @unlink("have.image.tar");
            foreach ($detail_have as $ident => $have_pkg) {
                $cnt++;
                if (!file_exists("/var/cache/binpkgs/" . $have_pkg["PATH"])) {
                    echo "file " . "/var/cache/binpkgs/" . $have_pkg["PATH"] . " not present on server :( - skip...\r\n";
                    continue;
                }
                copy("/var/cache/binpkgs/" . $have_pkg["PATH"], $tmpdir . "/have." . $cnt . ".gpkg.tar");

                @mkdir("have." . $cnt . ".gpkg");
                chdir("have." . $cnt . ".gpkg");
                echo shell_exec("tar --strip-components=1 -xf ../have." . $cnt . ".gpkg.tar 2>&1");
                if (file_exists("image.tar.zst")) {
                    echo shell_exec("zstd -d image.tar.zst 2>&1");
                } elseif (file_exists("image.tar.xz")) {
                    echo shell_exec("xz -k -d image.tar.xz 2>&1");
                } else {
                    echo "have." . $cnt . " - image.tar.zst/.xz not found - skip...\r\n";
                    chdir("..");
                    continue;
                }
                $detail_have[$ident]["path"] = "have." . $cnt . ".gpkg";
                $detail_have[$ident]["extracted_size"] = filesize("image.tar");
                $detail_have[$ident]["extracted_filemtime"] = filemtime("image.tar");
                chdir("..");
            }

            echo "for debugging: (why using xdelta 400limit instead of extracted bsdiff 300limit?\r\n";
            print_r($detail_have);
            // Take care, that we dont have >20 Packages or >300MiB
            $i = 1;
            while (true) {
                $i++;
                if ($i > 1000) {
                    echo "break while(true) - 1000 iterations :(\r\n";
                    break;
                }
                $sumsize = 0;
                foreach ($detail_have as $ident => $have_pkg) {
                    $sumsize += $have_pkg["extracted_size"];
                }
                $sumcount = count($detail_have);
                if ($sumcount < 20 && $sumsize < (1024 * 1024 * 300)) {
                    // Size and numbers okay :)
                    break;
                } elseif ($sumcount <= 1) {
                    // Only one left, print error message later...
                    break;
                } else {
                    // If we only two left, delete the smallest
                    if ($sumcount == 2) {
                        $the_one = -1;
                        $the_one_size = -1;
                        foreach ($detail_have as $ident => $have_pkg) {
                            if ($the_one_size == -1 || $the_one_size < $have_pkg["extracted_size"]) {
                                $the_one = $ident;
                                $the_one_size = $have_pkg["extracted_size"];
                            }
                        }
                        echo "20pkg/200mib limit: removed " . $detail_have[$the_one]["PATH"] . " (" . $detail_have[$the_one]["MD5"] . ") because of only two left -> smallest\r\n";
                        unset($detail_have[$the_one]);
                        break;
                    } else { // more than 2
                        // Add Info prev/next
                        $prev_ident = -1;
                        foreach ($detail_have as $ident => $have_pkg) {
                            if ($prev_ident !== -1) {
                                $detail_have[$ident]["ident_prev"] = $prev_ident;
                                $detail_have[$prev_ident]["ident_next"] = $ident;
                            }
                            $prev_ident = $ident;
                        }

                        // Add timespan prev/next
                        foreach ($detail_have as $ident => $have_pkg) {
                            if (!isset($have_pkg["ident_prev"]) || !isset($have_pkg["ident_next"])) {
                                unset($detail_have[$ident]["timedelta_prevnext"]);
                                continue;
                            }

                            if (isset($have_pkg["ident_prev"]["extracted_filemtime"]) && isset($have_pkg["ident_next"]["extracted_filemtime"])) {
                                $timeprev = $have_pkg["ident_prev"]["extracted_filemtime"];
                                $timenext = $have_pkg["ident_next"]["extracted_filemtime"];

                                $timedelta = $timeprev - $timenext;
                                if ($timedelta < 0) $timedelta *= -1;
                                $detail_have[$ident]["timedelta_prevnext"] = $timedelta;
                            }
                        }

                        // Delete smallest timespan to the next partner
                        $the_one = -1;
                        $the_one_time = -1;
                        foreach ($detail_have as $ident => $have_pkg) {
                            if (!isset($have_pkg["timedelta_prevnext"])) continue;
                            if ($the_one_time == -1 || $the_one_time < $have_pkg["timedelta_prevnext"]) {
                                $the_one = $ident;
                                $the_one_time = $have_pkg["timedelta_prevnext"];
                            }
                        }
                        if (isset($detail_have[$the_one])) {
                            echo "20pkg/200mib limit: removed " . $detail_have[$the_one]["PATH"] . " (" . $detail_have[$the_one]["MD5"] . ") because of smallest timespan prev/next!\r\n";
                            unset($detail_have[$the_one]);
                        }
                    }
                }
            }

            // Buildup md5orderhave
            foreach ($detail_have as $ident => $have_pkg) {
                echo shell_exec("cat " . $have_pkg["path"] . "/image.tar >> have.image.tar");
                $md5orderhave[] = $have_pkg["MD5"];
            }
            echo "finally used " . count($detail_have) . " packages for delta...\r\n";

            if (count($md5orderhave) < 1) {
                echo "we dont have at least 1 zstd compressed have.-file :(\r\n";
                echo "ABORT!\r\n";
                pw_diffget_server::exit(1);
            }

            if (filesize("need.image.tar") > (1024 * 1024 * 400)) {                                            ## dont use bsdiff on >400MiB
                echo "switch to xdelta because need-filesize is " . filesize("need.image.tar") . "\r\n";
                $difftype = "xdelta";
            }
            if (filesize("have.image.tar") > (1024 * 1024 * 400)) {                                            ## dont use bsdiff on >400MiB
                echo "switch to xdelta because have-filesize is " . filesize("have.image.tar") . "\r\n";
                $difftype = "xdelta";
            }
            if ($difftype == "bsdiff") {
                echo "Calling bsdiff...\r\n";
                echo shell_exec("bsdiff have.image.tar need.image.tar delta.image.tar.diff 2>&1");
            } elseif ($difftype == "xdelta") {
                echo "Calling xdelta...\r\n";
                echo shell_exec("xdelta3 -e -s have.image.tar need.image.tar delta.image.tar.diff 2>&1");
            } elseif ($difftype == "xdelta+xz") {
                @unlink("delta.image.tar.diff.pre.xz");
                @unlink("delta.image.tar.diff.pre");
                @unlink("delta.image.tar.diff");
                echo "Calling xdelta...\r\n";
                echo shell_exec("xdelta3 -e -s have.image.tar need.image.tar delta.image.tar.diff.pre 2>&1");
                echo "Calling xdelta/xz...\r\n";
                echo shell_exec("xz delta.image.tar.diff.pre 2>&1");
                rename("delta.image.tar.diff.pre.xz", "delta.image.tar.diff");
            } else {
                echo "unknown difftype " . $difftype . " :(\r\n";
                pw_diffget_server::exit(1);
            }

            if ($compression == "zstd") {
                echo "Calling zstd...\r\n";
                @unlink("need.image.tar.zst");
                echo shell_exec("zstd need.image.tar 2>&1");
                if ($difftype == "bsdiff") {
                    echo "Calling bsdiff2...\r\n";
                    echo shell_exec("bsdiff need.image.tar.zst need.orig.image.tar.zst delta.image.tar.zst.diff 2>&1");
                } elseif ($difftype == "xdelta") {
                    echo "Calling xdelta2...\r\n";
                    echo shell_exec("xdelta3 -D -R -e -s need.image.tar.zst need.orig.image.tar.zst delta.image.tar.zst.diff 2>&1");
                } elseif ($difftype == "xdelta+xz") {
                    echo "Calling xdelta2...\r\n";
                    echo shell_exec("xdelta3 -D -R -e -s need.image.tar.zst need.orig.image.tar.zst delta.image.tar.zst.diff.pre 2>&1");
                    echo "Calling xdelta/xz2...\r\n";
                    echo shell_exec("xz delta.image.tar.zst.diff.pre 2>&1");
                    rename("delta.image.tar.zst.diff.pre.xz", "delta.image.tar.zst.diff");
                } else {
                    echo "unknown difftype " . $difftype . " :(\r\n";
                    pw_diffget_server::exit(1);
                }
            } elseif ($compression == "xz") {
                echo "Calling xz...\r\n";
                @unlink("need.image.tar.xz");
                echo shell_exec("xz -k need.image.tar 2>&1");
                if ($difftype == "bsdiff") {
                    echo "Calling bsdiff2...\r\n";
                    echo shell_exec("bsdiff need.image.tar.xz need.orig.image.tar.xz delta.image.tar.xz.diff 2>&1");
                } elseif ($difftype == "xdelta") {
                    echo "Calling xdelta2...\r\n";
                    echo shell_exec("xdelta3 -D -R -e -s need.image.tar.xz need.orig.image.tar.xz delta.image.tar.xz.diff 2>&1");
                } elseif ($difftype == "xdelta+xz") {
                    echo "Calling xdelta2...\r\n";
                    echo shell_exec("xdelta3 -D -R -e -s need.image.tar.xz need.orig.image.tar.xz delta.image.tar.xz.diff.pre 2>&1");
                    echo "Calling xdelta/xz2...\r\n";
                    echo shell_exec("xz delta.image.tar.xz.diff.pre 2>&1");
                    rename("delta.image.tar.xz.diff.pre.xz", "delta.image.tar.xz.diff");
                } else {
                    echo "unknown difftype " . $difftype . " :(\r\n";
                    pw_diffget_server::exit(1);
                }
            }


            // Recreate new gpkg.tar
            if ($compression == "zstd") {
                $fp = fopen("need.orig.image.tar.zst", "r");
                $ident_image = fread($fp, 1024);
                fclose($fp);
            } elseif ($compression == "xz") {
                $fp = fopen("need.orig.image.tar.xz", "r");
                $ident_image = fread($fp, 1024);
                fclose($fp);
            }

            $fp = fopen("need.gpkg.tar", "r");
            $start_new = fread($fp, 1024 * 1024);
            fclose($fp);

            if ($compression == "zstd") $len_image = filesize("need.orig.image.tar.zst");
            if ($compression == "xz") $len_image = filesize("need.orig.image.tar.xz");

            $pos_image = strpos($start_new, $ident_image);
            if ($pos_image > 0) {
                echo "Found image.tar.zst/.xz at " . $pos_image . ", len: " . $len_image . "\r\n";
            } else {
                echo "Dont' find image.tar.zst/.xz in gpkg.tar :(\r\n";
                pw_diffget_server::exit(1);
            }

            $fp = fopen("need.gpkg.tar", "r");
            $pre_tar = fread($fp, $pos_image);
            fread($fp, $len_image); // to trash
            $post_tar = fread($fp, filesize("need.gpkg.tar"));


            // Rebuilding original file for testing purpose....
            @unlink("target.image.tar");
            @unlink("target.image.tar.zst");
            @unlink("target.image.tar.xz");
            @unlink("target.gpkg.tar");
            if ($difftype == "bsdiff") {
                echo "Calling bspatch...\r\n";
                echo shell_exec("bspatch have.image.tar target.image.tar delta.image.tar.diff 2>&1");
            } elseif ($difftype == "xdelta") {
                echo "Calling patch xdelta...\r\n";
                echo shell_exec("xdelta3 -d -s have.image.tar delta.image.tar.diff target.image.tar 2>&1");
            } elseif ($difftype == "xdelta+xz") {
                echo "Calling patch xdelta+xz...\r\n";
                copy("delta.image.tar.diff", "delta.image.tar.diff.check.xz");
                echo shell_exec("xz -d delta.image.tar.diff.check.xz 2>&1");
                echo shell_exec("xdelta3 -d -s have.image.tar delta.image.tar.diff.check target.image.tar 2>&1");
            } else {
                echo "unknown difftype " . $difftype . " :(\r\n";
                pw_diffget_server::exit(1);
            }
            if ($compression == "zstd") {
                echo shell_exec("zstd target.image.tar 2>&1");
                if ($difftype == "bsdiff") {
                    echo "Calling patch2 zstd bspatch...\r\n";
                    echo shell_exec("bspatch target.image.tar.zst target_orig_new.image.tar.zst delta.image.tar.zst.diff 2>&1");
                } elseif ($difftype == "xdelta") {
                    echo "Calling patch2 zst xdelta...\r\n";
                    echo shell_exec("xdelta3 -D -R -d -s target.image.tar.zst delta.image.tar.zst.diff target_orig_new.image.tar.zst 2>&1");
                } elseif ($difftype == "xdelta+xz") {
                    echo "Calling patch2 zst xdelta+xz...\r\n";
                    copy("delta.image.tar.zst.diff", "delta.image.tar.zst.diff.check.xz");
                    echo shell_exec("xz -d delta.image.tar.zst.diff.check.xz 2>&1");
                    echo shell_exec("xdelta3 -D -R -d -s target.image.tar.zst delta.image.tar.zst.diff.check target_orig_new.image.tar.zst 2>&1");
                } else {
                    echo "unknown difftype " . $difftype . " :(\r\n";
                    pw_diffget_server::exit(1);
                }

                $md5_a_image = md5_file("need.orig.image.tar.zst");
                $md5_b_image = md5_file("target_orig_new.image.tar.zst");
                if ($md5_a_image == $md5_b_image) {
                    echo "MD5 generated file image.tar.zst matches! :)\r\n";
                } else {
                    echo "Resulting image defect :( MD5 mismatch :(\r\n";
                    pw_diffget_server::exit(1);
                }
            } elseif ($compression == "xz") {
                echo shell_exec("xz -k target.image.tar 2>&1");
                if ($difftype == "bsdiff") {
                    echo "Calling patch2 xz bspatch...\r\n";
                    echo shell_exec("bspatch target.image.tar.xz target_orig_new.image.tar.xz delta.image.tar.xz.diff 2>&1");
                } elseif ($difftype == "xdelta") {
                    echo "Calling patch2 xz xdelta...\r\n";
                    echo shell_exec("xdelta3 -D -R -d -s target.image.tar.xz delta.image.tar.xz.diff target_orig_new.image.tar.xz 2>&1");
                } elseif ($difftype == "xdelta+xz") {
                    echo "Calling patch2 xz xdelta+xz...\r\n";
                    copy("delta.image.tar.xz.diff", "delta.image.tar.xz.diff.check.xz");
                    echo shell_exec("xz -d delta.image.tar.xz.diff.check.xz 2>&1");
                    echo shell_exec("xdelta3 -D -R -d -s target.image.tar.xz delta.image.tar.xz.diff.check target_orig_new.image.tar.xz 2>&1");
                } else {
                    echo "unknown difftype " . $difftype . " :(\r\n";
                    pw_diffget_server::exit(1);
                }

                $md5_a_image = md5_file("need.orig.image.tar.xz");
                $md5_b_image = md5_file("target_orig_new.image.tar.xz");
                if ($md5_a_image == $md5_b_image) {
                    echo "MD5 generated file image.tar.xz matches! :)\r\n";
                } else {
                    echo "Resulting image defect :( MD5 mismatch :(\r\n";
                    pw_diffget_server::exit(1);
                }
            }


            $fp = fopen("target.gpkg.tar", "w");
            fwrite($fp, $pre_tar);
            if ($compression == "zstd") fwrite($fp, file_get_contents("target_orig_new.image.tar.zst"));
            if ($compression == "xz") fwrite($fp, file_get_contents("target_orig_new.image.tar.xz"));
            fwrite($fp, $post_tar);
            fclose($fp);

            $md5_target = md5_file("target.gpkg.tar");
            if ($md5_target == $needmd5) {
                echo "MD5 generated file target.gpkg.tar matches! :)\r\n";
            } else {
                echo "Resulting package defect :( MD5 mismatch :(\r\n";
                pw_diffget_server::exit(1);
            }


            // Create patch stream
            $result_array = array();
            $result_array["filetype"] = 3;
            $result_array["difftype"] = $difftype;
            $result_array["compression"] = $compression;
            $result_array["gpkg_pre"] = $pre_tar;
            $result_array["gpkg_post"] = $post_tar;
            unset($pre_tar, $post_tar);
            gc_collect_cycles();
            $result_array["patch.image"] = file_get_contents("delta.image.tar.diff");
            if ($compression == "zstd") $result_array["patch.image.zst"] = file_get_contents("delta.image.tar.zst.diff");
            if ($compression == "xz") $result_array["patch.image.xz"] = file_get_contents("delta.image.tar.xz.diff");

            $result_array["server_path"] = $detail_need["PATH"];
            $tmp = explode("/", $detail_need["PATH"]);
            $tmp = $tmp[count($tmp) - 1];
            $tmp = str_replace(".gpkg.tar", "", $tmp);
            $result_array["dir_in_tar"] = $tmp;
            $result_array["order_md5"] = $md5orderhave;
            $result_array["filemtime"] = filemtime("/var/cache/binpkgs/" . $detail_need["PATH"]);
            $serialize = serialize($result_array);
            unset($result_array);
            gc_collect_cycles();

            $srvfolder_explode = explode("/", $srvfolder);
            echo "SRVFOLDER: ".$srvfolder."\r\n";
            chdir("/");
            foreach ($srvfolder_explode as $next_folder) {
                if (empty($next_folder)) continue;
                @mkdir($next_folder);
                chdir($next_folder);
            }

            echo "Compress...\r\n";
            $compress = gzcompress($serialize);
            unset($serialize);
            gc_collect_cycles();
            $fp = fopen($srvpath, "w");
            fwrite($fp, $compress);
            fclose($fp);
            unset($compress);
            gc_collect_cycles();
            echo "File creation done!\r\n";
            echo "Cleanup...\r\n";
            self::clean_dir($tmpdir);
            fclose($lockfile_pkg);
            fclose($lockfile_glob);
            unlink("/tmp/diffget_srv/diffget_srv." . $needmd5 . ".lock");
        } else {
            echo "File is cached available!\r\n";
        }

        if (!file_exists($srvpath)) {
            echo "Internal error - file not found :(\r\n";
            pw_diffget_server::exit(1);
        }

        echo "Original Filesize: " . $detail_need["SIZE"] . "\r\n";
        echo "Delta filesize: " . filesize($srvpath) . "\r\n";
        echo "Optimized: " . ($detail_need["SIZE"] - filesize($srvpath)) . "\r\n";
        echo "Optimized rate: " . round(($detail_need["SIZE"] - filesize($srvpath)) / $detail_need["SIZE"] * 100) . "%\r\n";
        if (round(($detail_need["SIZE"] - filesize($srvpath)) / $detail_need["SIZE"] * 100) < 1) {
            echo "Optimize under 1% - please load original file!\r\n";
        } else {
            echo "Filepath for download is: " . $filepath . "\r\n";
        }
    }


    static private function get_local_pkg_info($spkg){
        $local_package = "/var/cache/binpkgs/Packages";
        $loc_pkg = self::parse_packages($local_package);

        $found_loc_pkgs=array();
        foreach($loc_pkg as $pkg){
        $search_path=$spkg;
        if(substr($pkg["PATH"],0,strlen($search_path))==$search_path){
            $found_loc_pkgs[]=$pkg;
        }
        }

        if(count($found_loc_pkgs)<1){
        echo "We dont have a local package - must do a complete download :(\r\n";
            pw_diffget_server::exit(2);
        }


        $relinfos=array();
        foreach($found_loc_pkgs as $pkg){
        $relinfo=array();
        $relinfo["PATH"]=$pkg["PATH"];
        $relinfo["MD5"]=$pkg["MD5"];
        $relinfo["SHA1"]=$pkg["SHA1"];
        $relinfo["SIZE"]=$pkg["SIZE"];
        $relinfos[]=$relinfo;
        }

        return($relinfos);
    }

    static private function get_lockfile_glob($pkg_md5){
        for($i=1;$i<=3;$i++){
        @mkdir("/tmp/diffget_srv");
        $lockfile=fopen("/tmp/diffget_srv/diffget_srv.".$i.".lock","w+");
        echo "Aqkuire global ".$i." (".date("H:i:s").") lock...";
        if(!flock($lockfile,LOCK_EX | LOCK_NB)){
            sleep(rand(10,30));
            echo " lock (".date("H:i:s").") could not be aqkuiered! try next slot...\r\n";
        }
        else{
            echo " lock (".date("H:i:s").") aqkuired!\r\n";
            return($lockfile);
        }
        }
        echo "every slot is not usable :( - try again later - we are busy!\r\n";
        pw_diffget_server::exit(1);
    }
    static private function get_lockfile_pkg($pkg_md5,$cnt=0){
        if($cnt==0){
        @mkdir("/tmp/diffget_srv");
        $lockfile=fopen("/tmp/diffget_srv/diffget_srv.".$pkg_md5.".lock","w+");
        echo "Aqkuire pkg ".$pkg_md5." (".date("H:i:s").") lock...";
        if(!flock($lockfile,LOCK_EX | LOCK_NB)){
            sleep(rand(10,30));
            echo " lock (".date("H:i:s").") could not be aqkuiered! - still in progress for this pkg, try again later :/\r\n";
            pw_diffget_server::exit(1);
        }
        else{
            echo " lock (".date("H:i:s").") aqkuired!\r\n";
        }
        }

        return($lockfile);
    }
    static private function parse_packages($pkg_file){
        $srv_packages = file($pkg_file);
        $cur_pkg=array();
        $all_pkg=array();
        foreach($srv_packages as $line){
        $line=trim($line);
        if(empty($line)){
            if(isset($cur_pkg["PATH"])){
            $all_pkg[]=$cur_pkg;
            }
            $cur_pkg=array();
            continue;
        }
        $pos=strpos($line,":");
        $key=trim(substr($line,0,$pos));
        $val=trim(substr($line,$pos+1));
        $cur_pkg[$key]=$val;
        }
        return($all_pkg);
    }
    static private function clean_dir($dir){
        $exp = explode("/",$dir);
        if(count($exp)<2){
            echo "Cant clean_dir(".$dir.") :(\r\n";
            pw_diffget_server::exit(1);
        }
        $scan = scandir($dir);
        foreach($scan as $file){
            if($file=="." || $file=="..")continue;
            if(is_dir($dir."/".$file)){
                self::clean_dir($dir."/".$file);
                rmdir($dir."/".$file);
            }
            else{
                unlink($dir."/".$file);
            }
        }
    }

    static private function print_copyright(){
        if(!class_exists("pwgeneralcopyright")){
            echo "Can't print copyright, class missing :(\r\n";
        }
        else{
            pwgeneralcopyright::getcopyright();
        }
    }
}
