length bits, exploding the getRepoForUrl bit
[glcas.git] / plugins / repo.php
index a703429..31a257f 100644 (file)
@@ -41,6 +41,236 @@ class GLCASRepo {
                echo "i am the repo, $url";
        }
        
+       
+       // TODO: rework this function
+       /*
+        * What i need to do is have a downloader function
+        * that can cope with lots of different shit
+        * but thats a pipe dream
+        * 
+        * what *THIS* function needs to do is
+        * 1) figure out the repo
+        * 2) figure out the file in the repo
+        * 2.1) if its a directory, go to print directory
+        * 3) if the file exists, give it to the user (if a range is specified give the user the range)
+        * 4) if the file does not exist
+        *    - check if a tmp file exists
+        *    - attempt to get an exclusive flock
+        *    - if flock fails, donwload in progress
+        *    - if flock succeeds, truncate file and re-start download
+        *    - if a range request was made, send the range once available
+        *    - if range not available, sleep for 5 and check again.
+        * 
+        * I dont want to code this from scratch, but i probably need to
+        */
+       function getRepoForUrlNew($url)
+       {
+               $xurl = split("[/,]", $url);
+               
+               // first get the config         
+               $uconf = unserialize($this->config->getConfigVar("repodata"));
+               $repostore = $this->config->getConfigVar("storagelocation");
+               
+               // preset matched to -1
+               $matched = -1;
+               
+               // first we check for /repo/repoid as a url
+               $startat = 0;
+               if($xurl[0] == "repo") {
+                       $repid = $xurl[1];
+                       error_log("trying to get repo for repoid, $repid");
+                       if(isset($uconf[$repid])) {
+                               $matched = ((int)($repid));
+                               error_log("set matched, $matched, $repid");
+                               $startat +=2;
+                       }
+               }
+               
+               // now check for a prefix match
+               $prematch = false;
+               if($matched < 0) foreach($uconf as $key => $var) {
+                       $pre = $var["prefix"];
+                       
+                       if($pre!="") {
+                               //echo "Checking pre $pre against ".$xurl[0]."\n";
+                               if(strcasecmp($pre, $xurl[0])==0) {
+                                       //echo "Matched pre\n";
+                                       $prematch = true;
+                                       $startat++;
+                               }
+                       }
+               }
+               
+               // next, check for a short url match
+               if($matched < 0) foreach($uconf as $key => $var) {
+                       // if we matched a pre, then we check against the second url component
+                       
+                       $short = $var["shorturl"];
+                       
+                       if($short!="") {
+                               //echo "Checking short $short against ".$xurl[$startat]."\n";
+                               if(strcasecmp($xurl[$startat], $short)==0) {
+                                       //echo "Matched\n";
+                                       $matched = $key;
+                                       $startat++;
+                               }
+                       }
+               }
+               
+               // TODO: this deterministic bit
+               // so far nothing has matched - what this next bit needs to do is try and "Determine" a repo from url
+               // for eg, if a user gets /fedora/x86_64/os we need to return something appropriate
+               if($matched < 0) {
+                       echo "No such repo<br>";
+                       header("HTTP/1.0 404 Not Found");
+                       return;
+               }
+               
+               
+               // something was matched, so now we reconstruct the file component of the url
+               $file = "/";
+               if(count($xurl) > $startat) for($i=$startat; $i < count($xurl); $i++) {
+                       $file .= "/".$xurl[$i];
+               }
+               
+               // so, the ultimate url for the file we need is:
+               $actualfile = "$repostore/$matched/$file";
+               error_log("Atcualfile is $actualfile");
+               
+               // if its a directory, lets do a print
+               if(is_dir($actualfile)) {
+                       $this->printDir($actualfile, $file, $url);
+                       return;
+               }
+               
+               // check if the file exists and serve it up
+               if(file_exists($actualfile)) {
+                       $this->serveUpFile($actualFile, $matched);
+                       return;
+               } else {
+                       // the file does not exist, we now need to go into "download" mode
+                       $remoteurl = $uconf[$matched]["url"]."/$file";
+                       $this->downloadAndServe($actualFile, $matched, $remoteurl);
+                       return;
+               }
+       }
+       
+       function serveUpFile($filename, $repoid)
+       {
+               $uconf = unserialize($this->config->getConfigVar("repodata"));
+               $repostore = $this->config->getConfigVar("storagelocation");
+               
+               // figure out the range header garbage that centos/redhat send
+               if(isset($_SERVER["HTTP_RANGE"])) {
+                       // we're using ranges - screw you stupid installer
+                       $pr_range = preg_split("/[:\-, ]+/", $_SERVER["HTTP_RANGE"]);
+                       
+                       // cut up ranges
+                       $rangestart = $pr_range[1];
+                       $rangelength = $pr_range[2] - $pr_range[1] +1;
+                       $rangestr = $pr_range[1]."-".$pr_range[2];
+                       error_log("going ranges at $rangestart, $rangelength,".$rangesa[1].",".$rangesb[0]);
+                       
+                       // now spit some headers
+                       header("HTTP/1.1 206 Partial Content");
+                       header("Content-Length: ".$rangelength);
+                       header("Content-Range: bytes $rangesstr/".filesize($actualfile));
+                       
+                       // determine mime type
+                       $type = mime_content_type($actualfile);
+                       
+                       // set mime type header
+                       header("Content-type: $type");
+                       
+                       // open the local file (TODO: error check)
+                       $localfile = fopen($actualfile, "r");
+                       fseek($localfile, $rangestart, SEEK_SET);
+                       
+                       // read in the data, god i hope its not big
+                       $data = fread($localfile, $rangelength);
+                       
+                       // lastly, send data
+                       echo $data;
+                       flush();
+                       
+                       // and close the file
+                       fclose($localfile);
+                       return;
+               } else {
+
+                       // we're not using range's - good on you installer thingy
+                       header("Content-Length: ".filesize($actualfile));
+
+                       // set the mime type header
+                       $type = mime_content_type($actualfile);
+                       header("Content-type: $type");
+                       
+                       // open the local file                  
+                       $localfile = fopen($actualfile, "r");
+                       
+                       // iterate over its length, send 8k at a time
+                       while(!feof($localfile)) {
+                               // read and send data
+                               $data = fread($localfile, 8192);
+                               echo $data;
+                               
+                               // flush so the client sees the data
+                               flush();
+                       }
+                       
+                       // close the file
+                       fclose($localfile);
+                       return;
+               }               
+       }
+       
+       // TODO: this is the function im working on
+       function downloadAndServe($filename, $repoid, $remoteurl)
+       {
+               $uconf = unserialize($this->config->getConfigVar("repodata"));
+               $repostore = $this->config->getConfigVar("storagelocation");
+               
+               // this is the tricky one for ranges.
+               
+               // check if a download exists
+               if(file_exists("$actualfile.tmp.data.deleteme")) {
+                       // a download exists, does it still work
+                       $localtmpfh = fopen("$actualfile.tmp.data.deleteme", "r");
+                       $lockres = flock($localtmpfh, LOCK_EX|LOCK_NB);
+                       if(!$lockres) {
+                               error_log("flock did fail, all is right with the world a download is in progress");
+                       } else {
+                               unlink("$actualfile.tmp.data.deleteme");
+                               unlink("$actualfile.tmp.data.deleteme.size");
+                       }
+               }
+
+               
+               $localfile = fopen($actualfile.".tmp.data.deleteme", "w");                              
+               $localsizefile = fopen($actualfile.".tmp.data.deleteme.size", "w");
+               
+               
+               // get the headers from the remote request and use them to hurt people
+               foreach($http_response_header as $key => $val) {
+                       if(preg_match("/HTTP.*30[1-9].*/", $val)) {
+                               error_log("got a 30x, must be a directory");
+                               mkdir($actualfile);
+                               header("Location: ".$_SERVER["REQUEST_URI"]."/");
+                               return;
+                       }
+                       // get content length form upstream and print
+                       if(preg_match("/^Content-Length:.*/", $val)) {
+                               $clen = $val;
+                               header($val);
+                       }
+                       // get content type from upstream and print
+                       if(preg_match("/^Content-Type:.*/", $val)) {
+                               header($val);   
+                       }
+               }
+       }
+       
+       // this is a nightmare
        function getRepoForUrl($url)
        {
                // the way we breakdown a url is to explode it
@@ -52,9 +282,22 @@ class GLCASRepo {
                $repostore = $this->config->getConfigVar("storagelocation");
                
                $matched = -1;
+               
+               // first we check for /repo/repoid as a url
                $startat = 0;
+               if($xurl[0] == "repo") {
+                       $repid = $xurl[1];
+                       error_log("trying to get repo for repoid, $repid");
+                       if(isset($uconf[$repid])) {
+                               $matched = ((int)($repid));
+                               error_log("set matched, $matched, $repid");
+                               $startat +=2;
+                       }
+               }
+               
+               
                $prematch = false;
-               foreach($uconf as $key => $var) {
+               if($matched < 0) foreach($uconf as $key => $var) {
                        $pre = $var["prefix"];
                        
                        if($pre!="") {
@@ -68,7 +311,7 @@ class GLCASRepo {
                }
                
                
-               foreach($uconf as $key => $var) {
+               if($matched < 0) foreach($uconf as $key => $var) {
                        // if we matched a pre, then we check against the second url component
                        
                        $short = $var["shorturl"];
@@ -83,8 +326,9 @@ class GLCASRepo {
                        }
                }
                
-               if($matched > -1) {
-                       //echo "Match on $key\n";
+               if($matched < 0) {
+                       echo "No such repo<br>";
+                       return;
                }
                
                
@@ -96,6 +340,7 @@ class GLCASRepo {
                
                // now we want to find repostore/$matched/$file;
                $actualfile = "$repostore/$matched/$file";
+               error_log("Atcualfile is $actualfile");
                //echo "Start file for $actualfile\n";
                
                // first check any directories in $file are in existence
@@ -109,17 +354,57 @@ class GLCASRepo {
                        }
                }
                
+               $reqhead = print_r($_REQUEST, true);
+               $sevhead = print_r($_SERVER, true);
+               
+               error_log("req $reqhead");
+               error_log("sev $sevhead");
+               
+               $rangestart = -1;
+               $rangelength = -1;
+               $rangesstr = "";
+               if(isset($_SERVER["HTTP_RANGE"])) {
+                       // oh shit
+                       $rangesa = explode("=", $_SERVER["HTTP_RANGE"]);
+                       $rangesb = explode(",", $rangesa[1]);
+                       $rangesstr = $rangesb[0];
+                       $ranges = explode("-", $rangesb[0]);
+                       $rangestart = $ranges[0];
+                       $rangelength = $ranges[1] - $ranges[0] +1; 
+                       error_log("going ranges at $rangestart, $rangelength,".$rangesa[1].",".$rangesb[0]);
+               }
                
+               // i have to support http_range cause REDHAT/CENTOS IS annoying as all hell. christ, why do this?
                if(is_file($actualfile)) {
                        // file is stored locally, away we go
-                       header("Content-Length: ".filesize($actualfile));
+                       if($rangelength != -1) {
+                               header("HTTP/1.1 206 Partial Content");
+                               header("Content-Length: ".$rangelength);
+                               header("Content-Range: bytes $rangesstr/".filesize($actualfile));
+                               //header("Content-Length: ".filesize($actualfile));
+                       } else {
+                               header("Content-Length: ".filesize($actualfile));
+                       }
                        $type = mime_content_type($actualfile);
                        header("Content-type: $type");
                        $localfile = fopen($actualfile, "r");
+                       if($rangestart!=-1) fseek($localfile, $rangestart, SEEK_SET);
                        while(!feof($localfile)) {
-                               $data = fread($localfile, 16384);
+                               // cant make this high cause centos is crap
+                               if($rangelength!=-1) {
+                                       $data = fread($localfile, $rangelength);
+                                       error_log("data size was ".strlen($data));
+                               } else {
+                                       $data = fread($localfile, 2048);
+                               }
+                               
                                echo $data;
                                flush();
+                               
+                               if($rangelength!=-1) {
+                                       fclose($localfile);
+                                       exit(0);
+                               }
                        }
                        fclose($localfile);
                } else if(is_dir($actualfile)) {
@@ -134,6 +419,7 @@ class GLCASRepo {
                        // TODO: i should get remote contents with fopen/fread/fwrite as
                        // it should be more memory conservative and we can push to the end client
                        // straight away
+                       ignore_user_abort(true);
                        $rf = fopen($remotefile, "r");
                        error_log("attempting to get remote file $remotefile");
 
@@ -151,6 +437,7 @@ class GLCASRepo {
                                }
                                // get content length form upstream and print
                                if(preg_match("/^Content-Length:.*/", $val)) {
+                                       $clen = $val;
                                        header($val);
                                }
                                // get content type from upstream and print
@@ -164,6 +451,9 @@ class GLCASRepo {
                                header("HTTP/1.0 404 Not Found");
                        } else {
                                $localfile = fopen($actualfile.".tmp.data.deleteme", "w");                              
+                               $localsizefile = fopen($actualfile.".tmp.data.deleteme.size", "w");
+                               fwrite($localsizefile, "$clen");
+                               fclose($localsizefile);         
                                while(!feof($rf)) {
                                        $data = fread($rf, 8192);
                                        echo $data;
@@ -173,7 +463,7 @@ class GLCASRepo {
                                fclose($localfile);
                                fclose($rf);
                                rename($actualfile.".tmp.data.deleteme", $actualfile);
-                               error_log("got actualfile, tried to save as $actualfile, did it work?");
+                               //error_log("got actualfile, tried to save as $actualfile, did it work?");
                        }
                }
                
@@ -184,15 +474,19 @@ class GLCASRepo {
        
        function printDir($dir, $localfile, $baseurl)
        {
+               $localfile = preg_replace("/\/\/+/", "/", $localfile);
                $uri = $_SERVER["REQUEST_URI"];
+               $content = "";
                if(is_dir($dir)) {
-                       echo "<html><head><title>Index of $localfile</title></head><body><h1>Index of $localfile</h1>";
-                       echo "<table>";
+                       $content .= "<html><head><title>Index of $localfile</title></head><body><h1>Index of $localfile</h1>";
+                       $content .= "<table>";
                        $dh = opendir($dir);
                        while(($file = readdir($dh))!==false) {
-                               if($file != "." && $file != "..") echo "<tr><td><a href=\"$uri/$file\">$file</a></td></tr>";
+                               if($file != "." && $file != "..") $content .= "<tr><td><a href=\"$uri/$file\">$file</a></td></tr>";
                        }
-                       echo "</table></body></html>";
+                       $content .= "</table></body></html>";
+                       
+                       GLCASpageBuilder(null, null, $content);
                        
                } else return false;
        }
@@ -233,7 +527,7 @@ class GLCASRepo {
                        
                        // find a version, we assume its going to be something [numbers] and a . (optional)
                        if(preg_match("/^[0-9.]+$/", $comp)>0) {
-                               error_log("version match of $comp");
+                               //error_log("version match of $comp");
                                $glt["version"] = $comp;
                        }
                        
@@ -241,7 +535,7 @@ class GLCASRepo {
                        foreach($kos["arch"] as $archinter => $archname ) {
                                //error_log("Comparing $archinter, $archname with $comp");
                                if(strcasecmp($archname, $comp) == 0) {
-                                       error_log("arch match of $archname with $comp");
+                                       //error_log("arch match of $archname with $comp");
                                        $glt["arch"] = $archname;
                                }
                        }
@@ -339,6 +633,7 @@ class GLCASRepo {
                        mkdir("$repostore/$repokey/repodata");
                }
                
+               //ignore_user_abort(true);
                $actionurl = "$repourl/repodata/repomd.xml";
                $repomdxml = file_get_contents($actionurl);
                file_put_contents("$repostore/$repokey/repodata/repomd.xml", $repomdxml);