OSDN Git Service

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