Getting Gmail Emails with Attachments usig Java IMAP - java

I am trying to get emails from Gmail using Java. I using the java-gmail-imap library. When I try to get message.getContent(), it is always giving Inputstream. It doesn't provide me enough information to parse through the Part. My code looks like this. Please let me know what I am missing?
IMAPStore imapStore = OAuth2Authenticator.connectToImap(
"imap.gmail.com", 993, email, token, false);
Folder folder = imapStore.getFolder("Inbox");
if (folder.isOpen()) {
if ((folder.getMode() & Folder.READ_WRITE) != 0) {
folder.close(false);
folder.open(Folder.READ_ONLY);
}
} else {
try {
folder.open(Folder.READ_ONLY);
} catch (Exception e) {
e.printStackTrace();
}
}
Message[] messages = null;
SearchTerm newerThan = new ReceivedDateTerm(ComparisonTerm.GT, lastDate);
messages = folder.search(newerThan);
FetchProfile fp = new FetchProfile();
fp.add("X-GM-MSGID");
fp.add(UIDFolder.FetchProfileItem.UID);
fp.add(IMAPFolder.FetchProfileItem.X_GM_MSGID);
//fp.add(FetchProfile.Item.ENVELOPE);
//fp.add(FetchProfileItem.FLAGS);
//fp.add(FetchProfileItem.CONTENT_INFO);
// fp.add("X-mailer");
folder.fetch(messages, fp);
for (Message message : messages) {
if (message != null) {
Object o = message.getContent();
if (o instanceof String) {
logger.info("Message content is string");
} else if (o instanceof Multipart) {
logger.info("Message content is multipart");
} else if (o instanceof Message) {
logger.info("Nested Message");
} else if (o instanceof InputStream) {
logger.info("Message content is inputstream");
}
}
}
Edit:
Issue is fixed by adding mailcap
CommandMap.setDefaultCommandMap(new com.google.code.javax.activation.MailcapCommandMap(this.getClass().getClassLoader().getResourceAsStream("mailcap")));

Related

Extracting inline images coming in mail body using java Mail API

