package com.alexanderlogistics; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import java.util.Properties; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.eclipse.angus.mail.imap.IMAPFolder; import org.imixs.archive.importer.mail.IMAPImportHelper; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.mail.Address; import jakarta.mail.BodyPart; import jakarta.mail.Folder; import jakarta.mail.Message; import jakarta.mail.MessagingException; import jakarta.mail.Multipart; import jakarta.mail.Part; import jakarta.mail.Session; import jakarta.mail.Store; import jakarta.mail.internet.MimeBodyPart; import jakarta.mail.internet.MimeUtility; /** * This test shows how to access outlook via oauth2 * * See: https://www.java-forum.org/thema/javamail-mit-oauth2-an-o365.199133/ * * @author rsoika * */ public class TestIMAPOutlookOAuth { private static final String TENANT_ID = "51e2f038-0a96-41be-801d-bb4118aa018e"; private static final String CLIENT_ID = "39e9eec5-6939-47dc-9729-3438a9898e63"; // private static final String CLIENT_SECRET = // "A6x8Q~EWVUyt~gfyTHtIJf4ig.F9tajUpEEXMaCL"; private static final String CLIENT_SECRET = "0lG8Q~UigK0f7n-cR8SSiSXhVChkBD3Qm1CqvbID"; /* * Neue Daten * * Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ Geheime ID: * bcaa558a-3300-4498-94e1-2267f418afd5 */ @Test public void TestMailboxImixs() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); assertNotNull(token); store = connect(token, "imixs@alexander-logistics.com"); assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); } } catch (IOException e) { e.printStackTrace(); fail(); } catch (MessagingException e) { e.printStackTrace(); fail(); } } /** * Debug reading first mail.... */ @Test @Disabled public void TestMailboxImixsDebug() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); assertNotNull(token); store = connect(token, "imixs@alexander-logistics.com"); assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); for (Message message : messages) { Address[] fromAddress = null; String subject = null; // if we can not open the mail (messaging Exception) than we simply skipp this // mail see Issue #175 try { System.out.println("...try to open first message..."); fromAddress = message.getFrom(); subject = message.getSubject(); } catch (MessagingException me) { System.out.println("...Failed to read message from inbox: " + me.getMessage()); fail(); } System.out.println("......received mail from: " + fromAddress[0].toString()); System.out.println("......subject = " + subject); Object contentObject = message.getContent(); if (contentObject instanceof Multipart) { System.out.println("......found multipart"); // here we are save to cast the content to Mulipart Multipart multiPart = (Multipart) message.getContent(); System.out.println("......multipartcount = " + multiPart.getCount()); for (int i = 0; i < multiPart.getCount(); i++) { BodyPart bodyPart = multiPart.getBodyPart(i); if (bodyPart instanceof MimeBodyPart) { MimeBodyPart mimeBodyPart = (MimeBodyPart) multiPart.getBodyPart(i); if (Part.ATTACHMENT.equalsIgnoreCase(mimeBodyPart.getDisposition())) { String fileName = mimeBodyPart.getFileName(); if (fileName == null) { System.out.println("...skip detaching file, because of missing filename"); continue; // skip this attachment } // decode filename (issue #202) fileName = MimeUtility.decodeText(fileName); System.out.println("... filename = " + fileName); // detach only add PDF files? // add this attachment InputStream input = mimeBodyPart.getInputStream(); byte[] content = IMAPImportHelper.readAllBytes(input); String contentType = mimeBodyPart.getContentType(); System.out.println("mimetype=" + contentType); System.out.println("content size=" + content.length); } } } } break; } } } catch (IOException e) { e.printStackTrace(); fail(); } catch (MessagingException e) { e.printStackTrace(); fail(); } } @Test public void TestMailboxImixsBHV() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); assertNotNull(token); store = connect(token, "Imixsbhv@alexander-logistics.com"); assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); } } catch (IOException e) { e.printStackTrace(); fail(); } catch (MessagingException e) { e.printStackTrace(); fail(); } } @Test @Disabled public void TestMailboxImixsPL() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); assertNotNull(token); store = connect(token, "imixspl@alexander-logistics.com"); assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); System.out.println("...read message subjects..."); for (Message message : messages) { Address[] fromAddress = null; String subject = null; // if we can not open the mail (messaging Exception) than we simply skipp this // mail see Issue #175 try { fromAddress = message.getFrom(); subject = message.getSubject(); } catch (MessagingException me) { System.out.println("...Failed to read message from inbox: " + me.getMessage()); fail(); } System.out.println("......received mail from: " + fromAddress[0].toString()); System.out.println("......subject = " + subject); Object contentObject = message.getContent(); if (contentObject instanceof Multipart) { System.out.println("......multipart objects found"); // here we are save to cast the content to Mulipart Multipart multiPart = (Multipart) message.getContent(); for (int i = 0; i < multiPart.getCount(); i++) { BodyPart bodyPart = multiPart.getBodyPart(i); if (bodyPart instanceof MimeBodyPart) { MimeBodyPart mimeBodyPart = (MimeBodyPart) multiPart.getBodyPart(i); if (Part.ATTACHMENT.equalsIgnoreCase(mimeBodyPart.getDisposition())) { String fileName = mimeBodyPart.getFileName(); if (fileName == null) { System.out.println("...skip detaching file, because of missing filename"); continue; // skip this attachment } // add this attachment InputStream input = mimeBodyPart.getInputStream(); byte[] content = IMAPImportHelper.readAllBytes(input); String contentType = mimeBodyPart.getContentType(); System.out .println("...Attachment '" + fileName + "' is ok - size=" + content.length); } } } } } System.out.println("...completed!"); } } catch (IOException e) { e.printStackTrace(); fail(); } catch (MessagingException e) { e.printStackTrace(); fail(); } } @Test public void TestMailboxImixsDWC() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); assertNotNull(token); store = connect(token, "Imixsae@alexander-logistics.com"); assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); } } catch (IOException e) { e.printStackTrace(); fail(); } catch (MessagingException e) { e.printStackTrace(); fail(); } } /** * This method returns a connection to an javax.mail.store */ public Store connect(String oauth2AccessToken, String userEmailId) { try { String host = "outlook.office365.com"; String port = "993"; Store store = null; String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory"; Properties props = new Properties(); props.put("mail.imaps.ssl.enable", "true"); props.put("mail.imaps.sasl.enable", "true"); props.put("mail.imaps.port", port); props.put("mail.imaps.auth.mechanisms", "XOAUTH2"); props.put("mail.imaps.sasl.mechanisms", "XOAUTH2"); props.put("mail.imaps.auth.login.disable", "true"); props.put("mail.imaps.auth.plain.disable", "true"); props.setProperty("mail.imaps.socketFactory.class", SSL_FACTORY); props.setProperty("mail.imaps.socketFactory.fallback", "false"); props.setProperty("mail.imaps.socketFactory.port", port); props.setProperty("mail.imaps.starttls.enable", "true"); // optional set imap user? // props.put("mail.imap.user", userEmailId); props.put("mail.imaps.auth.plain.disable", "true"); props.put("mail.debug", "true"); props.put("mail.debug.auth", "true"); Session session = Session.getInstance(props); session.setDebug(true); store = session.getStore("imaps"); System.out.println("OAUTH2 IMAP trying to connect with system properties to Host:" + host + ", Port: " + port + ", userEmailId: " + userEmailId + ", AccessToken: " + oauth2AccessToken); store.connect(host, userEmailId, oauth2AccessToken); if (store.isConnected()) { System.out.println("Connection Established using imap protocol successfully !"); return store; } System.out.println("Store.Connect failed!"); } catch (Exception e) { System.out.println("Store.Connect failed with the errror: " + e.getMessage()); StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); System.out.println(exceptionAsString); fail(); } return null; } /** * Helper method to receive a Microsoft access Token * * https://jwt.ms/ * * "OAuth2": { * * "Authority": * https://login.microsoftonline.com/51e2f038-0a96-41be-801d-bb4118aa018e/v2.0, * * "ClientId": "39e9eec5-6939-47dc-9729-3438a9898e63", * * "ClientSecret": "............." * * } * * * @return * @throws IOException * @throws ClientProtocolException */ public String getAuthToken() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost loginPost = new HttpPost("https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/v2.0/token"); String scopes = "https://outlook.office365.com/.default"; // scopes="https://outlook.office365.com/IMAP.AccessAsUser.All"; // scopes="https://graph.microsoft.com/.default"; // scopes="openid profile offline_access https://outlook.office365.com/SMTP.Send // https://outlook.office365.com/POP.AccessAsUser.All // https://outlook.office365.com/IMAP.AccessAsUser.All"; String encodedBody = "client_id=" + CLIENT_ID + "&scope=" + scopes + "&client_secret=" + CLIENT_SECRET + "&grant_type=client_credentials"; loginPost.setEntity(new StringEntity(encodedBody, ContentType.APPLICATION_FORM_URLENCODED)); loginPost.addHeader(new BasicHeader("cache-control", "no-cache")); CloseableHttpResponse loginResponse = client.execute(loginPost); InputStream inputStream = loginResponse.getEntity().getContent(); byte[] response = readAllBytes(inputStream); ObjectMapper objectMapper = new ObjectMapper(); JavaType type = objectMapper.constructType( objectMapper.getTypeFactory().constructParametricType(Map.class, String.class, String.class)); Map parsed = new ObjectMapper().readValue(response, type); String result = parsed.get("access_token"); System.out.println("....access token = " + result); return result; } /** * Helper method to read bytes from strem * * @param inputStream * @return * @throws IOException */ private static byte[] readAllBytes(InputStream inputStream) throws IOException { final int bufLen = 4 * 0x400; // 4KB byte[] buf = new byte[bufLen]; int readLen; IOException exception = null; try { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) outputStream.write(buf, 0, readLen); return outputStream.toByteArray(); } } catch (IOException e) { exception = e; throw e; } finally { if (exception == null) inputStream.close(); else try { inputStream.close(); } catch (IOException e) { exception.addSuppressed(e); } } } }