わかってみれば大したことないExchange OnlineからJavaMailでメール受信にハマったこと

## はじめに
Java(Kotlin)アプリでExchange Onlineとメール送受信することになり、簡単に調べがついたメール送信をしてみた。

```kotlin:build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
  id("org.springframework.boot") version "2.1.6.RELEASE"
  kotlin("jvm") version "1.3.41"
  kotlin("plugin.spring") version "1.3.41"
}
repositories {
  mavenCentral()
}
dependencies {
  implementation(platform("org.springframework.boot:spring-boot-dependencies:2.1.6.RELEASE"))
  implementation("org.springframework.boot:spring-boot-starter-mail")
  implementation(kotlin("stdlib-jdk8"))
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
  jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
  jvmTarget = "1.8"
}
```

```yml:application.yml
spring:
  mail:
    host: smtp.office365.com
    port: 587
    username: め〜るあどれす
    password: ぱすわ〜ど
    properties:
      mail:
        smtp:
          starttls:
            enable: true
```

```kotlin:Application.kt
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.mail.SimpleMailMessage
import org.springframework.mail.javamail.JavaMailSender

@SpringBootApplication
class Application(val mailSender: JavaMailSender): CommandLineRunner {
  override fun run(vararg args: String?) {
    val mailMessage = SimpleMailMessage().apply {
      setTo("宛先め〜るあどれす")
      from = "め〜るあどれす"
      subject = "けんめい"
      text = "ほんぶん"
    }
    mailSender.send(mailMessage)
  }
}

fun main( args: Array<String>) {
  runApplication<Application>(*args)
}
```
* 今までMaven専門できたけど、GroovyじゃなくてKotlinで書けるようになったというのでGradleに挑戦中。だから記述内容が最適化は自信なし。これからもGradleを積極的に使うつもりだけど、時々Mavenより判りやすいという話を聞くけど、自由度が高いから当然判りづらいという感想。
* プロパティでmail.smtp.ssl.enable=trueだと、javax.net.ssl.SSLException: Unsupported or unrecognized SSL messageが発生するので、[こちら](https://stackoverflow.com/questions/14430962/send-javamail-using-office365)を参考にした。mail.smtp.authはなくても、デバッグログ的に動きに差はなかった。

このように簡単にメール送信ができたので、Exchange Onlineとはいえ普通?のメールサーバと同じで受信も簡単にできるだろうと思ってしまった。最終的にはその通りなのだが、途中で躓くと疑心暗鬼にかられて右往左往してしまった。

## ちょっと雲行きが...
メール送信できたから受信も当然できると言いふらしてしまい、いざ実装という段階になって、あれ!?と思ったのが、JavaMailSenderがあるspring-context-support-5.1.8.RELEASE.jarにJavaMailReciverはない。JavaMail自身は受信もできるのに何故?。spring integrationにはメール受信もあるけど、わざわざ組み込む気にはなれずJavaMailを直接使うことにした。
しかし、いろいろな実装をラッピングして使いやすくしてくれるSpring Frameworkにメール受信がないってことは、何か共通化しにくい事情があるのでは?と思ってしまった。

## そしてハマった
JavaMailでの受信ロジックだけど、それほど警戒することはないものだった。

```kotlin
val props = Properties()
// セッションを生成
val session = Session.getInstance(props)
// プロトコルを選択
val store = session.getStore("imaps")
// サーバに接続
store.connect("outlook.office365.com", 993, "め〜るあどれす", "ぱすわ〜ど")
// フォルダを選択
val folder = store.getFolder("INBOX")
folder.open(Folder.READ_ONLY)
// メール受信
folder.messages.map {
  println(it.subject)
}
folder.close(false)
store.close()
```
するとstore.connectメソッドの呼び出しでjavax.mail.AuthenticationFailedException: AUTHENTICATE failedが発生。session.debug = trueを指定して詳細なログを取得すると

```
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle]
DEBUG IMAPS: mail.imap.fetchsize: 16384
DEBUG IMAPS: mail.imap.ignorebodystructuresize: false
DEBUG IMAPS: mail.imap.statuscachetimeout: 1000
DEBUG IMAPS: mail.imap.appendbuffersize: -1
DEBUG IMAPS: mail.imap.minidletime: 10
DEBUG IMAPS: closeFoldersOnStoreFailure
DEBUG IMAPS: trying to connect to host "outlook.office365.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready. 
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAPS: AUTH: PLAIN
DEBUG IMAPS: AUTH: XOAUTH2
DEBUG IMAPS: protocolConnect login, host=outlook.office365.com, user=め〜るあどれす, password=<non-null>
DEBUG IMAPS: AUTHENTICATE PLAIN command trace suppressed
DEBUG IMAPS: AUTHENTICATE PLAIN command result: A1 NO AUTHENTICATE failed.
```
なんか認証に失敗している?ここが迷走の始まり。
プロトコルをpop3sに、ポート番号を995で試したけど

```
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]
DEBUG POP3: mail.pop3s.rsetbeforequit: false
DEBUG POP3: mail.pop3s.disabletop: false
DEBUG POP3: mail.pop3s.forgettopheaders: false
DEBUG POP3: mail.pop3s.cachewriteto: false
DEBUG POP3: mail.pop3s.filecache.enable: false
DEBUG POP3: mail.pop3s.keepmessagecontent: false
DEBUG POP3: mail.pop3s.starttls.enable: false
DEBUG POP3: mail.pop3s.starttls.required: false
DEBUG POP3: mail.pop3s.finalizecleanclose: false
DEBUG POP3: mail.pop3s.apop.enable: false
DEBUG POP3: mail.pop3s.disablecapa: false
DEBUG POP3: connecting to host "outlook.office365.com", port 995, isSSL true
+OK The Microsoft Exchange POP3 service is ready. 
CAPA
+OK
TOP
UIDL
SASL PLAIN
USER
.
DEBUG POP3: authentication command trace suppressed
DEBUG POP3: authentication command failed
QUIT
+OK Microsoft Exchange Server POP3 server signing off.
```
やはり認証で失敗している。
そこでネットで情報を漁ると、メールアドレスではなくて@より前で認証しないとダメとか、それだとドメインが判らないだろうからoutlook.office365.comの先でプロキシ?かリダイレクト?されているExchangeサーバに直接接続しないとダメじゃないかとか、などなど半日程度試行錯誤を繰り返しました。

## こたえ
Exchange管理コンソールでユーザのメール接続設定でIMAPやPOP3が無効になってました。
上記の直接Exchangeサーバに接続しなきゃって時に、管理者の人にお願いして管理コンソールにアクセスさせてもらいました。直接は結局TCP/IPが通らずダメでしたが、この時にみた[ドキュメント](https://docs.microsoft.com/ja-jp/exchange/clients-and-mobile-in-exchange-online/pop3-and-imap4/pop3-and-imap4)に「既定では、POP3 と IMAP4 は Exchange Online のすべてのユーザーに対して有効になっています。」だがユーザ毎に無効化できるってことで確認してみたら、まんまと無効化されてました。
そこで有効化して、しばらくすると(5～10分)、すんなり動きました!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387890/c247b3e9-38bc-e2d6-ef55-f2e93928e500.png)