libcurl
libcurl は様々な通信プロトコルに対応している C ライブラリです。 メール関連では、smtp, pop3, imap に対応している。
libcurl - the multiprotocol file transfer library
https://curl.haxx.se/libcurl/
アプリ作成方針
SMTP サーバとして、ローカルネット内に建てた postfix サーバGmail サーバを対象とする postfix で動作確認をした後、Gmail で動作確認する。
ユーザー名とパスワードは、プログラムに記述するのではなく、 json 形式の設定ファイルから読み込む。 そのため、jsonライブラリ json-c が必要です。
jsonライブラリ json-c
https://github.com/json-c/json-c/wiki
設定ファイルはホームディレクトリ内の好みの場所におく
Gmail サーバーの仕様
サーバ名とポート番号
Google: アプリからのメール送信
https://support.google.com/a/answer/176600?hl=ja
ユーザ認証
Gmail は、下記の2つに対応している。
(1) AUTH PLAIN: ユーザ名(Google アカウント) と パスワード
(2) XOAUTH2: アクセストークン
(1) のときは、安全性の低いアプリからのアクセスを許可すること。
安全性の低いアプリのアクセスを有効にする方法
https://qiita.com/miriwo/items/7c5a451a35cecdd72085
この記事では、(1) の AUTH PLAIN で、アプリを作成する。
補足:
Gmail サーバからは、ユーザ認証の方式として、下記の応答が返る。
AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
libcurl のインストール
MACの場合 curl (libcurl) はプリインストールされている。
% curl --version
curl 7.64.1
header files /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curl
librariy files /usr/lib/libcurl.dylib
最新版( 7.73.0 )を使う場合は、 brew コマンドでインストールする。 https://formulae.brew.sh/formula/curl
アプリを作成する
公式サイトに SMTP の例がある。
(1) SMTP サーバにメールを送信する
lincurl にはメールを組み立てる機能はない。 下記のように、プログラマがコールバック関数を記述する。
static const char *payload_text[] = { "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n", "To: " TO_MAIL "\r\n", "From: " FROM_MAIL "\r\n", "Cc: " CC_MAIL "\r\n", "Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@" "rfcpedant.example.org>\r\n", "Subject: SMTP example message\r\n", "\r\n", /* empty line to divide headers from body, see RFC5322 */ "The body of the message starts here.\r\n", "\r\n", "It could be a lot of lines, could be MIME encoded, whatever.\r\n", "Check RFC5322.\r\n", NULL }; struct upload_status { int lines_read; }; static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { return 0; } data = payload_text[upload_ctx->lines_read]; if(data) { size_t len = strlen(data); memcpy(ptr, data, len); upload_ctx->lines_read++; return len; } return 0; } int main(void) { curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); }
メール文 payload_text は、 インターネットメッセージフォーマット( RFC 5322/2822) に準拠する必要がある。
メールを書くときに使う文法はRFC 5322です
https://ponsuke-tarou.hatenablog.com/entry/2018/02/24/163729
RFC 5322
http://srgia.com/docs/rfc5322j.html
(2) TLS 暗号化
下記のように記述する。
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
[API: CURLOPT_USE_SSL](https://curl.haxx.se/libcurl/c/CURLOPT_USE_SSL.html
gmailサーバのときは、これでよいが。 postfixサーバのときは、下記のエラーになる。
SSL certificate problem: Invalid certificate chain
サーバ証明書が自己認証なので、 サーバ証明書の検証を無効にする。
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
(3) ユーザ認証
ユーザ認証はpop3を参考にする。
下記のように。ユーザ名とパスワードを設定する。
curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
デバッグのため、通信のトレースを有効にする。
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
smtp-mail.c では、 Date ヘッダと Message-ID ヘッダが固定になっている。
下記を参考に、動的に生成するようにする。
libcurl によるGMAIL送信
https://hatenaclang.blogspot.com/2014/10/libcurl-gmail.html
プログラム全文は github に公開した。
https://github.com/ohwada/MAC_cpp_Samples/tree/master/libcurl/mail/smtp
2022年 10月 追記
2022年 5月 より
Gmail および Googleのサービスにおいて
ユーザ名とパスワード によるログインが廃止になった。
Google: 安全性の低いアプリと Google アカウント
上記のプログラムを実行すると下記の応答が返る。
Username and Password not accepted.
以前は Googleアカウント管理において 「安全性の低いアプリの許可」というオプションがあった。
現時点では非推奨となり無くなった。