I have written two methods one for attachments and one email body content. I need to extract the images from email body. These two methods are working fine but when images are coming in email body it is should be saved in database. So it can be used later.
For attachmnents:-
public static List getAttachments(MimeMultipart multipart, List existingAttachments) {
if (multipart != null) {
try {
if (existingAttachments == null) {
existingAttachments = new ArrayList<MimeBodyPart>();
}
for (int i = 0; i < multipart.getCount(); i++) {
if (multipart.getBodyPart(i) instanceof MimeBodyPart) {
MimeBodyPart currentPart = (MimeBodyPart) multipart.getBodyPart(i);
if (Part.ATTACHMENT.equalsIgnoreCase(currentPart.getDisposition())) {
if (!existingAttachments.contains(currentPart)) {
existingAttachments.add(currentPart);
System.out.println(currentPart.getFileName());
}
} else if (currentPart.getContent() instanceof MimeMultipart) {
existingAttachments = getAttachments((MimeMultipart) currentPart.getContent(), existingAttachments);
}
}
}
} catch (MessagingException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error(ex.getMessage());
} catch (IOException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error(ex.getMessage());
}
}
return existingAttachments;
}
for email Body ContentThis method is extracting email body content
public static String getContent(MimeMultipart multipart) {
String emailContent = null;
if (multipart != null) {
try {
for (int i = 0; i < multipart.getCount(); i++) {
if (multipart.getBodyPart(i) instanceof MimeBodyPart) {
MimeBodyPart currentPart = (MimeBodyPart) multipart.getBodyPart(i);
if (Part.INLINE.equalsIgnoreCase(currentPart.getDisposition())) {
LoggerFactory.getLogger(EmailUtil.class.getName()).info("Content dispo is inline");
emailContent = (String) currentPart.getContent();
} else if (currentPart.getDisposition() == null && currentPart.getContentType().toLowerCase().contains("text")) {
LoggerFactory.getLogger(EmailUtil.class.getName()).info("Content dispo is null and type is text/*");
try {
emailContent = (String) currentPart.getContent();
} catch (ClassCastException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).warn("Classcast exception caught and managed");
try {
InputStream is = currentPart.getInputStream();
emailContent = IOUtils.toString(is, currentPart.getEncoding());
Document doc=Jsoup.parse(emailContent);
Elements elements =doc.getElementsByTag("img");
System.out.println(elements);
int htmlCloseIndex = emailContent.indexOf("</html>");
emailContent = emailContent.substring(0, htmlCloseIndex);
emailContent+="</html>";
} catch (Exception e) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error("Exception rebound caught and managed, email content will not read");
//emailContent = "Unable to read email content";
e.printStackTrace();
}
}
}else if (currentPart.getDisposition() == null && currentPart.getContentType().contains("TEXT")) {
LoggerFactory.getLogger(EmailUtil.class.getName()).info("Content dispo is null and type is TEXT/*");
try {
emailContent = (String) currentPart.getContent();
} catch (ClassCastException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).warn("Classcast exception caught and managed");
try {
InputStream is = currentPart.getInputStream();
emailContent = IOUtils.toString(is, currentPart.getEncoding());
int htmlCloseIndex = emailContent.indexOf("</html>");
emailContent = emailContent.substring(0, htmlCloseIndex);
emailContent+="</html>";
} catch (Exception e) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error("Exception rebound caught and managed, email content will not read");
//emailContent = "Unable to read email content";
e.printStackTrace();
}
}
}
else if (currentPart.getContent() instanceof MimeMultipart) {
emailContent = getContent((MimeMultipart) currentPart.getContent());
}
}
}
} catch (MessagingException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error(ex.getMessage());
LoggerFactory.getLogger(EmailUtil.class.getName()).warn("email content will not read");
//emailContent = "Unable to read email content";
} catch (IOException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).error(ex.getMessage());
LoggerFactory.getLogger(EmailUtil.class.getName()).warn("email content will not read");
// emailContent = "Unable to read email content";
} catch (ClassCastException ex) {
LoggerFactory.getLogger(EmailUtil.class.getName()).warn("Classcast exception caught and managed");
// emailContent = "Unable to read email content";
}
}
return emailContent;
}
Okay one starts with the <img src="..."> tags, you already took:
Elements elements = doc.getElementsByTag("img");
An img tag for an embedded image looks like:
<img src="data:image/jpeg;base64,..." ...>
So having the src attribute do:
String src = ...
if (src.startsWith("data:")) { // Embedded image data.
int p = src.indexOf(';'); // Or better ";base64,"
if (p == -1) {
throw new IllegalArgumentException();
}
String mimeType = src.substring(5, p);
String base64Data = src.substring(p + 1 + 6 + 1); // ";base64,"
byte[] data = Base64.getDecoder().decode(base64Data);
String file = "test." + mimeType.replaceFirst("^.*/(.*)$", "$1");
Path path = Paths.get(file);
Files.write(path, data);
}

Get a message's ID using Gmail API from a list so it can be used later in automation

