はじめまして。
KLabにインターンでお邪魔させていただいている nakatani-s です。

インターンで製作しているアプリの中で、GmailのSMTPサーバにOAuth認証で入る部分があります。 こちらにあるように、GmailはIMAP・SMTPともにOAuth認証に対応しているのですが、OAuth+IMAPのサンプルコードはあってもOAuth+SMTPのサンプルって中々見つからないんです。
そこで、OAuth+SMTP in Gmail のシンプルなPHPコードを公開して、少しでも世のお役に立てたらなと思うわけです。
いえ、僕が探した範囲なので本当はあるかもしれませんよ?でも恥ずかしいので知っている方も生温かい目でスルーしてください。

開発中にたくさんの助言・アイディアを下さり、インターンの身にこのような機会を与えてくださったKLabの方々には頭が上がらない思いです。

目次

  1. 必要な環境
  2. 下準備
  3. 動作確認
  4. コードと解説
  5. まとめ
  6. 参考

必要な環境

PHPが動くサーバが必要です。サーバのルートに自分でファイルが置ける必要もあります。

下準備

まずはGoogleに、サーバをOAuthのConsumerとして利用するための申請をし ましょう。これはこちらのページを参考にしました。
OAuth Consumer Key と OAuth Consumer Secret は後で使うので、忘れないようにしておいて下さい。

次に、GoogleオフィシャルのOAuth+IMAPのプログラムを動かしてみます。
Gmail IMAP and SMTP using OAuth - Libraries and SamplesのPHP Sampleをダウンロード・解凍し、 xoauth-php-samples/common.php を開きます。
先ほど仕入れた OAuth Consumer Key と OAuth Consumer Secret の情報を、以下の部分に加えて下さい。

$THREE_LEGGED_CONSUMER_KEY = 'your_consumer_key';  // ここと
$THREE_LEGGED_SIGNATURE_METHOD = 'HMAC-SHA1';
$THREE_LEGGED_CONSUMER_SECRET_HMAC = 'your_consumer_secret';  // ここ

それが終わったら xoauth-php-samples/ ディレクトリ以下のファイルを全て自分のサーバにアップロードし、ブラウザ上から three-legged.php にアクセスしてみて下さい。
表示されたページでエディットボックスにご自分のGmailアドレスを入力し、実行ボタンを押すと、GoogleのOAuth認証画面にリダイレクトされます。

これで、"Total messages: X" と表示されたら、ここまではOKです。

ちなみに、僕はここで入力するアドレスをtypoしたせいで、社員の方を巻き込み6時間は悩みましたw

動作確認

こちらのPHPコードをダウンロードしていただき、エディタで開いて下さい。

190-195行目を、ご自分の環境に合わせて編集します。

  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */

これが終わったら、サーバ上の common.php や three-legged.php と同じディレクトリ上に置いて下さい。ブラウザから three-legged-smtp.php にアクセスすると、指定した送信先にメールが届くはずです。

コードと解説

three-legged-smtp.php の解説をさせていただきます。
お気づきな方はお気づきでしょうが、ほぼ three-legged.php と同内容です。
内部で行われている処理の流れを簡単に説明すると、

  1. OAuth認証をし、Googleから認証情報をもらう(1-113行目)
  2. もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)
  3. SMTPプロトコルでメールを送る(190-212行目)
  4. Googleとのコネクションをクローズする(124-126行目)
という感じです。
この「OAuth認証をし、Googleから認証情報をもらう(1-113行目)」と「もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)」の部分が、OAuth+IMAP通信を行っていた three-legged.phpとほぼ共通なので、ほぼ同内容となっています。

さて、元のコードから追加・変更・削除した部分を説明します。

22-23行目: インクルードファイルの変更

require_once 'Zend/Mail/Protocol/Smtp.php'; /* Imap.php -> Smtp.php */
// require_once 'Zend/Mail/Storage/Imap.php';  /* 必要無し */

121-123行目: ssl通信時に送るURLを、SMTP用のものに変更

  $url = 'https://mail.google.com/mail/b/' .
       $email_address . 
       '/smtp/';                /* imap -> smtp */

167-216行目: コメント参照。
要は、Googleのドキュメントに従ってssl通信をしているだけです。

  /* GmailのSMTPサーバと通信するためのsocketを開く */
  $smtpServer = "tls://smtp.gmail.com";
  $port = "465";                /* 465番ポートでうまくいかなかったら587番も試してみてください */
  $timeout = "45";
  $localhost = $_SERVER['REMOTE_ADDR'];
  $newLine = "\r\n";

  $smtpConnect = fsockopen($smtpServer, $port, $errno, $errstr, $timeout);

  $smtpResponse = fgets($smtpConnect, 4096);

  /* SMTP+OAuthの認証手続き */
  /* http://code.google.com/intl/ja/apis/gmail/oauth/protocol.html に規格があります */
  fputs($smtpConnect, "HELO $localhost". $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  fputs($smtpConnect,"AUTH XOAUTH" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect, $initClientRequestEncoded . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  /* 認証終了後にメールを送る */
  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */

  fputs($smtpConnect, "MAIL FROM: <$from>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect, "RCPT TO: <$to>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  fputs($smtpConnect, "DATA" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  $headers = "MIME-Version: 1.0" . $newLine;
  $headers .= "Content-type: text/html; charset=iso-8859-1" . $newLine; /* charsetはお好みで */
  $headers .= "To: $nameto $to1" . $newLine;
  $headers .= "From: $namefrom <$from>" . $newLine;
	 
  fputs($smtpConnect, "To: <$to>\r\nFrom: $from\r\nSubject: $subject\r\n$headers\r\n\r\n$message\r\n.\r\n");
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect,"QUIT" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  fclose($smtpConnect);

218-245行目: OAuth+IMAPでのメール受信に固有の部分をコメントアウト

  /* IMAP用の認証なので必要無し */
  /**
   * Make the IMAP connection and send the auth request
   */
  /* $imap = new Zend_Mail_Protocol_Imap('imap.gmail.com', '993', true); */
  /* $authenticateParams = array('XOAUTH', $initClientRequestEncoded); */
  /* $imap->requestAndResponse('AUTHENTICATE', $authenticateParams); */
  
  /* 以下、GMailからメールを受信して表示する部分なので必要無し */
  /**
   * Print the INBOX message count and the subject of all messages
   * in the INBOX
   */
  /* $storage = new Zend_Mail_Storage_Imap($imap); */
 
  /* include 'header.php';  */
  /* echo '<h1>Total messages: ' . $storage->countMessages() . "</h1>\n"; */

  /**
   * Retrieve first 5 messages.  If retrieving more, you'll want
   * to directly use Zend_Mail_Protocol_Imap and do a batch retrieval,
   * plus retrieve only the headers
   */
  /* echo 'First five messages: 
    '; */ /* for ($i = 1; $i <= $storage->countMessages() && $i <= 5; $i++ ){ */ /* echo '
  • ' . htmlentities($storage->getMessage($i)->subject) . "
  • \n"; */ /* } */ /* echo '
'; */

まとめ

いかがでしたか? Google Code に置いてあるコードをベースに、OAuth+SMTP認証ができました。
しかし、このコードでは Google からOAuth認証情報を得る部分が PHP のZend Framework の内部に隠されてしまっています。
インターン中は時間がないのですが、なるべくいろいろな言語に流用できるように認証情報取得部分もホワイトボックス化したいと思います。
もしできたら、僕の個人ブログにでもコードを書きたいと思っています。

最後までお読み下さりありがとうございました。

ソースコード

http://lab.klab.org/young/wp-content/uploads/data/code/three-legged-smtp.php.txt

参考