OSDN Git Service

BugTrack/2283 Fasten ls2 plugin listing on no-title or no-include
[pukiwiki/pukiwiki.git] / plugin / dump.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // dump.inc.php
4 // Copyright
5 //   2004-2017 PukiWiki Development Team
6 //   2004      teanan / Interfair Laboratory
7 // License: GPL v2 or (at your option) any later version
8 //
9 // Remote dump / restore plugin
10 // Originated as tarfile.inc.php by teanan / Interfair Laboratory 2004.
11
12 /////////////////////////////////////////////////
13 // User defines
14
15 // Allow using resture function
16 define('PLUGIN_DUMP_ALLOW_RESTORE', FALSE); // FALSE, TRUE
17
18 // ページ名をディレクトリ構造に変換する際の文字コード (for mbstring)
19 define('PLUGIN_DUMP_FILENAME_ENCORDING', 'SJIS');
20
21 // 最大アップロードサイズ
22 define('PLUGIN_DUMP_MAX_FILESIZE', 1024); // Kbyte
23
24 /////////////////////////////////////////////////
25 // Internal defines
26
27 // Action
28 define('PLUGIN_DUMP_DUMP',    'dump');    // Dump & download
29 define('PLUGIN_DUMP_RESTORE', 'restore'); // Upload & restore
30
31 global $_STORAGE;
32
33 // DATA_DIR (wiki/*.txt)
34 $_STORAGE['DATA_DIR']['add_filter']     = '^[0-9A-F]+\.txt';
35 $_STORAGE['DATA_DIR']['extract_filter'] = '^' . preg_quote(DATA_DIR, '/')   . '((?:[0-9A-F])+)(\.txt){0,1}';
36
37 // UPLOAD_DIR (attach/*)
38 $_STORAGE['UPLOAD_DIR']['add_filter']     = '^[0-9A-F_]+';
39 $_STORAGE['UPLOAD_DIR']['extract_filter'] = '^' . preg_quote(UPLOAD_DIR, '/') . '((?:[0-9A-F]{2})+)_((?:[0-9A-F])+)';
40
41 // BACKUP_DIR (backup/*.gz)
42 $_STORAGE['BACKUP_DIR']['add_filter']     = '^[0-9A-F]+\.gz';
43 $_STORAGE['BACKUP_DIR']['extract_filter'] =  '^' . preg_quote(BACKUP_DIR, '/') . '((?:[0-9A-F])+)(\.gz){0,1}';
44
45
46 /////////////////////////////////////////////////
47 // プラグイン本体
48 function plugin_dump_action()
49 {
50         global $vars;
51
52         if (PKWK_READONLY) die_message('PKWK_READONLY prohibits this');
53
54         $pass = isset($_POST['pass']) ? $_POST['pass'] : NULL;
55         $act  = isset($vars['act'])   ? $vars['act']   : NULL;
56
57         $body = '';
58
59         if ($pass !== NULL) {
60                 if (! pkwk_login($pass)) {
61                         $body = "<p><strong>パスワードが違います。</strong></p>\n";
62                 } else {
63                         switch($act){
64                         case PLUGIN_DUMP_DUMP:
65                                 $body = plugin_dump_download();
66                                 break;
67                         case PLUGIN_DUMP_RESTORE:
68                                 $retcode = plugin_dump_upload();
69                                 if ($retcode['code'] == TRUE) {
70                                         $msg = 'アップロードが完了しました';
71                                 } else {
72                                         $msg = 'アップロードに失敗しました';
73                                 }
74                                 $body .= $retcode['msg'];
75                                 return array('msg' => $msg, 'body' => $body);
76                                 break;
77                         }
78                 }
79         }
80
81         // 入力フォームを表示
82         $body .= plugin_dump_disp_form();
83
84         $msg = '';
85         if (PLUGIN_DUMP_ALLOW_RESTORE) {
86                 $msg = 'dump & restore';
87         } else {
88                 $msg = 'dump';
89         }
90
91         return array('msg' => $msg, 'body' => $body);
92 }
93
94 /////////////////////////////////////////////////
95 // ファイルのダウンロード
96 function plugin_dump_download()
97 {
98         global $vars, $_STORAGE;
99
100         // アーカイブの種類
101         $arc_kind = ($vars['pcmd'] == 'tar') ? 'tar' : 'tgz';
102
103         // ページ名に変換する
104         $namedecode = isset($vars['namedecode']) ? TRUE : FALSE;
105
106         // バックアップディレクトリ
107         $bk_wiki   = isset($vars['bk_wiki'])   ? TRUE : FALSE;
108         $bk_attach = isset($vars['bk_attach']) ? TRUE : FALSE;
109         $bk_backup = isset($vars['bk_backup']) ? TRUE : FALSE;
110
111         $filecount = 0;
112         $tar = new tarlib();
113         $tar->create(CACHE_DIR, $arc_kind) or
114                 die_message('テンポラリファイルの生成に失敗しました。');
115
116         if ($bk_wiki)   $filecount += $tar->add_dir(DATA_DIR,   $_STORAGE['DATA_DIR']['add_filter'],   $namedecode);
117         if ($bk_attach) $filecount += $tar->add_dir(UPLOAD_DIR, $_STORAGE['UPLOAD_DIR']['add_filter'], $namedecode);
118         if ($bk_backup) $filecount += $tar->add_dir(BACKUP_DIR, $_STORAGE['BACKUP_DIR']['add_filter'], $namedecode);
119
120         $tar->close();
121
122         if ($filecount === 0) {
123                 @unlink($tar->filename);
124                 return '<p><strong>ファイルがみつかりませんでした。</strong></p>';
125         } else {
126                 // ダウンロード
127                 download_tarfile($tar->filename, $arc_kind);
128                 @unlink($tar->filename);
129                 exit;   // 正常終了
130         }
131 }
132
133 /////////////////////////////////////////////////
134 // ファイルのアップロード
135 function plugin_dump_upload()
136 {
137         global $vars, $_STORAGE;
138
139         if (! PLUGIN_DUMP_ALLOW_RESTORE)
140                 return array('code' => FALSE , 'msg' => 'Restoring function is not allowed');
141
142         $filename = $_FILES['upload_file']['name'];
143         $matches  = array();
144         $arc_kind = FALSE;
145         if(! preg_match('/(\.tar|\.tar.gz|\.tgz)$/', $filename, $matches)){
146                 die_message('Invalid file suffix');
147         } else { 
148                 $matches[1] = strtolower($matches[1]);
149                 switch ($matches[1]) {
150                 case '.tar':    $arc_kind = 'tar'; break;
151                 case '.tgz':    $arc_kind = 'tar'; break;
152                 case '.tar.gz': $arc_kind = 'tgz'; break;
153                 default: die_message('Invalid file suffix: ' . $matches[1]); }
154         }
155
156         if ($_FILES['upload_file']['size'] >  PLUGIN_DUMP_MAX_FILESIZE * 1024)
157                 die_message('Max file size exceeded: ' . PLUGIN_DUMP_MAX_FILESIZE . 'KB');
158
159         // Create a temporary tar file
160         $uploadfile = tempnam(realpath(CACHE_DIR), 'tarlib_uploaded_');
161         $tar = new tarlib();
162         if(! move_uploaded_file($_FILES['upload_file']['tmp_name'], $uploadfile) ||
163            ! $tar->open($uploadfile, $arc_kind)) {
164                 @unlink($uploadfile);
165                 die_message('ファイルがみつかりませんでした。');
166         }
167
168         $pattern = "(({$_STORAGE['DATA_DIR']['extract_filter']})|" .
169                     "({$_STORAGE['UPLOAD_DIR']['extract_filter']})|" .
170                     "({$_STORAGE['BACKUP_DIR']['extract_filter']}))";
171         $files = $tar->extract($pattern);
172         if (empty($files)) {
173                 @unlink($uploadfile);
174                 return array('code' => FALSE, 'msg' => '<p>展開できるファイルがありませんでした。</p>');
175         }
176
177         $msg  = '<p><strong>展開したファイル一覧</strong><ul>';
178         foreach($files as $name) {
179                 $msg .= "<li>$name</li>\n";
180         }
181         $msg .= '</ul></p>';
182
183         $tar->close();
184         @unlink($uploadfile);
185
186         return array('code' => TRUE, 'msg' => $msg);
187 }
188
189 /////////////////////////////////////////////////
190 // tarファイルのダウンロード
191 function download_tarfile($tempnam, $arc_kind)
192 {
193         $size = filesize($tempnam);
194
195         $filename = strftime('tar%Y%m%d', time());
196         if ($arc_kind == 'tgz') {
197                 $filename .= '.tar.gz';
198         } else {
199                 $filename .= '.tar';
200         }
201
202         pkwk_common_headers();
203         header('Content-Disposition: attachment; filename="' . $filename . '"');
204         header('Content-Length: ' . $size);
205         header('Content-Type: application/octet-stream');
206         header('Pragma: no-cache');
207         // Disable output bufferring
208         while (ob_get_level()) {
209                 ob_end_flush();
210         }
211         flush();
212         @readfile($tempnam);
213 }
214
215 /////////////////////////////////////////////////
216 // 入力フォームを表示
217 function plugin_dump_disp_form()
218 {
219         global $defaultpage;
220
221         $script   = get_base_uri();
222         $act_down = PLUGIN_DUMP_DUMP;
223         $act_up   = PLUGIN_DUMP_RESTORE;
224         $maxsize  = PLUGIN_DUMP_MAX_FILESIZE;
225
226         $data = <<<EOD
227 <span class="small">
228 </span>
229 <h3>データのダウンロード</h3>
230 <form action="$script" method="post">
231  <div>
232   <input type="hidden" name="cmd"  value="dump" />
233   <input type="hidden" name="page" value="$defaultpage" />
234   <input type="hidden" name="act"  value="$act_down" />
235
236 <p><strong>アーカイブの形式</strong>
237 <br />
238   <input type="radio" name="pcmd" id="_p_dump_tgz" value="tgz" checked="checked" />
239   <label for="_p_dump_tgz"> ~.tar.gz 形式</label><br />
240   <input type="radio" name="pcmd" id="_p_dump_tar" value="tar" />
241   <label for="_p_dump_tar">~.tar 形式</label>
242 </p>
243 <p><strong>バックアップディレクトリ</strong>
244 <br />
245   <input type="checkbox" name="bk_wiki" id="_p_dump_d_wiki" checked="checked" />
246   <label for="_p_dump_d_wiki">wiki</label><br />
247   <input type="checkbox" name="bk_attach" id="_p_dump_d_attach" />
248   <label for="_p_dump_d_attach">attach</label><br />
249   <input type="checkbox" name="bk_backup" id="_p_dump_d_backup" />
250   <label for="_p_dump_d_backup">backup</label><br />
251 </p>
252 <p><strong>オプション</strong>
253 <br />
254   <input type="checkbox" name="namedecode" id="_p_dump_namedecode" />
255   <label for="_p_dump_namedecode">エンコードされているページ名をディレクトリ階層つきのファイルにデコード
256   (※リストアに使うことはできなくなります。また、一部の文字は '_' に置換されます)</label><br />
257 </p>
258 <p><label for="_p_dump_adminpass_dump"><strong>管理者パスワード</strong></label>
259   <input type="password" name="pass" id="_p_dump_adminpass_dump" size="12" />
260   <input type="submit"   name="ok"   value="OK" />
261 </p>
262  </div>
263 </form>
264 EOD;
265
266         if(PLUGIN_DUMP_ALLOW_RESTORE) {
267                 $data .= <<<EOD
268 <h3>データのリストア (*.tar, *.tar.gz)</h3>
269 <form enctype="multipart/form-data" action="$script" method="post">
270  <div>
271   <input type="hidden" name="cmd"  value="dump" />
272   <input type="hidden" name="page" value="$defaultpage" />
273   <input type="hidden" name="act"  value="$act_up" />
274 <p><strong>[重要] 同じ名前のデータファイルは上書きされますので、十分ご注意ください。</strong></p>
275 <p><span class="small">
276 アップロード可能な最大ファイルサイズは、$maxsize KByte までです。<br />
277 </span>
278   <label for="_p_dump_upload_file">ファイル:</label>
279   <input type="file" name="upload_file" id="_p_dump_upload_file" size="40" />
280 </p>
281 <p><label for="_p_dump_adminpass_restore"><strong>管理者パスワード</strong></label>
282   <input type="password" name="pass" id="_p_dump_adminpass_restore" size="12" />
283   <input type="submit"   name="ok"   value="OK" />
284 </p>
285  </div>
286 </form>
287 EOD;
288         }
289
290         return $data;
291 }
292
293 /////////////////////////////////////////////////
294 // tarlib: a class library for tar file creation and expansion
295
296 // Tar related definition
297 define('TARLIB_HDR_LEN',           512);        // ヘッダの大きさ
298 define('TARLIB_BLK_LEN',           512);        // 単位ブロック長さ
299 define('TARLIB_HDR_NAME_OFFSET',     0);        // ファイル名のオフセット
300 define('TARLIB_HDR_NAME_LEN',      100);        // ファイル名の最大長さ
301 define('TARLIB_HDR_MODE_OFFSET',   100);        // modeへのオフセット
302 define('TARLIB_HDR_UID_OFFSET',    108);        // uidへのオフセット
303 define('TARLIB_HDR_GID_OFFSET',    116);        // gidへのオフセット
304 define('TARLIB_HDR_SIZE_OFFSET',   124);        // サイズへのオフセット
305 define('TARLIB_HDR_SIZE_LEN',       12);        // サイズの長さ
306 define('TARLIB_HDR_MTIME_OFFSET',  136);        // 最終更新時刻のオフセット
307 define('TARLIB_HDR_MTIME_LEN',      12);        // 最終更新時刻の長さ
308 define('TARLIB_HDR_CHKSUM_OFFSET', 148);        // チェックサムのオフセット
309 define('TARLIB_HDR_CHKSUM_LEN',      8);        // チェックサムの長さ
310 define('TARLIB_HDR_TYPE_OFFSET',   156);        // ファイルタイプへのオフセット
311
312 // Status
313 define('TARLIB_STATUS_INIT',    0);             // 初期状態
314 define('TARLIB_STATUS_OPEN',   10);             // 読み取り
315 define('TARLIB_STATUS_CREATE', 20);             // 書き込み
316
317 define('TARLIB_DATA_MODE',      '100666 ');     // ファイルパーミッション
318 define('TARLIB_DATA_UGID',      '000000 ');     // uid / gid
319 define('TARLIB_DATA_CHKBLANKS', '        ');
320
321 // GNU拡張仕様(ロングファイル名対応)
322 define('TARLIB_DATA_LONGLINK', '././@LongLink');
323
324 // Type flag
325 define('TARLIB_HDR_FILE', '0');
326 define('TARLIB_HDR_LINK', 'L');
327
328 // Kind of the archive
329 define('TARLIB_KIND_TGZ', 0);
330 define('TARLIB_KIND_TAR', 1);
331
332 class tarlib
333 {
334         var $filename;
335         var $fp;
336         var $status;
337         var $arc_kind;
338         var $dummydata;
339
340         function tarlib() {
341                 $this->__construct();
342         }
343         function __construct() {
344                 $this->filename = '';
345                 $this->fp       = FALSE;
346                 $this->status   = TARLIB_STATUS_INIT;
347                 $this->arc_kind = TARLIB_KIND_TGZ;
348         }
349         
350         ////////////////////////////////////////////////////////////
351         // 関数  : tarファイルを作成する
352         // 引数  : tarファイルを作成するパス
353         // 返り値: TRUE .. 成功 , FALSE .. 失敗
354         ////////////////////////////////////////////////////////////
355         function create($tempdir, $kind = 'tgz')
356         {
357                 $tempnam = tempnam(realpath($tempdir), 'tarlib_create_');
358                 if ($tempnam === FALSE) return FALSE;
359
360                 if ($kind == 'tgz') {
361                         $this->arc_kind = TARLIB_KIND_TGZ;
362                         $this->fp       = gzopen($tempnam, 'wb');
363                 } else {
364                         $this->arc_kind = TARLIB_KIND_TAR;
365                         $this->fp       = @fopen($tempnam, 'wb');
366                 }
367
368                 if ($this->fp === FALSE) {
369                         @unlink($tempnam);
370                         return FALSE;
371                 } else {
372                         $this->filename  = $tempnam;
373                         $this->dummydata = join('', array_fill(0, TARLIB_BLK_LEN, "\0"));
374                         $this->status    = TARLIB_STATUS_CREATE;
375                         rewind($this->fp);
376                         return TRUE;
377                 }
378         }
379
380         ////////////////////////////////////////////////////////////
381         // 関数  : tarファイルにディレクトリを追加する
382         // 引数  : $dir    .. ディレクトリ名
383         //         $mask   .. 追加するファイル(正規表現)
384         //         $decode .. ページ名の変換をするか
385         // 返り値: 作成したファイル数
386         ////////////////////////////////////////////////////////////
387         function add_dir($dir, $mask, $decode = FALSE)
388         {
389                 $retvalue = 0;
390                 
391                 if ($this->status != TARLIB_STATUS_CREATE)
392                         return ''; // File is not created
393
394                 unset($files);
395
396                 //  指定されたパスのファイルのリストを取得する
397                 $dp = @opendir($dir);
398                 if($dp === FALSE) {
399                         @unlink($this->filename);
400                         die_message($dir . ' is not found or not readable.');
401                 }
402                 while (($filename = readdir($dp)) !== FALSE) {
403                         if (preg_match('/' . $mask . '/', $filename)) {
404                                 $files[] = $dir . $filename;
405                         }
406                 }
407                 closedir($dp);
408                 
409                 sort($files, SORT_STRING);
410
411                 $matches = array();
412                 foreach($files as $name)
413                 {
414                         // Tarに格納するファイル名をdecode
415                         if ($decode === FALSE) {
416                                 $filename = $name;
417                         } else {
418                                 $dirname  = dirname(trim($name)) . '/';
419                                 $filename = basename(trim($name));
420                                 if (preg_match("/^((?:[0-9A-F]{2})+)_((?:[0-9A-F]{2})+)/", $filename, $matches)) {
421                                         // attachファイル名
422                                         $filename = decode($matches[1]) . '/' . decode($matches[2]);
423                                 } else {
424                                         $pattern = '^((?:[0-9A-F]{2})+)((\.txt|\.gz)*)$';
425                                         if (preg_match("/$pattern/", $filename, $matches)) {
426                                                 $filename = decode($matches[1]) . $matches[2];
427
428                                                 // 危ないコードは置換しておく
429                                                 $filename = str_replace(':',  '_', $filename);
430                                                 $filename = str_replace('\\', '_', $filename);
431                                         }
432                                 }
433                                 $filename = $dirname . $filename;
434                                 // ファイル名の文字コードを変換
435                                 if (function_exists('mb_convert_encoding'))
436                                         $filename = mb_convert_encoding($filename, PLUGIN_DUMP_FILENAME_ENCORDING);
437                         }
438
439                         // 最終更新時刻
440                         $mtime = filemtime($name);
441
442                         // ファイル名長のチェック
443                         if (strlen($filename) > TARLIB_HDR_NAME_LEN) {
444                                 // LongLink対応
445                                 $size = strlen($filename);
446                                 // LonkLinkヘッダ生成
447                                 $tar_data = $this->_make_header(TARLIB_DATA_LONGLINK, $size, $mtime, TARLIB_HDR_LINK);
448                                 // ファイル出力
449                                 $this->_write_data(join('', $tar_data), $filename, $size);
450                         }
451
452                         // ファイルサイズを取得
453                         $size = filesize($name);
454                         if ($size === FALSE) {
455                                 @unlink($this->filename);
456                                 die_message($name . ' is not found or not readable.');
457                         }
458
459                         // ヘッダ生成
460                         $tar_data = $this->_make_header($filename, $size, $mtime, TARLIB_HDR_FILE);
461
462                         // ファイルデータの取得
463                         $fpr = @fopen($name , 'rb');
464                         flock($fpr, LOCK_SH);
465                         $data = fread($fpr, $size);
466                         flock($fpr, LOCK_UN);
467                         fclose( $fpr );
468
469                         // ファイル出力
470                         $this->_write_data(join('', $tar_data), $data, $size);
471                         ++$retvalue;
472                 }
473                 return $retvalue;
474         }
475         
476         ////////////////////////////////////////////////////////////
477         // 関数  : tarのヘッダ情報を生成する (add)
478         // 引数  : $filename .. ファイル名
479         //         $size     .. データサイズ
480         //         $mtime    .. 最終更新日
481         //         $typeflag .. TypeFlag (file/link)
482         // 戻り値: tarヘッダ情報
483         ////////////////////////////////////////////////////////////
484         function _make_header($filename, $size, $mtime, $typeflag)
485         {
486                 $tar_data = array_fill(0, TARLIB_HDR_LEN, "\0");
487                 
488                 // ファイル名を保存
489                 for($i = 0; $i < strlen($filename); $i++ ) {
490                         if ($i < TARLIB_HDR_NAME_LEN) {
491                                 $tar_data[$i + TARLIB_HDR_NAME_OFFSET] = $filename{$i};
492                         } else {
493                                 break;  // ファイル名が長すぎ
494                         }
495                 }
496
497                 // mode
498                 $modeid = TARLIB_DATA_MODE;
499                 for($i = 0; $i < strlen($modeid); $i++ ) {
500                         $tar_data[$i + TARLIB_HDR_MODE_OFFSET] = $modeid{$i};
501                 }
502
503                 // uid / gid
504                 $ugid = TARLIB_DATA_UGID;
505                 for($i = 0; $i < strlen($ugid); $i++ ) {
506                         $tar_data[$i + TARLIB_HDR_UID_OFFSET] = $ugid{$i};
507                         $tar_data[$i + TARLIB_HDR_GID_OFFSET] = $ugid{$i};
508                 }
509
510                 // サイズ
511                 $strsize = sprintf('%11o', $size);
512                 for($i = 0; $i < strlen($strsize); $i++ ) {
513                         $tar_data[$i + TARLIB_HDR_SIZE_OFFSET] = $strsize{$i};
514                 }
515
516                 // 最終更新時刻
517                 $strmtime = sprintf('%o', $mtime);
518                 for($i = 0; $i < strlen($strmtime); $i++ ) {
519                         $tar_data[$i + TARLIB_HDR_MTIME_OFFSET] = $strmtime{$i};
520                 }
521
522                 // チェックサム計算用のブランクを設定
523                 $chkblanks = TARLIB_DATA_CHKBLANKS;
524                 for($i = 0; $i < strlen($chkblanks); $i++ ) {
525                         $tar_data[$i + TARLIB_HDR_CHKSUM_OFFSET] = $chkblanks{$i};
526                 }
527
528                 // タイプフラグ
529                 $tar_data[TARLIB_HDR_TYPE_OFFSET] = $typeflag;
530
531                 // チェックサムの計算
532                 $sum = 0;
533                 for($i = 0; $i < TARLIB_BLK_LEN; $i++ ) {
534                         $sum += 0xff & ord($tar_data[$i]);
535                 }
536                 $strchksum = sprintf('%7o',$sum);
537                 for($i = 0; $i < strlen($strchksum); $i++ ) {
538                         $tar_data[$i + TARLIB_HDR_CHKSUM_OFFSET] = $strchksum{$i};
539                 }
540
541                 return $tar_data;
542         }
543         
544         ////////////////////////////////////////////////////////////
545         // 関数  : tarデータのファイル出力 (add)
546         // 引数  : $header .. tarヘッダ情報
547         //         $body   .. tarデータ
548         //         $size   .. データサイズ
549         // 戻り値: なし
550         ////////////////////////////////////////////////////////////
551         function _write_data($header, $body, $size)
552         {
553                 $fixsize  = ceil($size / TARLIB_BLK_LEN) * TARLIB_BLK_LEN - $size;
554
555                 if ($this->arc_kind == TARLIB_KIND_TGZ) {
556                         gzwrite($this->fp, $header, TARLIB_HDR_LEN);    // Header
557                         gzwrite($this->fp, $body, $size);               // Body
558                         gzwrite($this->fp, $this->dummydata, $fixsize); // Padding
559                 } else {
560                          fwrite($this->fp, $header, TARLIB_HDR_LEN);    // Header
561                          fwrite($this->fp, $body, $size);               // Body
562                          fwrite($this->fp, $this->dummydata, $fixsize); // Padding
563                 }
564         }
565
566         ////////////////////////////////////////////////////////////
567         // 関数  : tarファイルを開く
568         // 引数  : tarファイル名
569         // 返り値: TRUE .. 成功 , FALSE .. 失敗
570         ////////////////////////////////////////////////////////////
571         function open($name = '', $kind = 'tgz')
572         {
573                 if (! PLUGIN_DUMP_ALLOW_RESTORE) return FALSE; // Not allowed
574
575                 if ($name != '') $this->filename = $name;
576
577                 if ($kind == 'tgz') {
578                         $this->arc_kind = TARLIB_KIND_TGZ;
579                         $this->fp = gzopen($this->filename, 'rb');
580                 } else {
581                         $this->arc_kind = TARLIB_KIND_TAR;
582                         $this->fp =  fopen($this->filename, 'rb');
583                 }
584
585                 if ($this->fp === FALSE) {
586                         return FALSE;   // No such file
587                 } else {
588                         $this->status = TARLIB_STATUS_OPEN;
589                         rewind($this->fp);
590                         return TRUE;
591                 }
592         }
593
594         ////////////////////////////////////////////////////////////
595         // 関数  : 指定したディレクトリにtarファイルを展開する
596         // 引数  : 展開するファイルパターン(正規表現)
597         // 返り値: 展開したファイル名の一覧
598         // 補足  : ARAIさんのattachプラグインパッチを参考にしました
599         ////////////////////////////////////////////////////////////
600         function extract($pattern)
601         {
602                 if ($this->status != TARLIB_STATUS_OPEN) return ''; // Not opened
603                 
604                 $files = array();
605                 $longname = '';
606
607                 while(1) {
608                         $buff = fread($this->fp, TARLIB_HDR_LEN);
609                         if (strlen($buff) != TARLIB_HDR_LEN) break;
610
611                         // ファイル名
612                         $name = '';
613                         if ($longname != '') {
614                                 $name     = $longname;  // LongLink対応
615                                 $longname = '';
616                         } else {
617                                 for ($i = 0; $i < TARLIB_HDR_NAME_LEN; $i++ ) {
618                                         if ($buff{$i + TARLIB_HDR_NAME_OFFSET} != "\0") {
619                                                 $name .= $buff{$i + TARLIB_HDR_NAME_OFFSET};
620                                         } else {
621                                                 break;
622                                         }
623                                 }
624                         }
625                         $name = trim($name);
626
627                         if ($name == '') break; // 展開終了
628
629                         // チェックサムを取得しつつ、ブランクに置換していく
630                         $checksum = '';
631                         $chkblanks = TARLIB_DATA_CHKBLANKS;
632                         for ($i = 0; $i < TARLIB_HDR_CHKSUM_LEN; $i++ ) {
633                                 $checksum .= $buff{$i + TARLIB_HDR_CHKSUM_OFFSET};
634                                 $buff{$i + TARLIB_HDR_CHKSUM_OFFSET} = $chkblanks{$i};
635                         }
636                         list($checksum) = sscanf('0' . trim($checksum), '%i');
637
638                         // Compute checksum
639                         $sum = 0;
640                         for($i = 0; $i < TARLIB_BLK_LEN; $i++ ) {
641                                 $sum += 0xff & ord($buff{$i});
642                         }
643                         if ($sum != $checksum) break; // Error
644                                 
645                         // Size
646                         $size = '';
647                         for ($i = 0; $i < TARLIB_HDR_SIZE_LEN; $i++ ) {
648                                 $size .= $buff{$i + TARLIB_HDR_SIZE_OFFSET};
649                         }
650                         list($size) = sscanf('0' . trim($size), '%i');
651
652                         // ceil
653                         // データブロックは512byteでパディングされている
654                         $pdsz = ceil($size / TARLIB_BLK_LEN) * TARLIB_BLK_LEN;
655
656                         // 最終更新時刻
657                         $strmtime = '';
658                         for ($i = 0; $i < TARLIB_HDR_MTIME_LEN; $i++ ) {
659                                 $strmtime .= $buff{$i + TARLIB_HDR_MTIME_OFFSET};
660                         }
661                         list($mtime) = sscanf('0' . trim($strmtime), '%i');
662
663                         // タイプフラグ
664 //                       $type = $buff{TARLIB_HDR_TYPE_OFFSET};
665
666                         if ($name == TARLIB_DATA_LONGLINK) {
667                                 // LongLink
668                                 $buff     = fread($this->fp, $pdsz);
669                                 $longname = substr($buff, 0, $size);
670                         } else if (preg_match("/$pattern/", $name) ) {
671 //                      } else if ($type == 0 && preg_match("/$pattern/", $name) ) {
672                                 $buff = fread($this->fp, $pdsz);
673
674                                 // 既に同じファイルがある場合は上書きされる
675                                 $fpw = @fopen($name, 'wb');
676                                 if ($fpw !== FALSE) {
677                                         flock($fpw, LOCK_EX);
678                                         fwrite($fpw, $buff, $size);
679                                         @chmod($name, 0666);
680                                         @touch($name, $mtime);
681                                         flock($fpw, LOCK_UN);
682
683                                         fclose($fpw);
684                                         $files[] = $name;
685                                 }
686                         } else {
687                                 // ファイルポインタを進める
688                                 @fseek($this->fp, $pdsz, SEEK_CUR);
689                         }
690                 }
691                 return $files;
692         }
693
694         ////////////////////////////////////////////////////////////
695         // 関数  : tarファイルを閉じる
696         // 引数  : なし
697         // 返り値: なし
698         ////////////////////////////////////////////////////////////
699         function close()
700         {
701                 if ($this->status == TARLIB_STATUS_CREATE) {
702                         // ファイルを閉じる
703                         if ($this->arc_kind == TARLIB_KIND_TGZ) {
704                                 // バイナリーゼロを1024バイト出力
705                                 gzwrite($this->fp, $this->dummydata, TARLIB_HDR_LEN);
706                                 gzwrite($this->fp, $this->dummydata, TARLIB_HDR_LEN);
707                                 gzclose($this->fp);
708                         } else {
709                                 // バイナリーゼロを1024バイト出力
710                                 fwrite($this->fp, $this->dummydata, TARLIB_HDR_LEN);
711                                 fwrite($this->fp, $this->dummydata, TARLIB_HDR_LEN);
712                                 fclose($this->fp);
713                         }
714                 } else if ($this->status == TARLIB_STATUS_OPEN) {
715                         if ($this->arc_kind == TARLIB_KIND_TGZ) {
716                                 gzclose($this->fp);
717                         } else {
718                                  fclose($this->fp);
719                         }
720                 }
721
722                 $this->status = TARLIB_STATUS_INIT;
723         }
724 }