I currently have a quick block of code that will sort through the automation's Gmail account to find the latest message, and list its ID. How exactly can I save that ID to a separate string, so it can be used later on to get the message for comparison. Am I missing a specific line of code, or should I rewrite it in some way? Thanks.
Create a list of the messages using a query. It's going to print the ID of each message.
private List<Message> listMessage(Gmail service,
String query) throws IOException {
ListMessagesResponse response = service.users().messages().list("me").setQ(query).execute();
List<Message> messages = new ArrayList<Message>();
while (response.getMessages() != null) {
messages.addAll(response.getMessages());
if (response.getNextPageToken() != null) {
String pageToken = response.getNextPageToken();
response = service.users().messages().list("me").setQ(query)
.setPageToken(pageToken).execute();
} else {
break;
}
}
if(messages.isEmpty()) {
listMessage(service, query);
}
for (Message message : messages) { //This is going to print the ID of each message.
System.out.println(message.toPrettyString());
}
return messages;
}
This is going to find the latest one.
public void listGmailEmail() {
long unixTime = Instant.now().getEpochSecond();
try {
listMessage(service, "after: " + unixTime);
} catch (IOException ignored) { }
}
I figured it out eventually.
Get list of messages
Turn the list into a JSON
Create a method to get the message
Filter the JSON to get the message ID
Apply the message ID to the new method
Get the message
private List<Message> getMessageID(Gmail service,
String query) throws IOException {
ListMessagesResponse response = service.users().messages().list("me").setQ(query).execute();
List<Message> messages = new ArrayList<Message>();
while (response.getMessages() != null) {
messages.addAll(response.getMessages());
if (response.getNextPageToken() != null) {
String pageToken = response.getNextPageToken();
response = service.users().messages().list("me").setQ(query)
.setPageToken(pageToken).execute();
} else {
break;
}
}
if(messages.isEmpty()) {
getMessageID(service, query);
}
messageID = gson.toJson(messages);
return messages;
}
private Message getEmail(Gmail service, String userId, String messageId)
throws IOException {
Message message = service.users().messages().get(userId, messageId).execute();
email = message.toString();
return message;
}
public void getGmailEmail() {
try {
getMessageID(service, "after: " + unixTime);
messageID = messageID.split("\",")[0].substring(8);
getEmail(service,"me", messageID);
System.out.println("Email received");
emailOrThread = email;
} catch (IOException ignored) { }
}

Apache cxf attachment security

