diff --git a/.env.example b/.env.example index 51348d7..4594291 100644 --- a/.env.example +++ b/.env.example @@ -45,6 +45,8 @@ MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null +MAIL_NOREPLY_USER=null +MAIL_NOREPLY_PASS=null MAIL_ENCRYPTION=null MAIL_OWNER_ADDRESS=null MAIL_OWNER_NAME="${APP_NAME}" diff --git a/app/Http/Controllers/Home/Contact.php b/app/Http/Controllers/Home/Contact.php new file mode 100644 index 0000000..b5dc595 --- /dev/null +++ b/app/Http/Controllers/Home/Contact.php @@ -0,0 +1,158 @@ +field = [ + 'kenmei' => '', + 'adr' => '', + 'cat' => '', + 'cats' => [ + '' => '', + 'bugreport' => 'バグ報告したい', + 'chat' => 'チャットサービス(XMPP、IRC、Mumble)について聞きたい', + 'social' => 'SNSサービス(Pleroma、PeerTube)について聞きたい', + 'privfront' => '代替SNSフロントサービス(Nitter、Invidious、Librarian、Searx)について聞きたい', + 'storage' => 'ストレージサービス(Gitea、Nextcloud)について聞きたい', + 'otherserv' => '076外サービス(テク諏訪、076萌、URLoli、some.very.questionable.website、hozon.site、xmr.jp等)について聞きたい', + 'scam1' => 'DMCA報告したい', + 'scam2' => '営業したい', + 'scam3' => '法律について', + 'scam4' => '税金について', + 'scam5' => '無駄な話', + ], + 'bunsyo' => '', + 'gpg' => '', + 'ruleapply' => false, + ]; + } + + public function index (Request $r) { + if (isset($r->submit)) return $this->send($r); + return view('pages.site.contact', ['field' => $this->field, 'err' => []]); + } + + public function seiko () { + return view('pages.site.contact-seiko', ['field' => $this->field, 'suc' => ['メールを送りました!', '送信者様は連絡ルールを守ったら、24時間以内で返事します。']]); + } + + public function send (Request $r) { + $this->field['adr'] = $r->adr; + $this->field['kenmei'] = $r->kenmei; + $this->field['cat'] = $r->cat; + $this->field['catname'] = $r->cat; + $this->field['gpg'] = !isset($r->gpg) || is_null($r->gpg) || $r->gpg == '' ? null : file_get_contents($r->file('gpg')); + $this->field['bunsyo'] = $r->bunsyo; + $this->field['ruleapply'] = isset($r->ruleapply); + + $err = []; + $gpg = null; + + // メールアドレス + if (!isset($this->field['adr']) || is_null($this->field['adr']) || $this->field['adr'] == '') $err[] = 'メールアドレスをご入力下さい。'; + else if (!filter_var($this->field['adr'], FILTER_VALIDATE_EMAIL)) $err[] = 'メールアドレスを正しくご入力下さい。'; + $this->field['filename'] = trim($this->field['adr']).'.key'; + + // 件名 + if (!isset($this->field['kenmei']) || is_null($this->field['kenmei']) || $this->field['kenmei'] == '') $err[] = '件名をご入力下さい。'; + if (str_contains($this->field['kenmei'], 'http://') || str_contains($this->field['kenmei'], 'https://')) $err[] = '件名でURLを入らないで下さい。'; + $this->field['bunsyo'] = trim($this->field['bunsyo']); + + // カテゴリ + if (!isset($this->field['cat']) || is_null($this->field['cat']) || $this->field['cat'] == '') $err[] = 'カテゴリをご選択下さい。'; + + // GPG + if (!isset($this->field['gpg']) || is_null($this->field['gpg']) || $this->field['gpg'] == '') $err[] = 'GPGをご選択下さい。'; + else { + $gpg = new \gnupg(); + $info = $gpg->import($this->field['gpg']); + $gpg->addencryptkey($info['fingerprint']); + Storage::disk('public')->put($this->field['filename'], $this->field['gpg']); + $path = Storage::disk('public')->path($this->field['filename']); + $verifygpg = explode("\n", $this->run('gpg --dry-run --import '.$path))[0]; + if (!str_contains($verifygpg, '処理数の合計: 1') && !str_contains($verifygpg, 'Total number processed: 1')) $err[] = $verifygpg; + } + + // 文章 + if (!isset($this->field['bunsyo']) || is_null($this->field['bunsyo']) || $this->field['bunsyo'] == '') $err[] = '文章をご入力下さい。'; + if (str_contains($this->field['bunsyo'], 'http://') || str_contains($this->field['bunsyo'], 'https://')) $err[] = '文章でURLを入らないで下さい。'; + $this->field['bunsyo'] = $gpg->encrypt(trim("メール: ".$this->field['adr']."\n\n".$this->field['bunsyo'])); + + // 連絡ルール + if (!$this->field['ruleapply']) $err[] = 'ルールを同意して下さい。'; + + if (!empty($err)) { + if (isset($this->field['gpg']) && !is_null($this->field['gpg']) && $this->field['gpg'] != '') Storage::disk('public')->delete($this->field['filename']); + return view('pages.site.contact', ['field' => $this->field, 'err' => $err]); + } + + // カテゴリはDMCA報告、営業、税金、法律、又は無駄な話を選択したら、いつでも送信せず失敗します。 + if (str_contains($this->field['cat'], 'scam')) { + Storage::disk('public')->delete($this->field['filename']); + return view('pages.site.contact', ['field' => $this->field, 'err' => ['送信に失敗しました。数時間後もう一回送信してみて下さい。']]); + } + + foreach ($this->field['cats'] as $i => $c) { + if ($this->field['cat'] == $i) $this->field['catname'] = $c; + } + + // メールを送る + try { + $mail = new MailerController(); + if (!$mail->compose($this->field, true)) { + Storage::disk('public')->delete($this->field['filename']); + return view('pages.site.contact', ['field' => $this->field, 'err' => ['送信に失敗しました。数時間後もう一回送信してみて下さい。']]); + } + } catch (\Throwable $e) { + Storage::disk('public')->delete($this->field['filename']); + $this->field['bunsyo'] = $r->bunsyo; + Log::critical($e); + return view('pages.site.contact', ['field' => $this->field, 'err' => ['送信に失敗しました。数時間後もう一回送信してみて下さい。']]); + } + + Storage::disk('public')->delete($this->field['filename']); + $this->field['kenmei'] = ''; + $this->field['adr'] = ''; + $this->field['cat'] = ''; + $this->field['bunsyo'] = ''; + $this->field['gpg'] = ''; + $this->field['ruleapply'] = false; + + return redirect('/contact/seiko'); + } + + function run ($bin, $command = '', $force = true) { + $stream = null; + $bin .= $force ? ' 2>&1' : ''; + + $descriptorSpec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w') + ); + + $process = proc_open($bin, $descriptorSpec, $pipes); + + if (is_resource($process)) { + fwrite($pipes[0], $command); + fclose($pipes[0]); + + $stream = stream_get_contents($pipes[1]); + fclose($pipes[1]); + + proc_close($process); + } + + return $stream; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/MailerController.php b/app/Http/Controllers/MailerController.php new file mode 100644 index 0000000..f5274d1 --- /dev/null +++ b/app/Http/Controllers/MailerController.php @@ -0,0 +1,74 @@ +SMTPDebug = 2; + $mail->isSMTP(); + $mail->Host = config('mail.mailers.smtp.host'); + $mail->Port = config('mail.mailers.smtp.port'); + $mail->SMTPSecure = config('mail.mailers.smtp.encryption'); + $mail->SMTPAuth = true; + $mail->From = config('mail.'.($form ? 'owner' : 'from').'.address'); + $mail->FromName = mb_encode_mimeheader(config('mail.mailers.smtp.'.($form ? 'fromname' : 'noreplyfrom')), 'JIS'); + $mail->Username = config('mail.mailers.smtp.'.($form ? 'username' : 'noreplyuser')); + $mail->Password = config('mail.mailers.smtp.'.($form ? 'password' : 'noreplypass')); + + $mail->AddAddress(config('mail.'.($form ? 'owner' : 'from').'.address')); + if ($form) $mail->addReplyTo(config('mail.owner.address'), config('mail.owner.name')); + else $mail->addReplyTo($val['adr']); + + $mail->SMTPOptions = [ + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true + ] + ]; + + if (isset($val['attach'])) { + for ($i=0; $i < count($val['attach']); $i++) { + $mail->AddAttachment($val['attach'][$i], $val['attachname'][$i]); + } + } + + if (isset($val['gpg'])) { + $mail->AddStringAttachment($val['gpg'], $val['filename']); + $mail->AddStringAttachment($val['bunsyo'], $val['adr'].'.gpg'); + } + + $mail->isHTML(false); + + $mail->Subject = mb_encode_mimeheader('【'.$val['catname'].'】'.$val['kenmei'], 'JIS'); + if ($form) $mail->Body = mb_convert_encoding($val['bunsyo'], 'UTF-8'); + else $mail->Body = mb_convert_encoding($val['bunsyo'], 'JIS'); + + if (!$mail->send()) { + Log::critical('Mailer error: ' . $mail->ErrorInfo); + return false; + } + } catch (Exception $e) { + Log::critical($e); + return false; + } + + return true; + } +} diff --git a/composer.json b/composer.json index 4791810..45a6e77 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "guzzlehttp/guzzle": "^7.4.0", "laravel/framework": "^8.74.0", "laravel/tinker": "^2.6.2", + "phpmailer/phpmailer": "^6.5", "predis/predis": "^1.1.9" }, "require-dev": { diff --git a/config/filesystems.php b/config/filesystems.php index 94c8112..2ea896a 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -55,6 +55,13 @@ return [ 'visibility' => 'public', ], + 'private' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'private', + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/config/mail.php b/config/mail.php index d67deb6..c1894fd 100644 --- a/config/mail.php +++ b/config/mail.php @@ -39,8 +39,13 @@ return [ 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 'port' => env('MAIL_PORT', 587), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'fromname' => env('MAIL_OWNER_NAME'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), + 'noreplyfrom' => env('MAIL_FROM_NAME'), + 'noreplyuser' => env('MAIL_NOREPLY_USER'), + 'noreplypass' => env('MAIL_NOREPLY_PASS'), + 'sendmail' => '/usr/sbin/sendmail -bs', 'timeout' => null, 'auth_mode' => null, ], @@ -87,6 +92,10 @@ return [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 'name' => env('MAIL_FROM_NAME', 'Example'), ], + 'owner' => [ + 'address' => env('MAIL_OWNER_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_OWNER_NAME', 'Example'), + ], /* |-------------------------------------------------------------------------- diff --git a/resources/views/pages/site/contact-seiko.blade.php b/resources/views/pages/site/contact-seiko.blade.php new file mode 100644 index 0000000..df20af4 --- /dev/null +++ b/resources/views/pages/site/contact-seiko.blade.php @@ -0,0 +1,15 @@ +@extends('theme.'.env('THEME').'.site') + +@section('content') +
+
連絡フォーム
+
+ +
+
+ +@endsection diff --git a/resources/views/pages/site/contact.blade.php b/resources/views/pages/site/contact.blade.php new file mode 100644 index 0000000..ca77afc --- /dev/null +++ b/resources/views/pages/site/contact.blade.php @@ -0,0 +1,84 @@ +@extends('theme.'.env('THEME').'.site') + +@section('content') +
+
連絡フォーム
+
+ @if (!empty($err)) + + @endif + 私のGPGはこちらです→ suwako-gpg.key +
+
+ @csrf +
+
メールアドレス*
+
+ + 例)notwork@076.ne.jp +
+
+
+
件名*
+
+ + 例)アカウントの作成について +
+
+
+
カテゴリ*
+
+ + 注意)違うカテゴリを選択すると、メールアドレスをブロックされます!! +
+
+
+
GPG公開キー*
+
+
+
+
文章*
+
+
+
+
連絡ルール*
+
+

