業務でメール受信した内容をDBへ取り込むことになったので
骨組みを用意してほしいとの依頼がきたときの作業ログになります。
以前はphpでごりごりヘッダーを解析した記憶がありますが、
今は便利になりました。ありがたくライブラリーを使わせていただくことにしました。
■ まずはComposerの導入
Composerとはプロジェクト単位でパッケージを管理できるパッケージ管理ツールです。
# composer
Do not run Composer as root/super user! See https://getcomposer.org/root for details
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version 1.8.4 2019-02-11 10:52:10
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--profile Display timing and memory usage information
--no-plugins Whether to disable plugins.
-d, --working-dir=WORKING-DIR If specified, use the given directory as working directory.
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
about Shows the short information about Composer.
archive Creates an archive of this composer package.
browse Opens the package's repository URL or homepage in your browser.
check-platform-reqs Check that platform requirements are satisfied.
clear-cache Clears composer's internal package cache.
clearcache Clears composer's internal package cache.
config Sets config options.
create-project Creates new project from a package into given directory.
depends Shows which packages cause the given package to be installed.
diagnose Diagnoses the system to identify common errors.
dump-autoload Dumps the autoloader.
dumpautoload Dumps the autoloader.
exec Executes a vendored binary/script.
global Allows running commands in the global composer dir ($COMPOSER_HOME).
help Displays help for a command
home Opens the package's repository URL or homepage in your browser.
i Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
info Shows information about packages.
init Creates a basic composer.json file in current directory.
install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
licenses Shows information about licenses of dependencies.
list Lists commands
outdated Shows a list of installed packages that have updates available, including their latest version.
prohibits Shows which packages prevent the given package from being installed.
remove Removes a package from the require or require-dev.
require Adds required packages to your composer.json and installs them.
run-script Runs the scripts defined in composer.json.
search Searches for packages.
show Shows information about packages.
status Shows a list of locally modified packages, for packages installed from source.
suggests Shows package suggestions.
u Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
update Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
upgrade Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
validate Validates a composer.json and composer.lock.
why Shows which packages cause the given package to be installed.
why-not Shows which packages prevent the given package from being installed.
rootで操作しちゃ駄目だそうです。
■ composerのインストール
# yum install --enablerepo=remi,remi-php73 composer
■ php-mime-mail-parserを探してみる
$ composer search php-mime-mail-parser
php-mime-mail-parser/php-mime-mail-parser A fully tested email parser for PHP 7.1+ (mailparse extension wrapper).
messaged/php-mime-mail-parser Mailparse extension wrapper for PHP 5.3+
exorus/php-mime-mail-parser A fully tested email parser for PHP 7.1+ (mailparse extension wrapper).
benpoulson/php-mime-mail-parser A fully tested email parser for PHP 7.1+ (mailparse extension wrapper).
decodellc/php-mime-mail-parser (FORK) Fully Tested Mailparse Extension Wrapper for PHP 5.4+
wambacom/smtpd Fork thefox/smtpd. SMTP server (library) written in PHP. In this fork we use eXorus/php-mime-mail-parser as mail parser instead of zend::message
mambaru/smtpd Fork thefox/smtpd. SMTP server (library) written in PHP. In this fork we use eXorus/php-mime-mail-parser as mail parser instead of zend::message
上記で、「php-mime-mail-parser/php-mime-mail-parser」を入れてみます。
■ php-mime-mail-parserをインストール
$ composer require php-mime-mail-parser/php-mime-mail-parser
※rootで実行しようとすると、
「Do not run Composer as root/super user! See https://getcomposer.org/root for details」
って警告されます。
$ composer require php-mime-mail-parser/php-mime-mail-parser
Using version ^5.0 for php-mime-mail-parser/php-mime-mail-parser
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- php-mime-mail-parser/php-mime-mail-parser 5.0.1 requires ext-mailparse * -> the requested PHP extension mailparse is missing from your system.
- php-mime-mail-parser/php-mime-mail-parser 5.0.0 requires ext-mailparse * -> the requested PHP extension mailparse is missing from your system.
- Installation request for php-mime-mail-parser/php-mime-mail-parser ^5.0 -> satisfiable by php-mime-mail-parser/php-mime-mail-parser[5.0.0, 5.0.1].
こちら( https://packagist.org/packages/decodellc/php-mime-mail-parser )にrequiresがのってましたね。
・php: ^5.4.0 || ^7.0
・ext-mailparse: *
「PHP extension mailparse is missing」だそうです。
yumで必要なextensionをいれます。
# yum install --enablerepo=remi,remi-php73 php-pecl-mailparse
# ls -l /etc/php.d/40-mailparse.ini
-rw-r--r-- 1 root root 122 Mar 20 19:47 /etc/php.d/40-mailparse.ini
mailparseの設定用ファイルが、/etc/php.dの下に作成されていました。
無事php-pecl-mailparseをインストールできたので、
もう一度composerでphp-mime-mail-parserをインストールします。
$ composer require php-mime-mail-parser/php-mime-mail-parser
Using version ^5.0 for php-mime-mail-parser/php-mime-mail-parser
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing php-mime-mail-parser/php-mime-mail-parser (5.0.1): Downloading (100%)
Writing lock file
Generating autoload files
無事インストールもでき、今入れたパッケージがjsonに書き込まれていました。
$ cat composer.json
{
"require": {
"php-mime-mail-parser/php-mime-mail-parser": "^5.0"
}
}
phpとapacheを再起動します。
# systemctl restart php-fpm
# systemctl restart httpd
再起動後、iniファイルが反映されているか下記コマンドで確認。
mailparseが出ていればOKです。
# php -i |grep mailparse
/etc/php.d/40-mailparse.ini,
mailparse
mailparse support => enabled
mailparse.def_charset => us-ascii => us-ascii
■ その他
httpd-develパッケージやgcc、gcc-c++パッケージも必要みたいですが、
私の環境には既にインストールされていました。
まだインストールされていない場合は、下記でインストールすればよいかと。
# yum install --enablerepo=remi,remi-php73 httpd-devel gcc gcc-c++
■ MTA(Postfix)の設定

外部に対して SMTP と SMTPS のポートを開放したいので、
次のコマンドで開放するサービスを追加し、ファイアウォールの設定をリロードします。
# firewall-cmd --add-service=smtp --zone=public --permanent
success
# firewall-cmd --add-service=smtps --zone=public --permanent
success
# firewall-cmd --reload
success
# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: dhcpv6-client http https smtp smtps
ports: xxxx/tcp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
これで SMTP と SMTPS のポートが外部に向けて開放されました。
––––––––––––––––––––––––––––––––––––
<<全体的な流れ>>
––––––––––––––––––––––––––––––––––––
直接コマンドをaliasesに記述することも可能ですが
hoge: hoge, "|/usr/bin/php /path/to/cmd/kick.php"
実行ユーザがnobodyなってしまいメールヘッダに
X-Authentication-Warning: ~
のようなものが付加されてしまいます。
なので、aliasesでincludeを使えるようにし、hogeにメールが来た際に、
/path/to/file に渡し、/path/to/file の所有者としてコマンドを実行させます。
(:コロンを連続してるので注意)
hoge: :include:/path/to/file
次に/path/to/fileを新規作成してファイルの内容に実際のコマンドを記述します。
"| /usr/bin/php /path/to/cmd/kick.php >> /path/to/cmd/logs/command.log"
––––––––––––––––––––––––––––––––––––
(1)まずは、初期の状態ではpostfixはincludeを許可されていないので、
# postconf |grep allow_mail
allow_mail_to_commands = alias, forward
allow_mail_to_files = alias, forward
main.cfに下記を追加して、
allow_mail_to_commands = alias,forward,include
allow_mail_to_files = alias,forward,include
リロードします。
# systemctl reload postfix
# postconf |grep allow_mail
allow_mail_to_commands = alias,forward,include
allow_mail_to_files = alias, forward,include
(2)includeが有効になったので、下記をaliasesに追加します。
trigger: :include:/path/to/file
(3)aliasesに追記したファイル、/path/to/fileに実際のコマンドを記述します。
|"/usr/bin/php /path/to/cmd/kick.php >> /path/to/cmd/logs/command.log 2>&1"
(4)パーミッション、所有者の確認
# chmod 644 /path/to/file
# chown user.postfix /path/to/file
(5)trigger@~へメール送信し、動作確認
(6)連携用のphpはサンプルを修正し、下記のようにしてます。
<?php
//We need to add the library first !
require_once __DIR__.'/vendor/autoload.php';
$Parser = new PhpMimeMailParser\Parser();
// 下記の1~5のどれかで取り込むことになります。
// 本番
$raw_mail = stream_get_contents(STDIN);
$Parser->setText($raw_mail);
// テキストデータからの動作確認用
//$path = 'path/to/mail.txt';
// Or There are four methods available to indicate which mime mail to parse.
// You only need to use one of the following four:
// 1. Specify a file path to the mime mail.
//$Parser->setPath($path);
// 2. Specify a php file resource (stream) to the mime mail.
//$Parser->setStream(fopen($path, "r"));
// 3. Specify the raw mime mail text.
//$Parser->setText(file_get_contents($path));
// 4. Specify a stream to work with mail server
//$Parser->setStream(fopen("php://stdin", "r"));
// We can get all the necessary data
$to = $Parser->getHeader('to');
$from = $Parser->getHeader('from');
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
$htmlEmbedded = $Parser->getMessageBody('htmlEmbedded'); //HTML Body included data
// and the attachments also
$attach_dir = '/home/_USER_/httpd/sbin/attachments/';
$Parser->saveAttachments($attach_dir);
// loop the attachments
$attachments = $Parser->getAttachments();
if (count($attachments) > 0) {
foreach ($attachments as $attachment) {
echo 'Filename : '.$attachment->getFilename().'<br />'; // logo.jpg
echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />'; // 1000
echo 'Filetype : '.$attachment->getContentType().'<br />'; // image/jpeg
}
}
// Write Text
$log = "\n----------------------------------------------------------------\n";
$log.= "From : {$from}\n";
$log.= "To : {$to}\n";
$log.= "Subject : {$subject}\n";
$log.= "Text : {$text}\n";
$log.= "HTML : {$html}\n";
$log.= "Embedded : {$htmlEmbedded}\n";
$fp = fopen('/path/to/cmd/logs/receive.log', 'a');
fwrite($fp, $log);
fclose($fp);
?>
まずは、骨組みだけですが、動作しました。