/[drupal]/contributions/modules/filerequest/downloadhandler.php
ViewVC logotype

Diff of /contributions/modules/filerequest/downloadhandler.php

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.3, Sun Jan 8 21:00:53 2006 UTC revision 1.4, Mon Jul 3 14:03:54 2006 UTC
# Line 26  Line 26 
26      along with this program; if not, write to the Free Software      along with this program; if not, write to the Free Software
27      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  */  */
29  /* $Id: downloadhandler.php,v 1.2 2006/01/06 16:38:38 elmuerte Exp $ */  /* $Id: downloadhandler.php,v 1.3.2.7 2006/04/04 10:58:08 elmuerte Exp $ */
30    
31    
32  define("READBUFFER_SIZE", 4096); // read buffer size  define("READBUFFER_SIZE", 4096); // read buffer size
33  define("USE_GZIP", true); // use GZip compression for text/* files  define("USE_GZIP", true); // use GZip compression for text/* files
34    
35    define("WATERMARK", 1); // add a watermark to the image
36    define("SUBSTITUTE", 2); // replace the image with an other one
37    
38    define("UPSAMPLE_PALLET", true); // if set upsample pallet images, produces better results
39    define("DOWNSAMPLE_TO_ORIGIN", true); // if set to true downsample the watermarked
40                                         // image to the original format, otherwise it will
41                                         // be converted to a jpg
42    
43  /*  /*
44      not OS safe      not OS safe
45  */  */
46  if (!function_exists("mime_content_type"))  if (!function_exists("mime_content_type"))  {
47  {    function mime_content_type($f) {
48      function mime_content_type($f)      $f = escapeshellarg($f);
49      {      return trim( exec("file -bi ".$f) );
50          $f = escapeshellarg($f);    }
51          return trim( exec("file -bi ".$f) );  }
52      }  
53    /**
54     * Return true if the file is an image file.
55     *
56     * @param   string      the filename
57     * @return  boolean     true if it's an image we want to process
58     */
59    function __fr_is_image_file($filename) {
60      // only png, jpg, gif are supported
61      return (in_array(StrToLower(preg_replace("#^.*\.([^.]*)$#", "\\1", $filename)), array("jpg", "jpeg", "jpe", "png", "gif")));
62    }
63    
64    /**
65     * Reports the leech
66     */
67    function __fr_report_leech() {
68      global $__fr_reporting_leech;
69      $__fr_reporting_leech = true;
70      register_shutdown_function("__fr_delayed_report");
71    }
72    
73    function __fr_delayed_report() {
74      global $__fr_reporting_leech;
75      if (!$__fr_reporting_leech) return;
76      if (!function_exists('watchdog')) return;
77      watchdog('download leech', "File: <i>".$_GET["file"]."</i><br />\nSource: <i>".$_SERVER["HTTP_REFERER"]."</i>", WATCHDOG_NOTICE);
78  }  }
79    
80  /**  /**
81   * Detect leeching sites   * Detect leeching sites
82     *
83   * @param   mixed   array containing the configuration   * @param   mixed   array containing the configuration
84   * @return  bool    false if it's a leech   * @return  bool    false if it's a leech
85   */   */
86  function __anti_leech($config)  function __fr_can_download_file(&$config) {
87  {    $config["watermark"] = false;
88      if (!$config["filereq_antileech_enabled"]) return true;    if (!$config["filereq_antileech_enabled"]) return true;
89      if (!empty($_SERVER["HTTP_REFERER"]))    if (!empty($_SERVER["HTTP_REFERER"])) {
90      {      if (!preg_match("#^http(s)?://".$config["filereq_antileech_regex"]."/#i", $_SERVER["HTTP_REFERER"])) {
91          if (!preg_match("#^http(s)?://".$config["filereq_antileech_regex"]."/#i", $_SERVER["HTTP_REFERER"])) return false;  
92          if (!isset($config["filereq_nowatchdog"])) __fr_report_leech();
93    
94          if (__fr_is_image_file($config["filename"])) {
95            switch (intval($config["filereq_antileech_image_mode"])) {
96              case WATERMARK:
97                $config["watermark"] = __DRUPAL_BASE_DIR.$config["filereq_antileech_image_file"];
98                return true;
99              case SUBSTITUTE:
100                $config["filename"] = __DRUPAL_BASE_DIR.$config["filereq_antileech_image_file"];
101                return true;
102            }
103          }
104    
105          return false;
106      }      }
107      // empty referer is good, for now    }
108      return true;    // empty referer is good, for now
109      return true;
110  }  }
111    
112  /**  /**
# Line 66  function __anti_leech($config) Line 115  function __anti_leech($config)
115   * @param   function is a function in the form: function($filename) and returns the mimetype   * @param   function is a function in the form: function($filename) and returns the mimetype
116   * @return  bool     true if the download was handled   * @return  bool     true if the download was handled
117   */   */
118  function process_download($filename, $forcedl=false, $mimeOverride=null)  function __fr_process_download($filename, $forcedl=false, $watermark=false, $mimeOverride=null)
119  {  {
120      if (!file_exists($filename))    global $__fr_reporting_leech;
     {  
         trigger_error("Requested download file does not exist: <i>".$filename."</i>", E_USER_ERROR);  
         return false;  
     }  
     if (!is_readable($filename))  
     {  
         trigger_error("Requested file is not readable: <i>".$filename."</i>", E_USER_ERROR);  
         return false;  
     }  
     $fi["path"] = $filename;  
     $fi["size"] = filesize($filename);  
     $fi["time"] = filemtime($filename);  
     $fi["ranges"] = array();  
   
     if ($forcedl) $contentDisposition = "attachment";  // inline/attachment  
     else {  
         //$_SERVER["HTTP_ACCEPT"];  
         $contentDisposition = "inline"; // TODO: improve?  
     }  
   
     header("Content-Type: "); // unset content type  
   
     if (isset($_SERVER["HTTP_RANGE"]))  
     {  
         // process ranges  
         if (preg_match("/^bytes=(.*)$/", trim($_SERVER["HTTP_RANGE"]), $ranges))  
         {  
             $ranges = explode(",", $ranges[1]);  
             for ($i = 0; $i < count($ranges); $i++)  
             {  
                 if (preg_match("/^([0-9]*)-([0-9]*)$/", $ranges[$i], $r))  
                 {  
                     if ($r[1] == "") // -X : last X bytes  
                     {  
                         $fi["ranges"][] = array("start" => $fi["size"]-intval($r[2]), "stop" => $fi["size"]-1, "count" => intval($r[2]));  
                     }  
                     else if ($r[2] == "") // X- : skip first X bytes  
                     {  
                         $fi["ranges"][] = array("start" => intval($r[1]), "stop" => $fi["size"]-1, "count" => $fi["size"]-intval($r[1]));  
                     }  
                     else if ($r[1] == "" && $r[2] == "")  
                     {  
                         $fi["ranges"] = array();  
                         break;  
                     }  
                     else { // X-Y : X to Y bytes (0 = 1st byte)  
                         $fi["ranges"][] = array("start" => intval($r[1]), "stop" => intval($r[2]), "count" => intval($r[2])-intval($r[1])+1);  
                     }  
                 }  
                 else {  
                     $fi["ranges"] = array();  
                     break;  
                 }  
             }  
         }  
121    
122          for ($i = 0; $i < count($fi["ranges"]); $i++)    if (!file_exists($filename)) {
123          {      trigger_error("Requested download file does not exist: <i>".$filename."</i>", E_USER_ERROR);
124              if (($fi["ranges"][$i]["start"] < 0)      return false;
125                  || ($fi["ranges"][$i]["stop"] >= $fi["size"]))    }
126              {    if (!is_readable($filename)) {
127                  $fi["ranges"] = array();      trigger_error("Requested file is not readable: <i>".$filename."</i>", E_USER_ERROR);
128                  break;      return false;
129              }    }
130    
131      $fi["path"] = $filename;
132      $fi["name"] = basename($filename); // name reported to the browser
133      $fi["size"] = filesize($filename);
134      $fi["time"] = filemtime($filename);
135      $fi["ranges"] = array();
136    
137      if ($forcedl) $contentDisposition = "attachment";  // inline/attachment
138      else {
139        //$_SERVER["HTTP_ACCEPT"];
140        $contentDisposition = "inline"; // TODO: improve?
141      }
142    
143      $do_watermark = false;
144      if (file_exists($watermark) && __fr_is_image_file($fi["name"])) {
145        $do_watermark = true;
146        $fi["name"] = preg_replace("#^(.*)(\.[^.]*)$#", "\\1_branded\\2", $fi["name"]);
147      }
148    
149      header("Content-Type: "); // unset content type
150    
151      if (!$do_watermark && isset($_SERVER["HTTP_RANGE"])) {
152        // process ranges
153        if (preg_match("/^bytes=(.*)$/", trim($_SERVER["HTTP_RANGE"]), $ranges)) {
154          $ranges = explode(",", $ranges[1]);
155          for ($i = 0; $i < count($ranges); $i++) {
156            if (preg_match("/^([0-9]*)-([0-9]*)$/", $ranges[$i], $r)) {
157              if ($r[1] == "") { // -X : last X bytes
158                $fi["ranges"][] = array("start" => $fi["size"]-intval($r[2]), "stop" => $fi["size"]-1, "count" => intval($r[2]));
159              }
160              else if ($r[2] == "") { // X- : skip first X bytes
161                $fi["ranges"][] = array("start" => intval($r[1]), "stop" => $fi["size"]-1, "count" => $fi["size"]-intval($r[1]));
162              }
163              else if ($r[1] == "" && $r[2] == "") {
164                $fi["ranges"] = array();
165                break;
166              }
167              else { // X-Y : X to Y bytes (0 = 1st byte)
168                $fi["ranges"][] = array("start" => intval($r[1]), "stop" => intval($r[2]), "count" => intval($r[2])-intval($r[1])+1);
169              }
170          }          }
171            else {
172          if (count($fi["ranges"]) == 0) // no valid ranges specified            $fi["ranges"] = array();
173          {            break;
             header("HTTP/1.1 416 Requested range not satisfiable");  
             header("Content-Range: bytes */".$fi["size"]);  
             return true;  
         }  
     }  
   
     if ($_SERVER["HTTP_IF_MODIFIED_SINCE"])  
     {  
         if ($fi["time"] > strftime($_SERVER["HTTP_IF_MODIFIED_SINCE"]))  
         {  
             header("HTTP/1.1 304 Not Modified");  
             return true;  
174          }          }
175          }
176      }      }
177    
178      // figure out mime last      for ($i = 0; $i < count($fi["ranges"]); $i++) {
179      $fi["mime"] = "";        if (($fi["ranges"][$i]["start"] < 0)
180      if (is_callable($mimeOverride)) $fi["mime"] = $mimeOverride(basename($filename));          || ($fi["ranges"][$i]["stop"] >= $fi["size"])) {
181      if (empty($fi["mime"]))          $fi["ranges"] = array();
182      {          break;
183          if (function_exists("mime_content_type")) $fi["mime"] = mime_content_type($filename);        }
184      }      }
185      if (!preg_match("#^(\w*)/(\w*)$#i", $fi["mime"])) $fi["mime"] = "application/octet-stream";  
186        if (count($fi["ranges"]) == 0) { // no valid ranges specified
187      // send headers        header("HTTP/1.1 416 Requested range not satisfiable");
188          header("Content-Range: bytes */".$fi["size"]);
189      if (count($fi["ranges"]) > 0)        return true;
190      {      }
191          header("HTTP/1.1 206 Partial content");    }
192          if (count($fi["ranges"]) == 1)  
193          {    if ($_SERVER["HTTP_IF_MODIFIED_SINCE"]) {
194              header("Content-Range: bytes ".$fi["ranges"][0]["start"]."-".$fi["ranges"][0]["stop"]."/".$fi["size"]);      if ($fi["time"] > strftime($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
195              header("Content-Length: ".$fi["ranges"][0]["count"]);        header("HTTP/1.1 304 Not Modified");
196          }        $__fr_reporting_leech = false; // disable watchdog reporting
197          else {        return true;
198              $byteRangeBoundary = "_BYTE_RANGE_".md5(microtime());      }
199              header("Content-Type: multipart/byteranges; boundary=".$byteRangeBoundary);    }
200          }  
201      // figure out mime last
202      $fi["mime"] = "";
203      if (is_callable($mimeOverride)) $fi["mime"] = $mimeOverride(basename($filename));
204      if (empty($fi["mime"])) {
205        if (function_exists("mime_content_type")) $fi["mime"] = mime_content_type($filename);
206      }
207      if (!preg_match("#^(\w*)/(\w*)$#i", $fi["mime"])) $fi["mime"] = "application/octet-stream";
208    
209      // send headers
210    
211      if (count($fi["ranges"]) > 0)  {
212        header("HTTP/1.1 206 Partial content");
213        if (count($fi["ranges"]) == 1) {
214          header("Content-Range: bytes ".$fi["ranges"][0]["start"]."-".$fi["ranges"][0]["stop"]."/".$fi["size"]);
215          header("Content-Length: ".$fi["ranges"][0]["count"]);
216      }      }
217      else {      else {
218          header("HTTP/1.1 200 Ok");        $byteRangeBoundary = "_BYTE_RANGE_".md5(microtime());
219          header("Content-Length: ".$fi["size"]);        header("Content-Type: multipart/byteranges; boundary=".$byteRangeBoundary);
220      }      }
221      }
222      else {
223        header("HTTP/1.1 200 Ok");
224        if (!$do_watermark) header("Content-Length: ".$fi["size"]); // we don't know the final size when watermarking
225      }
226    
227      if (!$do_watermark) header("Accept-Ranges: bytes");
228      if (count($fi["ranges"]) <= 1) header("Content-Type: ".$fi["mime"]);
229      /*if (!$do_watermark)*/ header("Last-Modified: ".gmdate("D, d M Y H:i:s \G\M\T", $fi["time"]));
230      header("Content-Disposition: ".$contentDisposition."; filename=\"".$fi["name"]."\"");
231    
232      // encode text files when accepted
233      if (USE_GZIP && preg_match("#^text/#i", $fi["mime"]) && extension_loaded("zlib")
234        && in_array("gzip", explode(",", $_SERVER["HTTP_ACCEPT_ENCODING"]))) {
235        ob_start("ob_gzhandler");
236        ob_implicit_flush(false); // content length header needs to be updated
237      }
238    
239      if ($_SERVER["REQUEST_METHOD"] == "HEAD") return true; // no body for you
240    
241      if ($do_watermark) {
242        header("Expires: ".gmdate("D, d M Y H:i:s \G\M\T", mktime()+900)); // expire watermark in 15 minutes
243        header("X-Watermarked: true");
244        return __fr_add_watermark($filename, $watermark);
245      }
246    
247      $fp = fopen($filename, "rb");
248      if (!$fp) {
249        header("HTTP/1.1 500 Internal Server Error");
250        echo "Unable to open file for reading.";
251        return true;
252      }
253    
254      header("Accept-Ranges: bytes");    if (count($fi["ranges"]) == 0)  {
255      if (count($fi["ranges"]) <= 1) header("Content-Type: ".$fi["mime"]);      while (!connection_status() && !feof($fp)) {
256      header("Last-Modified: ".date("r", $fi["time"]));        echo fread($fp, READBUFFER_SIZE);
257      header("Content-Disposition: ".$contentDisposition."; filename=\"".(basename($fi["path"]))."\"");      }
258      }
259      // encode text files when accepted    else if (count($fi["ranges"]) == 1)  {
260      if (USE_GZIP && preg_match("#^text/#i", $fi["mime"]) && extension_loaded("zlib")      fseek($fp, $fi["ranges"][0]["start"], SEEK_SET);
261          && in_array("gzip", explode(",", $_SERVER["HTTP_ACCEPT_ENCODING"])))      while (!connection_status() && (ftell($fp) <= $fi["ranges"][0]["stop"]-READBUFFER_SIZE)) {
262      {        echo fread($fp, READBUFFER_SIZE);
263          ob_start("ob_gzhandler");      }
264          ob_implicit_flush(false); // content length header needs to be updated      if ($fi["ranges"][0]["stop"]-ftell($fp) > 1) echo fread($fp, $fi["ranges"][0]["stop"]-ftell($fp)+1);
265      }    }
266      else {
267      if ($_SERVER["REQUEST_METHOD"] == "HEAD") return true; // no body for you      for ($i = 0; $i < count($fi["ranges"]); $i++) {
268          echo "\r\n";
269      $fp = fopen($filename, "rb");        echo "--".$byteRangeBoundary."\r\n";
270      if (!$fp)        echo "Content-Type: ".$fi["mime"]."\r\n";
271      {        echo "Content-Range: bytes ".$fi["ranges"][$i]["start"]."-".$fi["ranges"][$i]["stop"]."/".$fi["size"]."\r\n";
272          header("HTTP/1.1 500 Internal Server Error");        echo "Content-Disposition: ".$contentDisposition."; filename=".$fi["name"]."\r\n";
273          echo "Unable to open file for reading.";        echo "\r\n";
274          return true;  
275      }        fseek($fp, $fi["ranges"][$i]["start"], SEEK_SET);
276          while (!connection_status() && (ftell($fp) <= $fi["ranges"][$i]["stop"]-READBUFFER_SIZE)) {
277      if (count($fi["ranges"]) == 0)          echo fread($fp, READBUFFER_SIZE);
278      {        }
279          while (!connection_status() && !feof($fp))        if ($fi["ranges"][$i]["stop"]-ftell($fp) > 1) echo fread($fp, $fi["ranges"][$i]["stop"]-ftell($fp)+1);
280          {      }
281              echo fread($fp, READBUFFER_SIZE);      echo "\r\n";
282          }      echo "--".$byteRangeBoundary."--";
283      }    }
284      else if (count($fi["ranges"]) == 1)  
285      {    fclose($fp);
286          fseek($fp, $fi["ranges"][0]["start"], SEEK_SET);    return true;
287          while (!connection_status() && (ftell($fp) <= $fi["ranges"][0]["stop"]-READBUFFER_SIZE))  }
288          {  
289              echo fread($fp, READBUFFER_SIZE);  
290          }  function __fr_imagetruecolortopalette_ex( $image, $dither, $ncolors )
291          if ($fi["ranges"][0]["stop"]-ftell($fp) > 1) echo fread($fp, $fi["ranges"][0]["stop"]-ftell($fp)+1);  {
292      if (function_exists('imagecolormatch')) {
293        $width = imagesx( $image );
294        $height = imagesy( $image );
295        $colors_handle = ImageCreateTrueColor( $width, $height );
296        ImageCopyMerge( $colors_handle, $image, 0, 0, 0, 0, $width, $height, 100 );
297      }
298      ImageTrueColorToPalette( $image, $dither, $ncolors );
299      if (function_exists('imagecolormatch')) {
300        ImageColorMatch( $colors_handle, $image );
301        ImageDestroy( $colors_handle );
302      }
303    }
304    
305    /*
306      Source of this routine:
307      http://www.php.net/manual/en/function.imagecopymerge.php
308    
309      slightly modified
310    */
311    
312    function __fr_add_watermark($sourcefile, $watermarkfile) {
313       #
314       # $sourcefile = Filename of the picture to be watermarked.
315       # $watermarkfile = Filename of the 24-bit PNG watermark file.
316       #
317    
318       //Get the resource ids of the pictures
319       $watermarkfile_id = imagecreatefrompng($watermarkfile);
320    
321       imageAlphaBlending($watermarkfile_id, false);
322       imageSaveAlpha($watermarkfile_id, true);
323    
324       $fileType = getimagesize($sourcefile);
325    
326       switch($fileType[2]) {
327         case 1:
328           $sourcefile_id = imagecreatefromgif($sourcefile);
329           break;
330    
331         case 2:
332           $sourcefile_id = imagecreatefromjpeg($sourcefile);
333           break;
334    
335         case 3:
336           $sourcefile_id = imagecreatefrompng($sourcefile);
337           break;
338    
339       }
340    
341       //Get the sizes of both pix
342      $sourcefile_width=imageSX($sourcefile_id);
343      $sourcefile_height=imageSY($sourcefile_id);
344      $watermarkfile_width=imageSX($watermarkfile_id);
345      $watermarkfile_height=imageSY($watermarkfile_id);
346    
347      $dest_x = ( $sourcefile_width / 2 ) - ( $watermarkfile_width / 2 );
348      $dest_y = ( $sourcefile_height / 2 ) - ( $watermarkfile_height / 2 );
349    
350      $upsampled = 0;
351    
352      // if a gif, we have to upsample it to a truecolor image
353      if(UPSAMPLE_PALLET && !imageistruecolor($sourcefile_id)) {
354        // create an empty truecolor container
355        $tempimage = imagecreatetruecolor($sourcefile_width, $sourcefile_height);
356    
357        $upsampled = imagecolorstotal($sourcefile_id);
358    
359        // copy the 8-bit gif into the truecolor image
360        imagecopy($tempimage, $sourcefile_id, 0, 0, 0, 0, $sourcefile_width, $sourcefile_height);
361        imagedestroy($sourcefile_id);
362    
363        // copy the source_id int
364        $sourcefile_id = $tempimage;
365      }
366    
367      imagecopy($sourcefile_id, $watermarkfile_id, $dest_x, $dest_y, 0, 0, $watermarkfile_width, $watermarkfile_height);
368    
369      if ($upsampled) {
370        if (DOWNSAMPLE_TO_ORIGIN && function_exists('imagetruecolortopalette')) {
371          __fr_imagetruecolortopalette_ex($sourcefile_id, true, 256);
372      }      }
373      else {      else {
374          for ($i = 0; $i < count($fi["ranges"]); $i++)        $fileType[2] = 2; // force to JPEG to reduce size
         {  
             echo "\r\n";  
             echo "--".$byteRangeBoundary."\r\n";  
             echo "Content-Type: ".$fi["mime"]."\r\n";  
             echo "Content-Range: bytes ".$fi["ranges"][$i]["start"]."-".$fi["ranges"][$i]["stop"]."/".$fi["size"]."\r\n";  
             echo "Content-Disposition: ".$contentDisposition."; filename=".basename($fi["path"])."\r\n";  
             echo "\r\n";  
   
             fseek($fp, $fi["ranges"][$i]["start"], SEEK_SET);  
             while (!connection_status() && (ftell($fp) <= $fi["ranges"][$i]["stop"]-READBUFFER_SIZE))  
             {  
                 echo fread($fp, READBUFFER_SIZE);  
             }  
             if ($fi["ranges"][$i]["stop"]-ftell($fp) > 1) echo fread($fp, $fi["ranges"][$i]["stop"]-ftell($fp)+1);  
         }  
         echo "\r\n";  
         echo "--".$byteRangeBoundary."--";  
375      }      }
376      }
377    
378      fclose($fp);    //Create a jpeg out of the modified picture
379      return true;    switch($fileType[2]) {
380    
381        // remember we don't need gif any more, so we use only png or jpeg.
382        // See the upsaple code immediately above to see how we handle gifs
383        case 3:
384          header("Content-type: image/png");
385          imagepng ($sourcefile_id);
386          break;
387    
388        case 1:
389          if (imagetypes() & IMG_GIF) {
390            header("Content-type: image/gif");
391            imagegif ($sourcefile_id);
392            break;
393          }
394    
395        default:
396          header("Content-type: image/jpg");
397          imagejpeg ($sourcefile_id);
398      }
399    
400      imagedestroy($sourcefile_id);
401      imagedestroy($watermarkfile_id);
402  }  }
   
 ?>  

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

  ViewVC Help
Powered by ViewVC 1.1.2