I try to get my apache cxf client to sign and encrypt attachments. As i have my solution now it does sign and encrypt message body, but it ignores attachments.
I have following code:
Map<String, Object> props = new HashMap<>();
props.put("action", "Signature Encrypt");
props.put("signaturePropFile", "client.properties");
props.put("passwordCallbackClass", "******.KeystorePasswordCallback");
props.put("user", "node1");
props.put("signatureKeyIdentifier", "DirectReference");
props.put("signatureParts",
"{Element}{http://www.w3.org/2003/05/soap-envelope}Body;" +
"{}cid:Attachments;");
props.put("encryptionParts",
"{Content}{http://www.w3.org/2003/05/soap-envelope}Body;" +
"{Element}cid:Attachments;" );
props.put("encryptionPropFile", "client.properties");
props.put("encryptionKeyIdentifier", "IssuerSerial");
props.put("encryptionKeyTransportAlgorithm",
"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);
client.getOutInterceptors().add(wss4jOut);
I'm following this example to make my code.
And {}cid:Attachments part is from this apache page.
The problem was that Apache CXF for some reason runs sign/enrypt interceptor before interceptor that adds attachments to message.
Simple workaround is to add your own WSS4J out/in interceptor ( the problem is in both ways - incoming/outgoing messages ) that adds attachments before encryption/decryption/signature(check) is done and.
Basically you can open SAAJ interceptor that adds attachments and copy paste part of code from handleMessage method to your interceptor.
For out incerceptor:
#Override
public void handleMessage(SoapMessage mc) throws Fault {
super.handleMessage(mc);
SOAPMessage soapMessage = mc.getContent(SOAPMessage.class);
if (soapMessage != null) {
if (soapMessage.countAttachments() > 0) {
if (mc.getAttachments() == null) {
mc.setAttachments(new ArrayList<Attachment>(soapMessage
.countAttachments()));
}
Iterator<AttachmentPart> it = CastUtils.cast(soapMessage.getAttachments());
while (it.hasNext()) {
AttachmentPart part = it.next();
String id = AttachmentUtil.cleanContentId(part.getContentId());
AttachmentImpl att = new AttachmentImpl(id);
try {
att.setDataHandler(part.getDataHandler());
} catch (SOAPException e) {
throw new Fault(e);
}
Iterator<MimeHeader> it2 = CastUtils.cast(part.getAllMimeHeaders());
while (it2.hasNext()) {
MimeHeader header = it2.next();
att.setHeader(header.getName(), header.getValue());
}
mc.getAttachments().add(att);
it.remove();
}
}
}
}
For in interceptor:
#Override
public void handleMessage(SoapMessage msg) throws Fault {
super.handleMessage(msg);
SOAPMessage soapMessage = msg.getContent(SOAPMessage.class);
soapMessage.removeAllAttachments();
Collection<Attachment> atts = msg.getAttachments();
if (atts != null) {
for (Attachment a : atts) {
if (a.getDataHandler().getDataSource() instanceof AttachmentDataSource) {
try {
((AttachmentDataSource) a.getDataHandler().getDataSource()).cache(msg);
} catch (IOException e) {
throw new Fault(e);
}
}
AttachmentPart ap = soapMessage.createAttachmentPart(a.getDataHandler());
Iterator<String> i = a.getHeaderNames();
while (i != null && i.hasNext()) {
String h = i.next();
String val = a.getHeader(h);
ap.addMimeHeader(h, val);
}
if (StringUtils.isEmpty(ap.getContentId())) {
ap.setContentId(a.getId());
}
soapMessage.addAttachmentPart(ap);
}
}
msg.setAttachments(Collections.<Attachment>emptyList());
msg.setContent(SOAPMessage.class, soapMessage);
}

How to parse signature on JavaMail api?

I want to get the body of a mail without its signature. This code is fetching all text type. How can I extract the text without mail signature?
public static void saveParts(Object content) throws MessagingException {
try {
if (content instanceof Multipart) {
Multipart multi = ((Multipart) content);
int parts = multi.getCount();
for (int j = 0; j < parts; ++j) {
MimeBodyPart part = (MimeBodyPart) multi.getBodyPart(j);
if (part.getContent() instanceof Multipart) {
saveParts(part.getContent());
} else {
if (part.isMimeType("text/plain")) {
System.out.println("message content : " + part.getContent());
}
}
}
}
} catch(MessagingException ex){
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
Signature is considered as a part of the mail body content. There is no separate method to fetch the signature alone. You will have to fetch the body of the mail, parse and use it as per your requirement.

read message text without getting all the content

public void getMessageById(
#PathParam("folderName") String folderName,
#PathParam("id") String id) {
MailMessage mailMessage = new MailMessage();
MimeMessage mimeMessage = null;
try {
Store store = mailSession.getStore();
store.connect("localhost", email, password);
Folder folder = store.getDefaultFolder();
folder = folder.getFolder(folderName.toUpperCase());
folder.open(Folder.READ_ONLY);
SearchTerm searchTerm = new MessageIDTerm(id);
Message[] messages = folder.search(searchTerm);
if (messages.length > 0) {
mimeMessage = (MimeMessage) messages[0];
}
if (mimeMessage != null) {
Object objRef = mimeMessage.getContent();
if (objRef != null) {
// if message content is not multipart
if (!(objRef instanceof Multipart)) {
//get message text here
System.out.println(mimeMessage.getContent().toString())
} else {
Multipart multipart = (Multipart) objRef;
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.isMimeType("text/*")) {
//get message text here
System.out.println(bodyPart.getContent()
.toString())
}
if (!Part.ATTACHMENT.equalsIgnoreCase(bodyPart
.getDisposition())) {
continue; // dealing with attachments only
}
if (bodyPart.isMimeType("image/*")) {
}
}
}
}
}
folder.close(false);
store.close();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The problem is that it take long time to fetch message text .
Is Object objRef = mimeMessage.getContent(); fetching all the content , if so is there any way to avoid fetching all the contents
Thanks for help ...
Rather than calling getContent and switching on whether it's a Multipart or not, use mimeMessage.isMimeType("multipart/") and mimeMessage.isMimeType("text/"). See the msgshow.java sample program.
Instead of fetching the text content all at once, you can use bodyPart.getInputStream to read it incrementally, which might help depending on what you're doing with it once you read it.
And of course you can use the Folder.fetch method to prefetch message metadata to speed up other parts of processing the messages. See the msgshow.java program again for an example.

Categories

Resources