+ メールを送る前、ちゃんと下記のルールをお読み下さい。
+ ルールを守らないと、送信者様のメールアドレスをブラックリストに追加しますので、ご注意下さい。

+ + ① 件名及び文章にURLを追加できません、「http」や「https」等を消しても駄目です。
+   それの場合、送信者様のメールアドレスをブラックリストに追加します。
+ ② 正しいカテゴリを選択して下さい。
+   文章と件名とカテゴリが異なったら、送信者様のメールアドレスをブラックリストに追加します。
+ ③ 送るには、GPGパブリックキーは必須です。新規創造するには:「gpg --generate」
+ ④ 返事する時も、GPGで暗号されないと、
+   私は返事しません。私のパブリックキーはページの上で御座います。
+ ⑤ このフォームで、私のGPGパブリックキーを送ったら、返事しません。
+ ⑥ 私は返事しなければ、複数同じメールを送ると、送信者様のメールアドレスをブラックリストに追加します。
+ ⑦ 日本語でメールを送信して下さい。他の言語の場合、翻訳機で読めないと、返事しません。 +

+
+ + +
+
+
+
+
+
+
+
+
+ +@endsection diff --git a/routes/view/site.php b/routes/view/site.php index 271c480..aa3fe76 100644 --- a/routes/view/site.php +++ b/routes/view/site.php @@ -32,5 +32,7 @@ Route::group(['prefix' => 'video'], function () { Route::get('/memberlist', 'User\MemberList@index'); Route::get('/commentlist', 'Home\CommentList@index'); +Route::get('/contact/seiko', 'Home\Contact@seiko'); +Route::any('/contact', 'Home\Contact@index'); Route::get('/{slug}', 'Home\Index@page');