C 言語にて libcurl を使って メールを送信する

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

設定ファイルはホームディレクトリ内の好みの場所におく

参考 : C言語にて ホームディレクトリのパス名を取得する

Gmail サーバーの仕様

サーバ名とポート番号

サーバ名:smtp.gmail.com

  • ポート番号 465:(SSL)
  • ポート番号 587: (TLS) 推奨

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 サーバにメールを送信する

example: smtp-mail.c

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);
}

API: CURLOPT_READFUNCTION

API: CURLOPT_READDATA

API: CURLOPT_UPLOAD

メール文 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 暗号化

example: smtp-tls.c

下記のように記述する。

    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);

API: CURLOPT_SSL_VERIFYPEER

API: CURLOPT_SSL_VERIFYHOST

(3) ユーザ認証

ユーザ認証はpop3を参考にする。

example: pop3-ssl.c

下記のように。ユーザ名とパスワードを設定する。

curl_easy_setopt(curl, CURLOPT_USERNAME, "user");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");

デバッグのため、通信のトレースを有効にする。

curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

API: CURLOPT_VERBOSE

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アカウント管理において 「安全性の低いアプリの許可」というオプションがあった。

現時点では非推奨となり無くなった。