IMAP List extension JAVA - java

I'm building a tool that need access to mail specific folders (e.g. '[Gmail]/Trash', '[Gmail]/Sent'). It seems that the names are localized with respect to the user localization settings, so '[Gmail]/Trash' show as '[Gmail]/Papelera' to Spanish users for example.
I read about XLIST command but now is deprecated in favor of the IMAP LIST Extension (https://developers.google.com/gmail/imap_extensions#special-use_extension_of_the_list_command).
I tried to do it that way javax.mail.Folder.list("\Trash") but nothing is returned.
How can I use the IMAP List extension in JAVA?
PS: Using several email providers, not just Gmail.

As Bill Shannon said, You can use Gmail attributes to get special folders like Trash. But this will work only with Gmail.
javax.mail.Folder[] folders = store.getDefaultFolder().list("*");
If you print this, it should look like the following as per Gmail
a004 LIST "" "*"
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\Noselect \HasChildren) "/" "[Gmail]"
* LIST (\HasNoChildren \All) "/" "[Gmail]/All Mail"
* LIST (\HasNoChildren \Drafts) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\HasNoChildren \Flagged) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
a004 OK Success
Once you have the folders with you, you can iterate for the attribute you are looking for.
For [Gmail]/All Mail, mailFolder = "\\All". similarly for [Gmail]/Trash it will be mailFolder = "\\Trash"
private static IMAPFolder getLocalisedFolder(IMAPStore store, String mailFolder) throws MessagingException {
Folder[] folders = store.getDefaultFolder().list("*");
for (Folder folder : folders) {
IMAPFolder imapFolder = (IMAPFolder) folder;
for (String attribute : imapFolder.getAttributes()) {
if (mailFolder.equals(attribute)) {
return imapFolder;
}
}
}
return null;
}

This should help:
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
try {
Session session = Session.getDefaultInstance(props, null);
javax.mail.Store store = session.getStore("imaps");
store.connect("imap.gmail.com", "myemail#gmail.com", "mypassword");
javax.mail.Folder[] folders = store.getDefaultFolder().list("*");
for (javax.mail.Folder folder : folders) {
if ((folder.getType() & javax.mail.Folder.HOLDS_MESSAGES) != 0) {
System.out.println("foldername->"+folder.getFullName() + " folder msg count->" + folder.getMessageCount());
}
}
} catch (MessagingException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}

You say you tried this:
javax.mail.Folder.list("\Trash")
Try without the slash:
javax.mail.Folder.list("Trash")
See how it goes. Now that's assuming that the folder is actually called "Trash". If it's localised in some other language, then you probably need to list all folders ("*"), iterate through them one by one, and find the one that gave you the \Trash attribute. I'm not very familiar with JavaMail so I don't know whether/how it gives you back the folder attributes.

