From 51d9bdd61733082d79a7017b7114ea87550ec293 Mon Sep 17 00:00:00 2001 From: dankito Date: Sat, 30 Nov 2024 21:05:59 +0100 Subject: [PATCH] Implemented checkCredentials() --- .../codinux/invoicing/email/EmailsFetcher.kt | 43 ++++++++++++++++++- .../invoicing/email/FetchEmailsOptions.kt | 2 + .../email/model/CheckCredentialsResult.kt | 18 ++++++++ .../codinux/invoicing/util/ExceptionHelper.kt | 17 ++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/model/CheckCredentialsResult.kt create mode 100644 e-invoice-domain/src/main/kotlin/net/codinux/invoicing/util/ExceptionHelper.kt diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/EmailsFetcher.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/EmailsFetcher.kt index 50a73a9..7d9c76a 100644 --- a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/EmailsFetcher.kt +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/EmailsFetcher.kt @@ -12,10 +12,14 @@ import net.codinux.invoicing.model.Invoice import net.codinux.invoicing.pdf.PdfInvoiceData import net.codinux.invoicing.pdf.PdfInvoiceDataExtractor import net.codinux.invoicing.reader.EInvoiceReader +import net.codinux.invoicing.util.ExceptionHelper import net.codinux.log.logger import org.eclipse.angus.mail.imap.IMAPFolder import org.eclipse.angus.mail.imap.IMAPMessage +import org.eclipse.angus.mail.util.MailConnectException import java.io.File +import java.net.ConnectException +import java.net.UnknownHostException import java.time.Instant import java.util.* import java.util.concurrent.Executors @@ -24,7 +28,8 @@ import kotlin.math.max open class EmailsFetcher( protected open val eInvoiceReader: EInvoiceReader = EInvoiceReader(), protected open val pdfInvoiceDataExtractor: PdfInvoiceDataExtractor = PdfInvoiceDataExtractor(), - protected open val coroutineDispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(max(24, Runtime.getRuntime().availableProcessors() * 4)).asCoroutineDispatcher() + protected open val coroutineDispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(max(24, Runtime.getRuntime().availableProcessors() * 4)).asCoroutineDispatcher(), + protected open val exceptionHelper: ExceptionHelper = ExceptionHelper() ) { protected data class MessagePart( @@ -43,6 +48,38 @@ open class EmailsFetcher( protected val log by logger() + open fun checkCredentials(account: EmailAccount): CheckCredentialsResult { + try { + val status = connect(account, FetchEmailsOptions(showDebugOutputOnConsole = true)) + + close(status) + + return CheckCredentialsResult.Ok + } catch (e: Throwable) { + log.info(e) { "Could not connect to account '$account'" } + return mapConnectResultError(e) + } + } + + protected open fun mapConnectResultError(exception: Throwable): CheckCredentialsResult { + if (exception is AuthenticationFailedException) { + return CheckCredentialsResult.WrongUsername + } else if (exception is MailConnectException) { + val innerInnerException = exceptionHelper.getInnerException(exception, 1) + + if (innerInnerException is UnknownHostException) { + return CheckCredentialsResult.InvalidImapServerAddress + } else if (innerInnerException is ConnectException) { + return CheckCredentialsResult.InvalidImapServerPort + } + } else if (exception is MessagingException) { // MessagingException is derived from MailConnectException, so place after MailConnectException + return CheckCredentialsResult.WrongPassword + } + + return CheckCredentialsResult.UnknownError // fallback for cases i am not aware of + } + + open fun listenForNewEmails(account: EmailAccount, options: ListenForNewMailsOptions) = runBlocking { try { val status = connect(account, options) @@ -336,6 +373,8 @@ open class EmailsFetcher( val properties = mapAccountToJavaMailProperties(account, options) val session = Session.getInstance(properties) + session.debug = options.showDebugOutputOnConsole + val store = session.getStore("imap") store.connect(account.serverAddress, account.username, account.password) @@ -345,7 +384,7 @@ open class EmailsFetcher( return FetchEmailsStatus(account, store, folder, options) } - protected open fun mapAccountToJavaMailProperties(account: EmailAccount, options: FetchEmailsOptions) = Properties().apply { + protected open fun mapAccountToJavaMailProperties(account: EmailAccount, options: FetchEmailsOptions = FetchEmailsOptions()) = Properties().apply { // the documentation of all properties can be found here: https://javaee.github.io/javamail/docs/api/com/sun/mail/imap/package-summary.html put("mail.store.protocol", "imap") diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/FetchEmailsOptions.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/FetchEmailsOptions.kt index b982636..fc897e4 100644 --- a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/FetchEmailsOptions.kt +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/FetchEmailsOptions.kt @@ -29,6 +29,8 @@ open class FetchEmailsOptions( val emailFolderName: String = "INBOX", val connectTimeoutSeconds: Int = 5, + val showDebugOutputOnConsole: Boolean = false, + val onError: ((FetchEmailError) -> Unit)? = null, val onEmailReceived: ((Email) -> Unit)? = null ) { diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/model/CheckCredentialsResult.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/model/CheckCredentialsResult.kt new file mode 100644 index 0000000..95cb538 --- /dev/null +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/email/model/CheckCredentialsResult.kt @@ -0,0 +1,18 @@ +package net.codinux.invoicing.email.model + + +enum class CheckCredentialsResult { + + Ok, + + WrongUsername, + + WrongPassword, + + InvalidImapServerAddress, + + InvalidImapServerPort, + + UnknownError + +} \ No newline at end of file diff --git a/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/util/ExceptionHelper.kt b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/util/ExceptionHelper.kt new file mode 100644 index 0000000..dd1c9be --- /dev/null +++ b/e-invoice-domain/src/main/kotlin/net/codinux/invoicing/util/ExceptionHelper.kt @@ -0,0 +1,17 @@ +package net.codinux.invoicing.util + +open class ExceptionHelper { + + open fun getInnerException(exception: Exception, maxDepth: Int = 3): Exception { + var innerException = exception + var depth = 0 + + while(innerException.cause is Exception && depth < maxDepth) { + innerException = innerException.cause as Exception + depth++ + } + + return innerException + } + +} \ No newline at end of file