move code up one directory
[atutor.git] / include / classes / zipfile.class.php.bak
1 <?php
2 /****************************************************************/
3 /* ATutor                                                                                                               */
4 /****************************************************************/
5 /* Copyright (c) 2002-2008 by Greg Gay & Joel Kronenberg        */
6 /* Adaptive Technology Resource Centre / University of Toronto  */
7 /* http://atutor.ca                                                                                             */
8 /*                                                              */
9 /* This program is free software. You can redistribute it and/or*/
10 /* modify it under the terms of the GNU General Public License  */
11 /* as published by the Free Software Foundation.                                */
12 /****************************************************************/
13 // $Id: zipfile.class.php 7208 2008-01-09 16:07:24Z greg $
14
15
16 /**
17 * Class for creating and accessing an archive zip file
18 * @access       public
19 * @link         http://www.pkware.com/products/enterprise/white_papers/appnote.html     for the specs
20 * @author       Joel Kronenberg
21 */
22 class zipfile {
23
24         /**
25         * string $files_data - stores file information like the header and description 
26         * @access  public 
27         */
28         var $files_data;
29
30         /**
31         * string $central_directory_headers - headers necessary for including file in central record
32         * @access  public 
33         */
34         var $central_directory_headers; 
35
36         /**
37         * int $num_entries - a counter for the number of entries in the archive
38         * @access  public 
39         */
40         var $num_entries = 0;
41
42         /**
43         * string $zip_file - complete contents of file
44         * @access  public 
45         */
46         var $zip_file;
47
48         /**
49         * boolean $is_closed - flag set to true if file is closed, false if still open
50         * @access  private
51         */
52         var $is_closed; 
53
54
55         /**
56         * Constructor method.  Initialises variables.
57         * @access       public
58         * @author       Joel Kronenberg
59         */
60         function zipfile() {
61                 $this->files_data = '';
62                 $this->central_directory_headers = '';
63                 $this->num_entries = 0;
64                 $this->is_closed = false;
65         }
66
67         /**
68         * Public interface for adding a dir and its contents recursively to zip file
69         * @access  public
70         * @param   string $dir                          the real system directory that contains the files to add to the zip              
71         * @param   string $zip_prefix_dir       the zip dir where the contents of $dir will be put in
72         * @param   string $pre_pend_dir         used during the recursion to keep track of the path, default=''
73         * @see     $_base_path                          in include/vitals.inc.php
74         * @see     priv_add_dir()                       in include/classes/zipfile.class.php
75         * @see     add_file()                           in include/classes/zipfile.class.php
76         * @author  Joel Kronenberg
77         */
78         function add_dir($dir, $zip_prefix_dir, $pre_pend_dir='') {
79                 if (!($dh = @opendir($dir.$pre_pend_dir))) {
80                         echo 'cant open dir: '.$dir.$pre_pend_dir;
81                         exit;
82                 }
83
84                 while (($file = readdir($dh)) !== false) {
85                         /* skip directories */
86                         if ($file == '.' || $file == '..') {
87                                 continue;
88                         }
89                         /* skip potential harmful files/directories */
90                         if ( (strpos($file, '..') !== false) || (strpos($file, '/') !== false)) {
91                                 continue;
92                         }
93
94                         $file_info = stat( $dir . $pre_pend_dir . $file );
95
96                         if (is_dir( $dir . $pre_pend_dir . $file )) {
97                                 /* create this dir in the zip */
98                                 $this->priv_add_dir( $zip_prefix_dir . $pre_pend_dir . $file . '/',
99                                                                          $file_info['mtime'] );
100
101                                 /* continue recursion, going down this dir */
102                                 $this->add_dir( $dir,
103                                                                 $zip_prefix_dir,
104                                                                 $pre_pend_dir . $file . '/' );
105
106                         } else {
107                                 /* add this file to the zip */
108                                 $this-> add_file( file_get_contents($dir . $pre_pend_dir . $file),
109                                                                   $zip_prefix_dir . $pre_pend_dir . $file,
110                                                                   $file_info['mtime'] );
111                         }
112                 }
113                 closedir($dh);
114         }
115
116         /**
117         * Adding a dir to the archive 
118         * @access  private
119         * @param   string $name                         directory name
120         * @param   string $timestamp            time, default=''
121         * @author  Joel Kronenberg
122         */
123     function priv_add_dir($name, $timestamp = '') {   
124         $name = str_replace("\\", "/", $name);   
125                 $old_offset = strlen($this->files_data);
126
127         $local_file_header  = "\x50\x4b\x03\x04";                                                                                               // local file header signature 4 bytes (0x04034b50) 
128         $local_file_header .= "\x0a\x00";    // ver needed to extract                                                   // version needed to extract 2 bytes
129         $local_file_header .= "\x00\x00";    // gen purpose bit flag                                                    // general purpose bit flag 2 bytes
130         $local_file_header .= "\x00\x00";    // compression method                                                              // compression method 2 bytes
131         $local_file_header .= "\x00\x00\x00\x00"; // last mod time and date                                     // last mod file time 2 bytes & last mod file date 2 bytes 
132         $local_file_header .= pack("V",0); // crc32                                                                                     // crc-32 4 bytes
133         $local_file_header .= pack("V",0); //compressed filesize                                                                // compressed size 4 bytes 
134         $local_file_header .= pack("V",0); //uncompressed filesize                                                              // uncompressed size 4 bytes
135         $local_file_header .= pack("v", strlen($name) ); //length of pathname                                   // file name length 2 bytes 
136         $local_file_header .= pack("v", 0 ); //extra field length                                                               // extra field length 2 bytes           
137         $local_file_header .= $name;                                                                                                                    // file name (variable size)  & extra field (variable size)
138         // end of "local file header" segment 
139
140         // no "file data" segment for path 
141
142         // add this entry to array 
143         $this->files_data .= $local_file_header;
144
145         // ext. file attributes mirrors MS-DOS directory attr byte, detailed 
146         // at http://support.microsoft.com/support/kb/articles/Q125/0/19.asp 
147
148                 if ($timestamp) {
149                         $v_date = getdate($timestamp);
150                 } else {
151                         $v_date = getdate();
152                 }
153                 $time = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
154                 $date = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
155
156         // now add to central record 
157         $central_directory = "\x50\x4b\x01\x02";                                                                                        // central file header signature 4 bytes (0x02014b50)
158         $central_directory .="\x14\x00";    // version made by                                                          // version made by 2 bytes
159         $central_directory .="\x14\x00";    // version needed to extract                                        // version needed to extract 2 bytes
160         $central_directory .="\x00\x00";    // gen purpose bit flag                                                     // general purpose bit flag 2 bytes
161         $central_directory .="\x00\x00";    // compression method                                                       // compression method 2 bytes
162                 $central_directory .= pack("v",$time); // time                                                                          // last mod file time 2 bytes
163         $central_directory .= pack("v",$date); // date                                                                          // last mod file date 2 bytes
164         $central_directory .= pack("V", 0); // crc32                                                                            // crc-32 4 bytes
165         $central_directory .= pack("V", 0); // compressed filesize                                                      // compressed size 4 bytes
166         $central_directory .= pack("V", 0); // uncompressed filesize                                            // uncompressed size 4 bytes
167         $central_directory .= pack("v", strlen($name) ); //length of filename                           // file name length 2 bytes
168         $central_directory .= pack("v", 0); // extra field length                                                       // extra field length 2 bytes
169         $central_directory .= pack("v", 0); // file comment length                                                      // file comment length 2 bytes 
170         $central_directory .= pack("v", 0); // disk number start                                                        // disk number start 2 bytes
171         $central_directory .= pack("v", 0); // internal file attributes                                         // internal file attributes 2 bytes
172         $central_directory .= pack("V", 16+32); //external file attributes  - 'directory' 'archive' bit set // external file attributes 4 bytes
173         $central_directory .= pack("V", $old_offset); //relative offset of local header // relative offset of local header 4 bytes
174         $central_directory .= $name;                                                                                                            // file name (variable size)
175
176         $this->central_directory_headers .= $central_directory;
177
178                 $this->num_entries++;
179     } 
180         
181         /**
182         * Public interface to create a directory in the archive.
183         * @access  public
184         * @param   string $name                         directory name
185         * @param   string $timestamp            time of creation, default=''
186         * @see     $_base_path                          in include/vitals.inc.php
187         * @see     priv_add_dir()                       in include/zipfile.class.php
188         * @author  Joel Kronenberg
189         */
190         function create_dir($name, $timestamp='') {
191                 $name = trim($name);
192
193                 if (substr($name, -1) != '/') {
194                         /* add the trailing slash */
195                         $name .= '/';
196                 }
197
198                 $this->priv_add_dir($name, $timestamp = '');
199         }
200
201         /**
202         * Adds a file to the archive.
203         * @access  public
204         * @param   string $file_data            file contents
205         * @param   string $name                         name of file in archive (add path if your want)
206         * @param   string $timestamp            time of creation, default=''
207         * @see     $_base_path                          in include/vitals.inc.php
208         * @see     priv_add_dir()                       in include/zipfile.class.php
209         * @author  Joel Kronenberg
210         */
211     function add_file($file_data, $name, $timestamp = '') {
212         $name = str_replace("\\", "/", $name);   
213         $crc = crc32($file_data);
214         $uncompressed_size = strlen($file_data);
215                 $file_data = substr(gzcompress($file_data, 9), 2, -4);
216         $compressed_size = strlen($file_data);
217                 $old_offset = strlen($this->files_data);
218
219                 /* local file header */
220         $local_file_header = "\x50\x4b\x03\x04";                                                                // local file header signature 4 bytes (0x04034b50) 
221         $local_file_header .= "\x14\x00";    // ver needed to extract                   // version needed to extract 2 bytes 
222         $local_file_header .= "\x00\x00";    // gen purpose bit flag                    // general purpose bit flag 2 bytes 
223         $local_file_header .= "\x08\x00";    // compression method                              // compression method 2 bytes 
224         $local_file_header .= "\x00\x00\x00\x00"; // last mod time and date     // last mod file time 2 bytes & last mod file date 2 bytes 
225         $local_file_header .= pack("V",$crc); // crc32                                                  // crc-32 4 bytes 
226         $local_file_header .= pack("V",$compressed_size); //compressed filesize                 // compressed size 4 bytes 
227         $local_file_header .= pack("V",$uncompressed_size); //uncompressed filesize             // uncompressed size 4 bytes 
228         $local_file_header .= pack("v", strlen($name) ); //length of filename  // file name length 2 bytes 
229         $local_file_header .= "\x00\x00"; //extra field length                          // extra field length 2 bytes 
230         $local_file_header .= $name;                                                                                    // file name (variable size)  & extra field (variable size) 
231                 /* end of local file header */
232           
233                 $this->files_data .= $local_file_header . $file_data; // . $data_descriptor;;
234
235                 /* create the central directory */
236                 $central_directory = '';
237                 if ($timestamp) {
238                         $v_date = getdate($timestamp);
239                 } else {
240                         $v_date = getdate();
241                 }
242                 $time = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
243                 $date = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
244
245         // now add to central directory record 
246         $central_directory = "\x50\x4b\x01\x02";                                                                                        // central file header signature 4 bytes (0x02014b50)
247         $central_directory .="\x14\x00";    // version made by                                                          // version made by 2 bytes 
248         $central_directory .="\x14\x00";    // version needed to extract                                        // version needed to extract 2 bytes 
249         $central_directory .="\x00\x00";    // gen purpose bit flag                                                     // general purpose bit flag 2 bytes 
250         $central_directory .="\x08\x00";    // compression method                                                       // compression method 2 bytes         
251         $central_directory .= pack("v",$time); // time                                                                          // last mod file time 2 bytes 
252                 $central_directory .= pack("v",$date); // date                                                                          // last mod file date 2 bytes 
253                 $central_directory .= pack("V",$crc); // crc32                                                                          // crc-32 4 bytes 
254         $central_directory .= pack("V",$compressed_size); //compressed filesize                                         // compressed size 4 bytes 
255         $central_directory .= pack("V",$uncompressed_size); //uncompressed filesize                                     // uncompressed size 4 bytes 
256         $central_directory .= pack("v", strlen($name) ); //length of filename                           // file name length 2 bytes 
257         $central_directory .= "\x00\x00"; //extra field length                                                  // extra field length 2 bytes 
258         $central_directory .= "\x00\x00"; //file comment length                                                 // file comment length 2 bytes 
259         $central_directory .= "\x00\x00"; //disk number start                                                   // disk number start 2 bytes 
260         $central_directory .= "\x00\x00"; //internal file attributes                                            // internal file attributes 2 bytes 
261         $central_directory .= pack("V", 32); //external file attributes - 'archive' bit set // external file attributes 4 bytes 
262                 $central_directory .= pack("V", $old_offset);
263
264         $central_directory .= $name;                                                                                                            // file name (variable size)
265
266                 $this->central_directory_headers .= $central_directory;
267         
268                 $this->num_entries++;
269     } 
270
271         /**
272         * Closes archive, sets $is_closed to true
273         * @access  public
274         * @param   none
275         * @author  Joel Kronenberg
276         */
277         function close() {
278                 $this->files_data .= $this->central_directory_headers . "\x50\x4b\x05\x06\x00\x00\x00\x00" .   
279             pack("v", $this->num_entries).     // total # of entries "on this disk" 
280             pack("v", $this->num_entries).     // total # of entries overall 
281             pack("V", strlen($this->central_directory_headers)).             // size of central dir 
282             pack("V", strlen($this->files_data)).                 // offset to start of central dir 
283             "\x00\x00"; 
284
285                 unset($this->central_directory_headers);
286                 unset($this->num_entries);
287
288                 $this->zip_file =& $this->files_data;
289                 $this->is_closed = true;
290         }
291
292     /**
293         * Gets size of new archive
294         * Only call this after calling close() - will return false if the zip wasn't close()d yet
295         * @access  public
296         * @return  int  size of file
297         * @author  Joel Kronenberg
298         */
299         function get_size() {
300                 if (!$this->is_closed) {
301                         return false;
302                 }
303                 return strlen($this->zip_file);
304         }
305
306
307     /**
308         * Returns binary file
309         * @access       public
310         * @see          get_size()              in include/classes/zipfile.class.php
311         * @author  Joel Kronenberg
312         */      
313         function get_file() {
314                 if (!$this->is_closed) {
315                         $this->close();
316                 }
317                 return $this->zip_file;
318     }
319
320         /**
321         * Writes the file to disk.
322         * Similar to get_file(), but instead of returning the file, it saves it to disk.
323         * @access  public
324         * @author  Joel Kronenberg
325         * @param  $file The full path and file name of the destination file.
326         */
327         function write_file($file) {
328                 if (!$this->is_closed) {
329                         $this->close();
330                 }
331                 if (function_exists('file_put_contents')) {
332                         file_put_contents($file, $this->zip_file);
333                 } else {
334                         $fp = fopen($file, 'wb+');
335                         fwrite($fp, $this->zip_file);
336                         fclose($fp);
337                 }
338         }
339
340
341     /**
342         * Outputs the file - sends headers to browser to force download
343         * Only call this after calling close() - will return false if the zip wasn't close()d yet
344         * @access       public
345         * @see          get_size()              in include/classes/zipfile.class.php
346         * @author  Joel Kronenberg
347         */
348         function send_file($file_name) {
349                 if (!$this->is_closed) {
350                         $this->close();
351                 }
352                 $file_name = str_replace(array('"', '<', '>', '|', '?', '*', ':', '/', '\\'), '', $file_name);
353
354                 header('Content-Type: application/x-zip');
355                 header('Content-transfer-encoding: binary'); 
356                 header('Content-Disposition: attachment; filename="'.htmlspecialchars($file_name).'.zip"');
357                 header('Expires: 0');
358                 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
359                 header('Pragma: public');
360                 header('Content-Length: '.$this->get_size());
361
362                 echo $this->get_file();
363
364                 exit;
365         }
366 }
367
368 ?>