maybe you can check (once you have a Folder folder) if
if ((folder.getType() & Folder.HOLDS_MESSAGES) != 0) {
and then check if messages in the folder have this flag:
Flags.Flag.DELETED
if so, that would mean this is the Trash? I don't remmeber if when a msg is deleted it is moved to the Trash or it can remain in the folder...if it is moved, that could make the trick.

Gmail no longer requires the use of the XLIST command. Gmail returns attributes with the regular IMAP LIST command that indicate the use of the localized mailboxes. You can access these attributes using the IMAPFolder.getAttributes method.
I'm afraid that doesn't help you if you have another IMAP server that only returns this information with XLIST.

Related

Downloading attachments from unseen messages

I work on university project in java. I have to download attachments from new emails using GMAIL API.
I successfully connected to gmail account using OAuth 2.0 authorization.
private static final List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
I tried to get unseen mails using
ListMessagesResponse listMessageResponse = service.users().messages().list(user).setQ("is:unseen").execute();
listMessageResponse is not null but when I call method .getResultSizeEstimate() it returns 0
also I tried to convert listMessageResponse to List < Message > (I guess this is more usable) using
List<Message> list = listMessageResponse.getMessages();
But list launches NullPointerException
Then tried to get each attachment with
for(Message m : list) {
List<MessagePart> part = m.getPayload().getParts();
for(MessagePart p: part) {
if(p.getFilename()!=null && p.getFilename().length()>0) {
System.out.println(p.getFilename()); // Just to check attachment filename
}
}
}
Is my approach correct (if not how to fix it) and how should I download those attachments.
EDIT 1:
Fixed q parameter, I mistakenly wrote is:unseen instead of is:unread.
Now app reaches unread mails successfully.
(For example there was two unread mails and both successfully reached, I can get theirs IDs easy).
Now this part trows NullPointerException
List<MessagePart> part = m.getPayload().getParts();
Both messages have attachments and m is not null (I get ID with .getID())
Any ideas how to overcome this and download attachment?
EDIT 2:
Attachments Downloading part
for(MessagePart p : parts) {
if ((p.getFilename() != null && p.getFilename().length() > 0)) {
String filename = p.getFilename();
String attId = p.getBody().getAttachmentId();
MessagePartBody attachPart;
FileOutputStream fileOutFile = null;
try {
attachPart = service.users().messages().attachments().get("me", p.getPartId(), attId).execute();
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
fileOutFile = new FileOutputStream(filename); // Or any other dir
fileOutFile.write(fileByteArray);
fileOutFile.close();
}catch (IOException e) {
System.out.println("IO Exception processing attachment: " + filename);
} finally {
if (fileOutFile != null) {
try {
fileOutFile.close();
} catch (IOException e) {
// probably doesn't matter
}
}
}
}
}
Downloading working like charm, tested app with different type of emails.
Only thing left is to change label of unread message (that was reached by app) to read. Any tips how to do it?
And one tiny question:
I want this app to fetch mails on every 10 minutes using TimerTask abstract class. Is there need for manual "closing" of connection with gmail or that's done automatically after run() method iteration ends?
#Override
public void run(){
// Some fancy code
service.close(); // Something like that if even exists
}
I don't think ListMessagesResponse ever becomes null. Even if there are no messages that match your query, at least resultSizeEstimate will get populated in the resulting response: see Users.messages: list > Response.
I think you are using the correct approach, just that there is no message that matches your query. Actually, I never saw is:unseen before. Did you mean is:unread instead?
Update:
When using Users.messages: list only the id and the threadId of each message is populated, so you cannot access the message payload. In order to get the full message resource, you have to use Users.messages: get instead, as you can see in the referenced link:
Note that each message resource contains only an id and a threadId. Additional message details can be fetched using the messages.get method.
So in this case, after getting the list of messages, you have to iterate through the list, and do the following for each message in the list:
Get the message id via m.getId().
Once you have retrieved the message id, use it to call Gmail.Users.Messages.Get and get the full message resource. The retrieved message should have all fields populated, including payload, and you should be able to access the corresponding attachments.
Code sample:
List<Message> list = listMessageResponse.getMessages();
for(Message m : list) {
Message message = service.users().messages().get(user, m.getId()).execute();
List<MessagePart> part = message.getPayload().getParts();
// Rest of code
}
Reference:
Class ListMessagesResponse
Users.messages: list > Response

How to search file inside a specific folder in google API v3

As i am using v3 of google api,So instead of using parent and chidren list i have to use fileList, So now i want to search list of file inside a specific folder.
So someone can suggest me what to do?
Here is the code i am using to search the file :
private String searchFile(String mimeType,String fileName) throws IOException{
Drive driveService = getDriveService();
String fileId = null;
String pageToken = null;
do {
FileList result = driveService.files().list()
.setQ(mimeType)
.setSpaces("drive")
.setFields("nextPageToken, files(id, name)")
.setPageToken(pageToken)
.execute();
for(File f: result.getFiles()) {
System.out.printf("Found file: %s (%s)\n",
f.getName(), f.getId());
if(f.getName().equals(fileName)){
//fileFlag++;
fileId = f.getId();
}
}
pageToken = result.getNextPageToken();
} while (pageToken != null);
return fileId;
}
But in this method it giving me all the files that are generated which i don't want.I want to create a FileList which will give file inside a specific folder.
It is now possible to do it with the term parents in q parameter in drives:list. For example, if you want to find all spreadsheets in a folder with id folder_id you can do so using the following q parameter (I am using python in my example):
q="mimeType='application/vnd.google-apps.spreadsheet' and parents in '{}'".format(folder_id)
Remember that you should find out the id of the folder files inside of which you are looking for. You can do this using the same drives:list.
More information on drives:list method can be seen here, and you can read more about other terms you can put to q parameter here.
To search in a specific directory you have to specify the following:
q : name = '2021' and mimeType = 'application/vnd.google-apps.folder' and '1fJ9TFZOe8G9PUMfC2Ts06sRnEPJQo7zG' in parents
This examples search a folder called "2021" into folder with 1fJ9TFZOe8G9PUMfC2Ts06sRnEPJQo7zG
In my case, I'm writing a code in c++ and the request url would be:
string url = "https://www.googleapis.com/drive/v3/files?q=name+%3d+%272021%27+and+mimeType+%3d+%27application/vnd.google-apps.folder%27+and+trashed+%3d+false+and+%271fJ9TFZOe8G9PUMfC2Ts06sRnEPJQo7zG%27+in+parents";
Searching files by folder name is not yet supported. It's been requested in this google forum but so far, nothing yet. However, try to look for other alternative search filters available in Search for Files.
Be creative. For example make sure the files within a certain folder contains a unique keyword which you can then query using
fullText contains 'my_unique_keyword'
You can use this method to search the files from google drive:
Files.List request = this.driveService.files().list();
noOfRecords = 100;
request.setPageSize(noOfRecords);
request.setPageToken(nextPageToken);
String searchQuery = "(name contains 'Hello')";
if (StringUtils.isNotBlank(searchQuery)) {
request.setQ(searchQuery);
}
request.execute();

View.getView() returns null

I'm trying to find my folder or view in my database. Which named Team Documents this folder has some filter option like By Date, By Category. But this returns me a null even the folder already exists.
String dbServer = "d23dbm95/23/A/IBM", dbFileName = "dbom\\farizan\\stsklb1.nsf";
public void runNotes()
{
Session session = null;
Database db = null;
View view = null;
Document doc = null;
try
{
NotesThread.sinitThread();
session = NotesFactory.createSession();
System.out.println("User = " + session.getUserName());
db = session.getDatabase(dbServer, dbFileName);
if(db.isOpen())
{
System.out.println("Title "+db.getTitle());
view = db.getView("Team Documents \\ By Date");
if(view == null)
{
System.out.println("still null");
}
}
}
catch(NotesException e)
{
e.printStackTrace();
}
}
I tried also to fill my getView() method like Team Documents. But still returns a null. Any approach to this problem?
While it would have been more helpful if you had included a link to a screenshot of your Domino Designer client's folder list, my best guess is that you have two folders, not one folder with "filter options". Also, my guess is that "Team Documents" is not actually a folder; it's just a prefix on the folder names that makes them appear to be nested in a parent folder.
If that's the case, you would need
iew = db.getView("Team Documents\\By Category");
Or
iew = db.getView("Team Documents\\By Date");
Note: No spaces before & after the backslashes.
If my assumptions above are not correct, then my suggestion would be to assign alias names to the folders in Domino Designer and use the aliases instead of the display names in your code. Frankly, that's always a good practice, because it allows your code to continue working even if you decide to change the display names.

Lotus Notes - Mail Document - Principal/From,INetFrom, SentTime, ReceivedTime fields

I have a requirement to fetch the SenderName,SenderEmail,ToNames,ToEmails,CCNames,CcEmails from a lotus notes document instance.
Issue1
Looking into lotus.domino.Document API I found out the method getItems. When I write the elements to the system.out values for SenderEmail, ToEmails and CcEmails can be found.
However values for SenderName(a.k.a From), ToNames cannot be derived that easily.
The values seems to be using an common name format. For example check check my system.output below.
Principal = "CN=Amaw Scritz/O=fictive"
$MessageID = "<OF0FF3779B.36590F8A-ON80257D15.001DBC47-65257D15.001DC804#LocalDomain>"
INetFrom = "AmawScritz#fictive.com"
Recipients = "CN=Girl1/O=fictive#fictive"
MailOptions = "0"
SaveOptions = "1"
From = "CN=Amaw Scritz/O=fictive"
AltFrom = "CN=Amaw Scritz/O=fictive"
SendTo = "CN=Girl1/O=fictive#fictive"
CopyTo = "CN=Girl2/O=fictive#fictive"
BlindCopyTo = ""
InetSendTo = "Girl1#fictive.com"
InetCopyTo = "Girl2#fictive.com"
$Abstract = "sasdasda"
$UpdatedBy = "CN=Amaw Scritz/O=fictive"
Body = "Hello World"
The question is how can I get 'Amaw Scritz' from the common name 'CN=Amaw Scritz/O=fictive'. Is there any look up mechanism that can be used. (I would prefer to have a option other than doing a substring of the common name)
Issue2
is it possible to retrieve SentTime and ReceivedTime from mail document instance?
I know that there are two methods called getCreated and getLastModified. getCreated can be loosely associated with the SentTime and getLastModified can be loosely associated with ReceivedTime. Are there are other ways to get times for SentTime and ReceivedTime.
Issue3
How can one distinguish whether a mail document is a Sent mail or a Received Mail?
Issue1
You can use Name class.
Here example from this link:
import lotus.domino.*;
public class JavaAgent extends AgentBase {
public void NotesMain() {
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
// (Your code goes here)
// Create a hierarchical name
Name nam = session.createName(
"CN=John B Goode/OU=Sales/OU=East/O=Acme/C=US");
// Returns:
// John B Goode
// John B Goode/Sales/East/Acme/US
// CN=John B Goode/OU=Sales/OU=East/O=Acme/C=US
System.out.println(nam.getCommon());
System.out.println(nam.getAbbreviated());
System.out.println(nam.getCanonical());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Issue2
Use values of PostedDate field and DeliveredDate field of mail document.
Issue3
Check that $Inbox folder contains your mail document. Or take a look at Dave Delay answer.
I agree with #nempoBu4 on Issues 1 and 2. I disagree with the answer to Issue 3. A received message can be removed from the inbox, so checking $Inbox doesn't help you distinguish between sent and received messages.
Assuming you have the document open, the best approach is to check two items. Sent and received messages both have a PostedDate item, but only a received message has a DeliveredDate item. Incidentally, a draft message has neither PostedDate or DeliveredDate.

How to use JavaMail to read emails with multiple labels from Google Mail (Gmail)?

In Google Mail, I would like to get messages that has been assigned multiple labels. For example, if in the Inbox we have three emails:
Email_1 with Label_A and Label_B
Email_2 with Label_A and Label_B
Email_3 with Label_A and Label_C
then I want to select those with Label_A and Label_B at the same time, which are Email_1 and Email_2. Currently the following codes work for one-label situation, but is there any way to do it with more than one label? Thanks.
Properties props = System.getProperties();
Session session = Session.getInstance(props, null);
Store store = session.getStore("imaps");
store.connect("imap.gmail.com", -1, "abc#def.com", "password");
Folder folder = store.getDefaultFolder();
folder = folder.getFolder("Label_A");
folder.open(Folder.READ_WRITE);
int totalMessages = folder.getMessageCount();
int newMessages = folder.getNewMessageCount();
System.out.println("Total messages = " + totalMessages);
System.out.println("New messages = " + newMessages);
You should be able to do something like this:
private Store store;
private Folder Label_A;
private Folder Label_B;
...
Label_A = store.getFolder("Label_A");
Label_B = store.getFolder("Label_B");
I ended up having to write my own raw IMAP commands to allow javamail to use Gmail's IMAP extensions. Then this works.
Access to Gmail labels: X-GM-LABELS
You then issue: folder.doCommand() with your command.

Categories