2 /*--------------------------------------------------
3 | TAR/GZIP/BZIP2/ZIP ARCHIVE CLASSES 2.1
5 | Copyright (c) 2005 Devin Doucette
6 | Email: darksnoopy@shaw.ca
7 +--------------------------------------------------
8 | Email bugs/suggestions to darksnoopy@shaw.ca
9 +--------------------------------------------------
10 | This script has been created and released under
11 | the GNU GPL and is free to use and redistribute
12 | only if this copyright statement is not removed
13 +--------------------------------------------------
14 | FrontAccounting bugfixes/changes added.
15 +--------------------------------------------------*/
19 function __construct($name)
21 $this->options = array (
36 $this->files = array ();
37 $this->exclude = array ();
38 $this->storeonly = array ();
39 $this->error = array ();
42 function set_options($options)
44 foreach ($options as $key => $value)
45 $this->options[$key] = $value;
46 if (!empty ($this->options['basedir']))
48 $this->options['basedir'] = str_replace("\\", "/", $this->options['basedir']);
49 $this->options['basedir'] = preg_replace("/\/+/", "/", $this->options['basedir']);
50 $this->options['basedir'] = preg_replace("/\/$/", "", $this->options['basedir']);
52 if (!empty ($this->options['name']))
54 $this->options['name'] = str_replace("\\", "/", $this->options['name']);
55 // $this->options['name'] = preg_replace("/\/+/", "/", $this->options['name']);
57 if (!empty ($this->options['prepend']))
59 $this->options['prepend'] = str_replace("\\", "/", $this->options['prepend']);
60 $this->options['prepend'] = preg_replace("/^(\.*\/+)+/", "", $this->options['prepend']);
61 $this->options['prepend'] = preg_replace("/\/+/", "/", $this->options['prepend']);
62 $this->options['prepend'] = preg_replace("/\/$/", "", $this->options['prepend']) . "/";
66 function create_archive()
70 if ($this->options['inmemory'] == 0)
72 if ($this->options['overwrite'] == 0 &&
73 file_exists($this->options['name']
74 . ($this->options['type'] == "pkg" || $this->options['type'] == "gzip" ||$this->options['type'] == "bzip" ? ".tmp" : "")))
76 $this->error[] = "File {$this->options['name']} already exists.";
79 else if (!($this->archive = @fopen($this->options['name'].".tmp", "wb+")))
81 $this->error[] = "Could not open {$this->options['name']} for writing.".':'.getcwd();
88 if (!$this->create_tar())
90 $this->error[] = "Could not create package file.";
93 if (!$this->create_pkg())
95 $this->error[] = "Could not compress package file.";
99 if ($this->options['inmemory'] == 0)
101 fclose($this->archive);
102 unlink($this->options['name'] . ".tmp");
106 function add_data($data)
108 if ($this->options['inmemory'] == 0)
109 fwrite($this->archive, $data);
111 $this->archive .= $data;
116 if (!empty ($this->exclude)) {
117 foreach ($this->files as $key => $value)
118 foreach ($this->exclude as $current)
119 if ($value['name'] == $current['name'])
120 unset ($this->files[$key]);
122 if (!empty ($this->storeonly))
123 foreach ($this->files as $key => $value)
124 foreach ($this->storeonly as $current)
125 if ($value['name'] == $current['name'])
126 $this->files[$key]['method'] = 0;
127 unset ($this->exclude, $this->storeonly);
130 function add_files($list)
132 $temp = $this->list_files($list);
133 foreach ($temp as $current)
134 $this->files[] = $current;
137 function exclude_files($list)
139 $temp = $this->list_files($list);
140 foreach ($temp as $current)
141 $this->exclude[] = $current;
144 function store_files($list)
146 $temp = $this->list_files($list);
147 foreach ($temp as $current)
148 $this->storeonly[] = $current;
151 function list_files($list)
153 if (!is_array ($list))
156 $list = array ($temp);
163 chdir($this->options['basedir']);
165 foreach ($list as $current)
167 $current = str_replace("\\", "/", $current);
168 $current = preg_replace("/\/+/", "/", $current);
169 $current = preg_replace("/\/$/", "", $current);
170 if (strstr($current, "*"))
172 $regex = preg_replace("/([\\\^\$\.\[\]\|\(\)\?\+\{\}\/])/", "\\\\\\1", $current);
173 $regex = str_replace("*", ".*", $regex);
174 $dir = strstr($current, "/") ? substr($current, 0, strrpos($current, "/")) : ".";
175 $temp = $this->parse_dir($dir);
176 foreach ($temp as $current2)
177 if (preg_match("/^{$regex}$/i", $current2['name']))
178 $files[] = $current2;
179 unset ($regex, $dir, $temp, $current);
181 else if (@is_dir($current))
183 $temp = $this->parse_dir($current);
184 foreach ($temp as $file)
186 unset ($temp, $file);
188 else if (@file_exists($current))
189 $files[] = array ('name' => $current, 'name2' => $this->options['prepend'] .
190 preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($current, "/")) ?
191 substr($current, strrpos($current, "/") + 1) : $current),
192 'type' => @is_link($current) && $this->options['followlinks'] == 0 ? 2 : 0,
193 'ext' => substr($current, strrpos($current, ".")), 'stat' => stat($current));
198 unset ($current, $pwd);
202 function parse_dir($dirname)
204 if ($this->options['storepaths'] == 1 && !preg_match("/^(\.+\/*)+$/", $dirname))
205 $files = array (array ('name' => $dirname, 'name2' => $this->options['prepend'] .
206 preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($dirname, "/")) ?
207 substr($dirname, strrpos($dirname, "/") + 1) : $dirname), 'type' => 5, 'stat' => stat($dirname)));
210 $dir = @opendir($dirname);
212 while ($file = @readdir($dir))
214 $fullname = $dirname . "/" . $file;
215 if ($file == "." || $file == "..")
217 else if (@is_dir($fullname))
219 if (empty ($this->options['recurse']))
221 $temp = $this->parse_dir($fullname);
222 foreach ($temp as $file2)
225 else if (@file_exists($fullname))
226 $files[] = array ('name' => $fullname, 'name2' => $this->options['prepend'] .
227 preg_replace("/(\.+\/+)+/", "", ($this->options['storepaths'] == 0 && strstr($fullname, "/")) ?
228 substr($fullname, strrpos($fullname, "/") + 1) : $fullname),
229 'type' => @is_link($fullname) && $this->options['followlinks'] == 0 ? 2 : 0,
230 'ext' => substr($file, strrpos($file, ".")), 'stat' => stat($fullname));
239 class tar_file extends archive
241 function __construct($name)
243 parent::__construct($name);
244 $this->options['type'] = "tar";
247 function create_tar()
250 chdir($this->options['basedir']);
251 $files = $this->files;
252 foreach ($files as $current)
254 if ($current['name'] == $this->options['name'])
256 if (strlen($current['name2']) > 99)
258 $path = substr($current['name2'], 0, strpos($current['name2'], "/", strlen($current['name2']) - 100) + 1);
259 $current['name2'] = substr($current['name2'], strlen($path));
260 if (strlen($path) > 154 || strlen($current['name2']) > 99)
262 $this->error[] = "Could not add {$path}{$current['name2']} to archive because the filename is too long.";
266 $block = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", $current['name2'], sprintf("%07o",
267 $current['stat'][2]), sprintf("%07o", $current['stat'][4]), sprintf("%07o", $current['stat'][5]),
268 sprintf("%011o", $current['type'] == 2 ? 0 : $current['stat'][7]), sprintf("%011o", $current['stat'][9]),
269 " ", $current['type'], $current['type'] == 2 ? @readlink($current['name']) : "", "ustar ", " ",
270 "Unknown", "Unknown", "", "", !empty ($path) ? $path : "", "");
273 for ($i = 0; $i < 512; $i++)
274 $checksum += ord(substr($block, $i, 1));
275 $checksum = pack("a8", sprintf("%07o", $checksum));
276 $block = substr_replace($block, $checksum, 148, 8);
278 if ($current['type'] == 2 || $current['stat'][7] == 0)
279 { $this->add_data($block);
281 else if ($fp = @fopen($current['name'], "rb"))
283 $this->add_data($block);
285 while ($temp = fread($fp, 1048576)) {
286 $this->add_data($temp);
288 if ($current['stat'][7] % 512 > 0)
291 for ($i = 0; $i < 512 - $current['stat'][7] % 512; $i++)
293 $this->add_data($temp);
298 $this->error[] = "Could not open file {$current['name']} for reading. It was not added.";
301 $this->add_data(pack("a1024", ""));
308 Extract files to base directory.
309 When $dry_run!=0 no file is written,
310 but list of existing files to be written is returned.
312 function extract_files($dry_run = false)
315 if ($fp = $this->open_archive())
318 // display_error($pwd.':'.$this->options['basedir']);
319 chdir($this->options['basedir']);
320 if ($this->options['inmemory'] == 1)
321 $this->files = array();
323 while ($block = fread($fp, 512))
325 $temp = unpack("a100name/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1type/a100symlink/a6magic/a2temp/a32temp/a32temp/a8temp/a8temp/a155prefix/a12temp", $block);
327 'name' => rtrim($temp['prefix']) . rtrim($temp['name']),
329 2 => octdec($temp['mode']),
330 4 => octdec($temp['uid']),
331 5 => octdec($temp['gid']),
332 7 => octdec($temp['size']),
333 9 => octdec($temp['mtime']),
335 'checksum' => octdec($temp['checksum']),
336 'type' => $temp['type'],
337 'magic' => $temp['magic'],
339 if ($file['checksum'] == 0x00000000)
341 else if (substr($file['magic'], 0, 5) != "ustar")
343 $this->error[] = "This script does not support extracting this type of tar file.";
346 $block = substr_replace($block, " ", 148, 8);
348 for ($i = 0; $i < 512; $i++)
349 $checksum += ord(substr($block, $i, 1));
350 if ($file['checksum'] != $checksum) {
351 $this->error[] = "Could not extract from {$this->options['name']}, it is corrupt.";
354 if ($dry_run || $this->options['inmemory'] == 1)
356 if ($file['type'] == 0) {
358 $tst = check_write($file['name']);
360 $this->error[] = "Could not open {$this->options['basedir']}/{$file['name']} for writing.";
363 $flist[] = $file['name'];
364 if ( $tst < 0 && $this->options['overwrite'] == 0)
365 $this->error[] = "{$file['name']} already exists.";
367 fseek($fp, ($file['stat'][7] + 511) & ~511, SEEK_CUR); // skip data
369 $dat = fread($fp, $file['stat'][7]);
370 $tail = $file['stat'][7] % 512;
372 fread($fp, 512 - $tail);
373 $file['data'] = $dat;
376 unset ($file['checksum'], $file['magic']);
377 $this->files[] = $file;
379 else if ($file['type'] == 5)
381 if (!is_dir($file['name'])) {
382 mkdir($file['name'], $file['stat'][2]);
385 else if ($this->options['overwrite'] == 0 && file_exists($file['name']))
387 $this->error[] = "{$file['name']} already exists.";
390 else if ($file['type'] == 2)
392 symlink($temp['symlink'], $file['name']);
393 @chmod($file['name'], $file['stat'][2]);
395 else if ($new = @fopen($file['name'], "wb"))
397 fwrite($new, fread($fp, $file['stat'][7]));
398 if ($file['stat'][7] % 512)
399 fread($fp, 512 - $file['stat'][7] % 512);
401 @chmod($file['name'], $file['stat'][2]);
405 $this->error[] = "Could not open {$file['name']} for writing.";
408 // @chown($file['name'], $file['stat'][4]);
409 // @chgrp($file['name'], $file['stat'][5]);
411 @touch($file['name'], $file['stat'][9]);
418 $this->error[] = "Could not open file {$this->options['name']}";
423 function open_archive()
425 return @fopen($this->options['name'], "rb");
429 class gzip_file extends tar_file
431 function __construct($name)
433 parent::__construct($name);
434 $this->options['type'] = "gzip";
437 function create_gzip()
439 if ($this->options['inmemory'] == 0)
441 if ($fp = gzopen($this->options['name'], "wb{$this->options['level']}"))
443 fseek($this->archive, 0);
444 while ($temp = fread($this->archive, 1048576))
450 $this->error[] = "Could not open {$this->options['name']} for writing.";
455 $this->archive = gzencode($this->archive, $this->options['level']);
460 function open_archive()
462 return @gzopen($this->options['name'], "rb");