Profile
MAJ is an Open Source, PHP-based content management system (CMS) that can be deployed as a blog, bulletin or message board, Internet forum, and wiki. It is extensible via PHP panels and is HTML5 and CSS3 ready.
Git
This blob has been accessed 927 times via Git panel.

  1. <?php
  2.  
  3. ##########################################################################
  4. # ZipStream - Streamed, dynamically generated zip archives.              #
  5. # by Paul Duncan <pabs@pablotron.org>                                    #
  6. #                                                                        #
  7. # Copyright (C) 2007 Paul Duncan <pabs@pablotron.org>                    #
  8. #                                                                        #
  9. # Permission is hereby granted, free of charge, to any person obtaining  #
  10. # a copy of this software and associated documentation files (the        #
  11. # "Software"), to deal in the Software without restriction, including    #
  12. # without limitation the rights to use, copy, modify, merge, publish,    #
  13. # distribute, sublicense, and/or sell copies of the Software, and to     #
  14. # permit persons to whom the Software is furnished to do so, subject to  #
  15. # the following conditions:                                              #
  16. #                                                                        #
  17. # The above copyright notice and this permission notice shall be         #
  18. # included in all copies of the Software, its documentation and          #
  19. # marketing & publicity materials, and acknowledgment shall be given in  #
  20. # the documentation, materials and software packages that this Software  #
  21. # was used.                                                              #
  22. #                                                                        #
  23. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        #
  24. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     #
  25. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. #
  26. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR      #
  27. # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  #
  28. # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  #
  29. # OTHER DEALINGS IN THE SOFTWARE.                                        #
  30. ##########################################################################
  31.  
  32. #
  33. # ZipStream - Streamed, dynamically generated zip archives.
  34. # by Paul Duncan <pabs@pablotron.org>
  35. #
  36. # Usage:
  37. #
  38. # Streaming zip archives is a simple, three-step process:
  39. #
  40. # 1.  Create the zip stream:
  41. #
  42. #     $zip = new ZipStream('example.zip');
  43. #
  44. # 2.  Add one or more files to the archive:
  45. #
  46. #     # add first file
  47. #     $data = file_get_contents('some_file.gif');
  48. #     $zip->add_file('some_file.gif', $data);
  49. #
  50. #     # add second file
  51. #     $data = file_get_contents('some_file.gif');
  52. #     $zip->add_file('another_file.png', $data);
  53. #
  54. # 3.  Finish the zip stream:
  55. #
  56. #     $zip->finish();
  57. #
  58. # You can also add an archive comment, add comments to individual files,
  59. # and adjust the timestamp of files.  See the API documentation for each
  60. # method below for additional information.
  61. #
  62. # Example:
  63. #
  64. #   # create a new zip stream object
  65. #   $zip = new ZipStream('some_files.zip');
  66. #
  67. #   # list of local files
  68. #   $files = array('foo.txt', 'bar.jpg');
  69. #
  70. #   # read and add each file to the archive
  71. #   foreach ($files as $path)
  72. #     $zip->add_file($path, file_get_contents($path));
  73. #
  74. #   # write archive footer to stream
  75. #   $zip->finish();
  76. #
  77. class ZipStream {
  78.   var $opt = array(),
  79.       $files = array(),
  80.       $cdr_ofs = 0,
  81.       $ofs = 0;
  82.  
  83.   #
  84.  # Create a new ZipStream object.
  85.  #
  86.  # Parameters:
  87.  #
  88.  #   $name - Name of output file (optional).
  89.  #   $opt  - Hash of archive options (optional, see "Archive Options"
  90.  #           below).
  91.  #
  92.  # Archive Options:
  93.  #
  94.  #   comment             - Comment for this archive.
  95.  #   content_type        - HTTP Content-Type.  Defaults to 'application/x-zip'.
  96.  #   content_disposition - HTTP Content-Disposition.  Defaults to
  97.  #                         'attachment; filename=\"FILENAME\"', where
  98.  #                         FILENAME is the specified filename.
  99.  #   large_file_size     - Size, in bytes, of the largest file to try
  100.  #                         and load into memory (used by
  101.  #                         add_file_from_path()).  Large files may also
  102.  #                         be compressed differently; see the
  103.  #                         'large_file_method' option.
  104.  #   large_file_method   - How to handle large files.  Legal values are
  105.  #                         'store' (the default), or 'deflate'.  Store
  106.  #                         sends the file raw and is significantly
  107.  #                         faster, while 'deflate' compresses the file
  108.  #                         and is much, much slower.  Note that deflate
  109.  #                         must compress the file twice and extremely
  110.  #                         slow.
  111.  #   send_http_headers   - Boolean indicating whether or not to send
  112.  #                         the HTTP headers for this file.
  113.  #
  114.  # Note that content_type and content_disposition do nothing if you are
  115.  # not sending HTTP headers.
  116.  #
  117.  # Large File Support:
  118.  #
  119.  # By default, the method add_file_from_path() will send send files
  120.  # larger than 20 megabytes along raw rather than attempting to
  121.  # compress them.  You can change both the maximum size and the
  122.  # compression behavior using the large_file_* options above, with the
  123.  # following caveats:
  124.  #
  125.  # * For "small" files (e.g. files smaller than large_file_size), the
  126.  #   memory use can be up to twice that of the actual file.  In other
  127.  #   words, adding a 10 megabyte file to the archive could potentially
  128.  #   occupty 20 megabytes of memory.
  129.  #
  130.  # * Enabling compression on large files (e.g. files larger than
  131.  #   large_file_size) is extremely slow, because ZipStream has to pass
  132.  #   over the large file once to calculate header information, and then
  133.  #   again to compress and send the actual data.
  134.  #
  135.  # Examples:
  136.  #
  137.  #   # create a new zip file named 'foo.zip'
  138.  #   $zip = new ZipStream('foo.zip');
  139.  #
  140.  #   # create a new zip file named 'bar.zip' with a comment
  141.  #   $zip = new ZipStream('bar.zip', array(
  142.  #     'comment' => 'this is a comment for the zip file.',
  143.  #   ));
  144.  #
  145.  # Notes:
  146.  #
  147.  # If you do not set a filename, then this library _DOES NOT_ send HTTP
  148.  # headers by default.  This behavior is to allow software to send its
  149.  # own headers (including the filename), and still use this library.
  150.  #
  151.  function ZipStream($name = null, $opt = array()) {
  152.     # save options
  153.    $this->opt = $opt;
  154.  
  155.     # set large file defaults: size = 20 megabytes, method = store
  156.    if (!$this->opt['large_file_size'])
  157.       $this->opt['large_file_size'] = 20 * 1024 * 1024;
  158.     if (!$this->opt['large_file_method'])
  159.       $this->opt['large_file_method'] = 'store';
  160.  
  161.     $this->output_name = $name;
  162.     if ($name || $opt['send_http_headers'])
  163.       $this->need_headers = true;
  164.   }
  165.  
  166.   #
  167.  # add_file - add a file to the archive
  168.  #
  169.  # Parameters:
  170.  #  
  171.  #  $name - path of file in archive (including directory).
  172.  #  $data - contents of file
  173.  #  $opt  - Hash of options for file (optional, see "File Options"
  174.  #          below).  
  175.  #
  176.  # File Options:
  177.  #  time     - Last-modified timestamp (seconds since the epoch) of
  178.  #             this file.  Defaults to the current time.
  179.  #  comment  - Comment related to this file.
  180.  #
  181.  # Examples:
  182.  #
  183.  #   # add a file named 'foo.txt'
  184.  #   $data = file_get_contents('foo.txt');
  185.  #   $zip->add_file('foo.txt', $data);
  186.  #
  187.  #   # add a file named 'bar.jpg' with a comment and a last-modified
  188.  #   # time of two hours ago
  189.  #   $data = file_get_contents('bar.jpg');
  190.  #   $zip->add_file('bar.jpg', $data, array(
  191.  #     'time'    => time() - 2 * 3600,
  192.  #     'comment' => 'this is a comment about bar.jpg',
  193.  #   ));
  194.  #
  195.  function add_file($name, $data, $opt = array()) {
  196.     # compress data
  197.    $zdata = gzdeflate($data);
  198.  
  199.     # calculate header attributes
  200.    $crc  = crc32($data);
  201.     $zlen = strlen($zdata);
  202.     $len  = strlen($data);
  203.     $meth = 0x08;
  204.  
  205.     # send file header
  206.    $this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
  207.  
  208.     # print data
  209.    $this->send($zdata);
  210.   }
  211.  
  212.   #
  213.  # add_file_from_path - add a file at path to the archive.
  214.  #
  215.  # Note that large files may be compresed differently than smaller
  216.  # files; see the "Large File Support" section above for more
  217.  # information.
  218.  #
  219.  # Parameters:
  220.  #  
  221.  #  $name - name of file in archive (including directory path).
  222.  #  $path - path to file on disk.
  223.  #  $opt  - Hash of options for file (optional, see "File Options"
  224.  #          below).  
  225.  #
  226.  # File Options:
  227.  #  time     - Last-modified timestamp (seconds since the epoch) of
  228.  #             this file.  Defaults to the current time.
  229.  #  comment  - Comment related to this file.
  230.  #
  231.  # Examples:
  232.  #
  233.  #   # add a file named 'foo.txt' from the local file '/tmp/foo.txt'
  234.  #   $zip->add_file_from_path('foo.txt', '/tmp/foo.txt');
  235.  #
  236.  #   # add a file named 'bigfile.rar' from the local file
  237.  #   # '/usr/share/bigfile.rar' with a comment and a last-modified
  238.  #   # time of two hours ago
  239.  #   $path = '/usr/share/bigfile.rar';
  240.  #   $zip->add_file_from_path('bigfile.rar', $path, array(
  241.  #     'time'    => time() - 2 * 3600,
  242.  #     'comment' => 'this is a comment about bar.jpg',
  243.  #   ));
  244.  #
  245.  function add_file_from_path($name, $path, $opt = array()) {
  246.     if ($this->is_large_file($path)) {
  247.       # file is too large to be read into memory; add progressively
  248.      $this->add_large_file($name, $path, $opt);
  249.     } else {
  250.       # file is small enough to read into memory; read file contents and
  251.      # handle with add_file()
  252.      $data = file_get_contents($path);
  253.       $this->add_file($name, $data, $opt);
  254.     }
  255.   }
  256.  
  257.   #
  258.  # finish - Write zip footer to stream.
  259.  #
  260.  # Example:
  261.  #
  262.  #   # add a list of files to the archive
  263.  #   $files = array('foo.txt', 'bar.jpg');
  264.  #   foreach ($files as $path)
  265.  #     $zip->add_file($path, file_get_contents($path));
  266.  #
  267.  #   # write footer to stream
  268.  #   $zip->finish();
  269.  #
  270.  function finish() {
  271.     # add trailing cdr record
  272.    $this->add_cdr($this->opt);
  273.     $this->clear();
  274.   }
  275.  
  276.   ###################
  277.  # PRIVATE METHODS #
  278.  ###################
  279.  
  280.   #
  281.  # Create and send zip header for this file.
  282.  #
  283.  function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
  284.     # calculate name length
  285.    $nlen = strlen($name);
  286.  
  287.     # create dos timestamp
  288.    $opt['time'] = $opt['time'] ? $opt['time'] : time();
  289.     $dts = $this->dostime($opt['time']);
  290.  
  291.     # build file header
  292.    $fields = array(            # (from V.A of APPNOTE.TXT)
  293.      array('V', 0x04034b50),     # local file header signature
  294.      array('v', 0x14),           # version needed to extract
  295.      array('v', 0x00),           # general purpose bit flag
  296.      array('v', $meth),          # compresion method (deflate or store)
  297.      array('V', $dts),           # dos timestamp
  298.      array('V', $crc),           # crc32 of data
  299.      array('V', $zlen),          # compressed data length
  300.      array('V', $len),           # uncompressed data length
  301.      array('v', $nlen),          # filename length
  302.      array('v', 0),              # extra data len
  303.    );
  304.  
  305.     # pack fields and calculate "total" length
  306.    $ret = $this->pack_fields($fields);
  307.     $cdr_len = strlen($ret) + $nlen + $zlen;
  308.  
  309.     # print header and filename
  310.    $this->send($ret . $name);
  311.  
  312.     # add to central directory record and increment offset
  313.    $this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
  314.   }
  315.  
  316.   #
  317.  # Add a large file from the given path.
  318.  #
  319.  function add_large_file($name, $path, $opt = array()) {
  320.     $st = stat($path);
  321.     $block_size = 1048576; # process in 1 megabyte chunks
  322.    $algo = 'crc32b';
  323.  
  324.     # calculate header attributes
  325.    $zlen = $len = $st['size'];
  326.  
  327.     $meth_str = $this->opt['large_file_method'];
  328.     if ($meth_str == 'store') {
  329.       # store method
  330.      $meth = 0x00;
  331.       $crc  = unpack('V', hash_file($algo, $path, true));
  332.       $crc = $crc[1];
  333.     } elseif ($meth_str == 'deflate') {
  334.       # deflate method
  335.      $meth = 0x08;
  336.  
  337.       # open file, calculate crc and compressed file length
  338.      $fh = fopen($path, 'rb');
  339.       $hash_ctx = hash_init($algo);
  340.       $zlen = 0;
  341.  
  342.       # read each block, update crc and zlen
  343.      while ($data = fgets($fh, $block_size)) {
  344.         hash_update($hash_ctx, $data);
  345.         $data = gzdeflate($data);
  346.         $zlen += strlen($data);
  347.       }
  348.  
  349.       # close file and finalize crc
  350.      fclose($fh);
  351.       $crc = unpack('V', hash_final($hash_ctx, true));
  352.       $crc = $crc[1];
  353.     } else {
  354.       die("unknown large_file_method: $meth_str");
  355.     }
  356.  
  357.     # send file header
  358.    $this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
  359.  
  360.     # open input file
  361.    $fh = fopen($path, 'rb');
  362.  
  363.     # send file blocks
  364.    while ($data = fgets($fh, $block_size)) {
  365.       if ($meth_str == 'deflate')
  366.         $data = gzdeflate($data);
  367.  
  368.       # send data
  369.      $this->send($data);
  370.     }
  371.  
  372.     # close input file
  373.    fclose($fh);
  374.   }
  375.  
  376.   #
  377.  # Is this file larger than large_file_size?
  378.  #
  379.  function is_large_file($path) {
  380.     $st = stat($path);
  381.     return ($this->opt['large_file_size'] > 0) &&
  382.            ($st['size'] > $this->opt['large_file_size']);
  383.   }
  384.  
  385.   #
  386.  # Save file attributes for trailing CDR record.
  387.  #
  388.  function add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $rec_len) {
  389.     $this->files[] = array($name, $opt, $meth, $crc, $zlen, $len, $this->ofs);
  390.     $this->ofs += $rec_len;
  391.   }
  392.  
  393.   #
  394.  # Send CDR record for specified file.
  395.  #
  396.  function add_cdr_file($args) {
  397.     list ($name, $opt, $meth, $crc, $zlen, $len, $ofs) = $args;
  398.  
  399.     # get attributes
  400.    $comment = $opt['comment'] ? $opt['comment'] : '';
  401.  
  402.     # get dos timestamp
  403.    $dts = $this->dostime($opt['time']);
  404.  
  405.     $fields = array(                  # (from V,F of APPNOTE.TXT)
  406.      array('V', 0x02014b50),           # central file header signature
  407.      array('v', 0x00),                 # version made by
  408.      array('v', 0x14),                 # version needed to extract
  409.      array('v', 0x00),                 # general purpose bit flag
  410.      array('v', $meth),                # compresion method (deflate or store)
  411.      array('V', $dts),                 # dos timestamp
  412.      array('V', $crc),                 # crc32 of data
  413.      array('V', $zlen),                # compressed data length
  414.      array('V', $len),                 # uncompressed data length
  415.      array('v', strlen($name)),        # filename length
  416.      array('v', 0),                    # extra data len
  417.      array('v', strlen($comment)),     # file comment length
  418.      array('v', 0),                    # disk number start
  419.      array('v', 0),                    # internal file attributes
  420.      array('V', 32),                   # external file attributes
  421.      array('V', $ofs),                 # relative offset of local header
  422.    );
  423.  
  424.     # pack fields, then append name and comment
  425.    $ret = $this->pack_fields($fields) . $name . $comment;
  426.  
  427.     $this->send($ret);
  428.  
  429.     # increment cdr offset
  430.    $this->cdr_ofs += strlen($ret);
  431.   }
  432.  
  433.   #
  434.  # Send CDR EOF (Central Directory Record End-of-File) record.
  435.  #
  436.  function add_cdr_eof($opt = null) {
  437.     $num = count($this->files);
  438.     $cdr_len = $this->cdr_ofs;
  439.     $cdr_ofs = $this->ofs;
  440.  
  441.     # grab comment (if specified)
  442.    $comment = '';
  443.     if ($opt && $opt['comment'])
  444.       $comment = $opt['comment'];
  445.  
  446.     $fields = array(                # (from V,F of APPNOTE.TXT)
  447.      array('V', 0x06054b50),         # end of central file header signature
  448.      array('v', 0x00),               # this disk number
  449.      array('v', 0x00),               # number of disk with cdr
  450.      array('v', $num),               # number of entries in the cdr on this disk
  451.      array('v', $num),               # number of entries in the cdr
  452.      array('V', $cdr_len),           # cdr size
  453.      array('V', $cdr_ofs),           # cdr ofs
  454.      array('v', strlen($comment)),   # zip file comment length
  455.    );
  456.  
  457.     $ret = $this->pack_fields($fields) . $comment;
  458.     $this->send($ret);
  459.   }
  460.  
  461.   #
  462.  # Add CDR (Central Directory Record) footer.
  463.  #
  464.  function add_cdr($opt = null) {
  465.     foreach ($this->files as $file)
  466.       $this->add_cdr_file($file);
  467.     $this->add_cdr_eof($opt);
  468.   }
  469.  
  470.   #
  471.  # Clear all internal variables.  Note that the stream object is not
  472.  # usable after this.
  473.  #
  474.  function clear() {
  475.     $this->files = array();
  476.     $this->ofs = 0;
  477.     $this->cdr_ofs = 0;
  478.     $this->opt = array();
  479.   }
  480.  
  481.   ###########################
  482.  # PRIVATE UTILITY METHODS #
  483.  ###########################
  484.  
  485.   #
  486.  # Send HTTP headers for this stream.
  487.  #
  488.  function send_http_headers() {
  489.     # grab options
  490.    $opt = $this->opt;
  491.    
  492.     # grab content type from options
  493.    $content_type = 'application/x-zip';
  494.     if ($opt['content_type'])
  495.       $content_type = $this->opt['content_type'];
  496.  
  497.     # grab content disposition
  498.    $disposition = 'attachment';
  499.     if ($opt['content_disposition'])
  500.       $disposition = $opt['content_disposition'];
  501.  
  502.     if ($this->output_name)
  503.       $disposition .= "; filename=\"{$this->output_name}\"";
  504.  
  505.     $headers = array(
  506.       'Content-Type'              => $content_type,
  507.       'Content-Disposition'       => $disposition,
  508.       'Pragma'                    => 'public',
  509.       'Cache-Control'             => 'public, must-revalidate',
  510.       'Content-Transfer-Encoding' => 'binary',
  511.     );
  512.  
  513.     foreach ($headers as $key => $val)
  514.       header("$key: $val");
  515.   }
  516.  
  517.   #
  518.  # Send string, sending HTTP headers if necessary.
  519.  #
  520.  function send($str) {
  521.     if ($this->need_headers)
  522.       $this->send_http_headers();
  523.     $this->need_headers = false;
  524.  
  525.     echo $str;
  526.   }
  527.  
  528.   #
  529.  # Convert a UNIX timestamp to a DOS timestamp.
  530.  #
  531.  function dostime($when = 0) {
  532.     # get date array for timestamp
  533.    $d = getdate($when);
  534.  
  535.     # set lower-bound on dates
  536.    if ($d['year'] < 1980) {
  537.       $d = array('year' => 1980, 'mon' => 1, 'mday' => 1,
  538.                  'hours' => 0, 'minutes' => 0, 'seconds' => 0);
  539.     }
  540.  
  541.     # remove extra years from 1980
  542.    $d['year'] -= 1980;
  543.  
  544.     # return date string
  545.    return ($d['year'] << 25) | ($d['mon'] << 21) | ($d['mday'] << 16) |
  546.            ($d['hours'] << 11) | ($d['minutes'] << 5) | ($d['seconds'] >> 1);
  547.   }
  548.  
  549.   #
  550.  # Create a format string and argument list for pack(), then call
  551.  # pack() and return the result.
  552.  #
  553.  function pack_fields($fields) {
  554.     list ($fmt, $args) = array('', array());
  555.  
  556.     # populate format string and argument list
  557.    foreach ($fields as $field) {
  558.       $fmt .= $field[0];
  559.       $args[] = $field[1];
  560.     }
  561.  
  562.     # prepend format string to argument list
  563.    array_unshift($args, $fmt);
  564.  
  565.     # build output string from header and compressed data
  566.    return call_user_func_array('pack', $args);
  567.   }
  568. };
  569.  
  570. ?>
  571.  
Credits
Sunday, Sep 18, 2005, 12:00 AM (Revision 11 - Thursday, Jul 14, 2011, 7:30 PM)
Open Source enables software developers to stand on the shoulders of giants instead of reinventing the wheel, so to speak. Kudos and many thanks to the folks who made their work freely available for reuse in MAJ.

read more
Bugs and Exploits
Friday, Aug 26, 2005, 12:00 AM (Revision 23 - Wednesday, May 29, 2013, 5:25 AM)
Although MAJ started as a family project, care has been taken to make it as "safe" as possible. With more people now working on MAJ, bugs and exploitable code may sometimes slip in. We invite you to poke around and see if you can find any. Generally, there are two things you can do when you find a MAJ or PHP-related bug or exploit:

1. Take advantage of it. But hey, what's so exciting about messing up someone's blog? Ho-hum, right?
2. Report it here so that we can work on a fix and make MAJ better.

read more
Search
Download
MAJ 2.0
75 files
33448 downloads
MAJ 1.0
26 files
13526 downloads
MAJ 0.14
45 files
35642 downloads