OSDN Git Service

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