2 ////////////////////////////////////
4 ////////////////////////////////////
6 ini_set('display_errors', 1);
7 require_once __DIR__ . '/common.php';
8 require_once __DIR__ . '/access_ctrl.php';
9 require_once __DIR__ . '/calc_trip.php';
10 require_once __DIR__ . '/read_catalog.php';
12 header("Content-type: application/json; charset=utf-8");
16 $title = isset($_POST['title']) ? $_POST['title'] : '';
17 $author = isset($_POST['author']) ? $_POST['author'] : '';
18 $delkey = isset($_POST['delkey']) ? $_POST['delkey'] : '';
19 $license = isset($_POST['license']) ? $_POST['license'] : '';
20 $comment = isset($_POST['comment']) ? $_POST['comment'] : '';
21 $validation = isset($_POST['validation']) ? ($_POST['validation'] == 'true') : false;
23 $result = ['result' => 'error'];
25 // フォームの入力データが大きすぎればエラーとして何もしない。
26 if (strlen($title) > 5000 || strlen($author) > 500 ||
27 strlen($delkey) > 500 || strlen($license) > 500 ||
28 strlen($comment) > 20000) {
29 echo json_encode($result);
33 $hostaddr = get_hostaddr();
35 // 一時ファイルができているか(アップロードされているか)チェック
36 if (isset($_FILES['file'])) {
37 $file = $_FILES['file'];
38 if ($file['error'] == 0) {
39 $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
40 $fsize = $file['size'];
41 if (($ext != 'zip' && $ext != 'cmj') || $fsize < 1000) {
42 // 拡張子がzip, cmjでない、もしくは異常に小さい(1kb以下はありえない)
43 $result = ['result' => 'error', 'message' => '不正なZIPファイルです。'];
47 $result = check_zip($file['tmp_name'], $validation);
48 if ($result['result'] == 'ok') {
50 $fname = sha1_file($file['tmp_name']) . '.' . $ext;
51 $destzip = ZIPDIR . $fname;
52 if (move_uploaded_file($file['tmp_name'], $destzip)) {
54 $result = append_entry($fname, $fsize, $title, $author, $delkey, $license, $comment, $hostaddr);
55 if ($result['result'] == 'ok') {
56 // catalog.txtの解析とデータベースへの登録
57 $zipid = $result['id'];
58 search_zip_catalog($destzip, $zipid, $hostaddr);
61 $result = ['result' => 'error', 'message' => 'ファイルの保存に失敗しました。'];
66 $result = ['result' => 'error', 'message' => 'ZIPファイルのアップロードでエラーが発生しました。err=' . $file['error']];
69 $result = ['result' => 'error', 'message' => 'ZIPファイルがアップロードされていません。'];
72 echo json_encode($result);
77 * zipファイルが正しく、png, jpeg, txt以外を含んでいないこと
80 * @param string $zipfile ZIPファイルのパス
81 * @return array 結果を格納したarray
83 function check_zip($zipfile, $validation) {
87 $za = new ZipArchive();
88 $res = $za->open($zipfile);
90 $filecnt = ['png' => 0, 'txt' => 0, 'jpeg' => 0, 'jpg' => 0,
91 'ini' => 0, 'xml' => 0, 'gif' => 0, 'root_png' => 0];
94 for ($i = 0; $i < $za->numFiles; $i++) {
95 $stat = $za->statIndex($i);
96 $entryname = $stat['name'];
97 $entryname = str_replace('\\', '/', $entryname); // バックスラッシュで入っているzipもあり
98 if (!preg_match('/\/$/', $entryname)) {
99 // フォルダ以外なら、ファイル名と拡張子、サイズを取り出す
100 $dirname = pathinfo($entryname, PATHINFO_DIRNAME);
101 $fname = pathinfo($entryname, PATHINFO_FILENAME);
102 $ext = strtolower(pathinfo($entryname, PATHINFO_EXTENSION));
103 $lcname = strtolower(pathinfo($entryname, PATHINFO_BASENAME));
104 $fsize = $stat['size'];
105 if ($fname != 'license' && $fname != 'readme' && $fname != 'read me' &&
106 $lcname != 'thumbs.db' && $lcname != '.ds_store' &&
107 $ext != 'txt' && $ext != 'ini' && $ext != 'xml' && $ext != 'cpd' &&
108 $ext != 'png' && $ext != 'jpg' && $ext != 'jpeg' && $ext != 'gif') {
109 // 対象外のファイル拡張子、またはファイル名
110 $error_files[] = $entryname;
113 if ($ext == 'png' && $dirname == '.') {
114 @$filecnt['root_png']++; // 親ディレクトリ上にあるPNGの数(パーツではない)
116 @$filecnt[$ext]++; // @で初回undefuned警告を握りつぶす
120 if ($lcname == 'catalog.txt') {
124 if ($fname == 'readme' || $fname == 'read me') {
129 if (!(($ext == 'png' && $fsize <= MAX_PNG_SIZE) ||
130 ($ext != 'xml' && $fsize <= MAX_CONTENT_SIZE))) {
131 // pngならMAX_PNG_SIZE以下、xml以外ならMAX_CONTENT_SIZE以下のこと。
133 $large_files[] = $entryname;
136 // validationの場合は、隠しファイル, gifもエラーにする
138 if ($lcname == 'thumbs.db' || $lcname == '.ds_store') {
139 $error_files[] = $entryname;
142 $error_files[] = $entryname;
149 if ($validation && (!$hasCatalog || !$hasReadme)) {
150 return ['result' => 'error',
151 'message' => 'ZIPファイル内にreadme.txt、またはcatalog.txtが含まれていません。',
152 'error_files' => $error_files, 'large_files' => $large_files];
153 } else if (count($error_files) > 0 || count($large_files) > 0) {
154 return ['result' => 'error',
155 'message' => 'ZIPファイル内に許可されない、もしくは大きすぎるファイルが含まれています。',
156 'error_files' => $error_files, 'large_files' => $large_files];
157 } else if ($filecnt['png'] == 0) {
158 return ['result' => 'error',
159 'message' => 'ZIPファイル内に有効なパーツ画像がありません'];
160 } else if (($filecnt['root_png'] + $filecnt['jpg'] + $filecnt['jpeg'] +
161 $filecnt['gif'] + $filecnt['txt'] + $filecnt['xml']) > 5) {
162 return ['result' => 'error',
163 'message' => 'ZIPファイル内にパーツ画像以外のファイルが多数あります。'];
165 return ['result' => 'ok', 'message' => 'valid zip'];
167 return ['result' => 'error', 'message' => 'ZIPファイルが不正です', 'error_code' => $res];
171 * データベースにエントリを追加または更新する
173 * @param string $fname ファイル名
174 * @param integer $fsize ファイルサイズ
175 * @param string $title タイトル
176 * @param string $author 作者名
177 * @param string $delkey 削除キー
178 * @param integer $license ライセンスタイプ
179 * @param string $fncommentame コメント
180 * @param string $hostaddr リモートアドレス
182 function append_entry($fname, $fsize, $title, $author, $delkey, $license, $comment, $hostaddr) {
186 $accessCtrl = new AccessCtrl($pdo);
187 if (!$accessCtrl->register($hostaddr) || $accessCtrl->getFailCount($hostaddr) > MAX_FAIL_COUNT) {
190 'message' => 'アップロード制限中です。しばらくお待ちください。'
195 $fetch = $pdo->prepare('SELECT id, author, delkey FROM ZIP_ENTRIES WHERE fname = ?');
196 $fetch->execute([$fname]);
197 $row = $fetch->fetch();
200 if ($row === FALSE) {
202 $stmt = $pdo->prepare('INSERT INTO ZIP_ENTRIES(fname, fsize, title, author, delkey,
203 license, comment, hostaddr)
204 VALUES (:fname, :fsize, :title, :author, :delkey, :license, :comment, :hostaddr)');
205 $stmt->execute(['fname' => $fname, 'fsize' => $fsize, 'title' => $title, 'author' => $author,
206 'delkey' => $delkey, 'license' => $license, 'comment' => $comment,
207 'hostaddr' => $hostaddr]);
208 $id = $pdo->lastInsertId();
209 append_log("insert ${id} ${fname} ${fsize} ${author} ${delkey} ${title}");
215 $prev_author = $row['author'];
216 $prev_delkey = $row['delkey'];
218 $passphrase = get_trip_pass($author);
219 $prev_passphrase = get_trip_pass($prev_author);
220 if (($prev_passphrase == '' && $delkey == $prev_delkey) ||
221 ($prev_passphrase != '' && $prev_passphrase == $passphrase)) {
222 // トリップ指定がない場合はdelkeyの一致、トリップの指定がある場合はトリップの一致をもって書き換え可とする
223 append_log("update ${id}");
224 $stmt = $pdo->prepare('UPDATE ZIP_ENTRIES
225 SET title = :title, author = :author, delkey = :delkey,
226 license = :license, comment = :comment, hostaddr = :hostaddr
228 $stmt->execute(['id' => $id, 'title' => $title, 'author' => $author,
229 'delkey' => $delkey, 'license' => $license, 'comment' => $comment,
230 'hostaddr' => $hostaddr]);
233 append_log("unmatch delkey. zipid:${id}");
234 $accessCtrl->failIncrement($hostaddr);
237 return ['result' => 'error', 'message' => 'トリップまたはdelkeyが一致しません'];
242 return ['result' => 'ok', 'id' => $id, 'hostaddr' => $hostaddr];