Why iterating over all java mail user's folders very slow? - java

Properties props = System.getProperties();
props.put("mail.imap.connectiontimeout",5000);
Session session = Session.getInstance(props);
Store store = session.getStore("imap");
for(50K users){
//login,password changed in loop
String[] folders = {"inbox", "f1", "f2", "f3", "spam"};
store.connect(serverAddress, login + emailSuffix, password);
for (int i = 0; i < folders.length; i++) {
Folder x = store.getFolder(folders[i]);
x.open(Folder.READ_ONLY);
System.out.println("folder " + folders[i] + " of " + login);
x.getUnreadMessageCount();
x.close(false);
}
store.close();
}
I'm using same store for all connections, changed service_count in dovecot according to this answer in order to improve imap-dovecot performance but I see only first iteration and after that code hangs or does next system.out after long time.
Actually, I need to grab all old messages of all users + count all unread messages as I want to migrate from pure Java Mail to some custom format. I didn;t manage even to just iterate over all users and folders for each user because even simple store.connect hangs after 1-st iteration!
I personally think that bottleneck is my dovecot config, but it uses default limits (1000 connections) which looks good.
My I somehow improve my dovecot or connect my store only once for all users or somehow fetch all messages of all users and unreadMessagesCount of all users in other way?
PS. The only alternative to programmatic way is some bash script in maildir which whill read each message from file system and pass it to some rest which converts to my custom format) but it much more harder than Java it's too difficult to parse smptp, parse seen flags from file name and so on.
UPDATE
I found apache commons net imapclient which works very fast.
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>
My code is the following
IMAPClient client = new IMAPClient();
client.connect("localhost");
for(50K users){
client.login(login + emailSuffix, password);
for (int i = 0; i < folders.length; i++) {
System.out.println(client.select("INBOX"); //prints true, it's ok
}
}
Looks like it connects faster than java mailapi because it may
connect once to host and after that login for each user. May I somehow repeat such behavior in JavaMail API?
How may I grab messages with apache commons client? All methods return boolean or void, so it's looks like just server checking library am I right? Is it possible to somehow get useful info from imapclient?

Finally solved my problem by simple iterating over file system (I have maildir format).
I guess Java Mail API makes new dovecot auth for each user in store.connect while it should just connect once (consume dovecot auth) and after that login for each user (consume dovecot imap-login). That's why I waited 1 minute for each iteration - it's std idle for auth process in dovecot config. I'm not sure but looks so.
Apache lib works fast but it's just testing library for pinging server, checking connection and other imap operations. It returns boolean result about operations but not useful information(

Related

LDAP. Java Application without Authentication

This application will be run on clients that are already authenticated in Active Directory.
Problem: the LDAP protocol (or Active Directory settings) seem to require username and password.
Goal: query Active Directory using LDAP in Java without having to authenticate (ask for username and password).
Gist: all clients who run this application have already logged in. Thus, they are already authenticated (into)/ by Active Directory.
Now that they are logged in and have access to AD outside the application, isn't it possible to "mooch" off of the fact that they are already authenticated and run my LDAP queries in my application?
Errors: while trying to maneuver past authentication; I have become accustomed to binding errors, log4j errors; and almost everything recommended on Stack Overflow, Oracle and Apache.
Methods tried: I have tried anonymous binding, Ldap api's, nada!!
Questions:
Is it possible to query Active Directory without authentication?
Is it possible to query Active Directory by telling the server that "hey, I am already logged into AD, proceed with my queries?" without prompting the user for Username and password?
Is it possible to query active directory without authentication?
I think no, you cannot as this will violate security. Another way might be to use Single sign on utilities that lets you sign in and then they will provide you the details.
Is it possible to query active directory by telling the server that
"hey, I am already logged into AD, proceed with my queries?" without
prompting the user for Username and password?
You can try http://spnego.sourceforge.net/ or http://jcifs.samba.org/src/docs/ntlmhttpauth.html to use NTLM
The following solution (or at least a very similar one) was used to solve this question:
import com4j.Variant;
import com4j.typelibs.ado20.ClassFactory;
import com4j.typelibs.ado20._Command;
import com4j.typelibs.ado20._Connection;
import com4j.typelibs.ado20._Recordset;
public static void queryADForComputers() throws Exception {
String query = "cn,sn,givenName,department";
String filter = "(&(objectclass=user)(objectcategory=person))";
String namingContext = "OU=Desktops,OU=Workstations,OU=HO,DC=win";
_Connection conn = ClassFactory.createConnection();
conn.provider("ADsDSOObject");
conn.open("Active Directory Provider","","",-1);
_Command cmd = ClassFactory.createCommand();
cmd.activeConnection(conn);
cmd.commandText("<LDAP://" + namingContext + ">;" + filter + ";" + query + ";subTree");
_Recordset rs = cmd.execute(null, Variant.getMissing(), -1);
System.out.println("Found " + rs.recordCount() + " users/computers/whatever i was looking for");
//Then here you can use a while loop while(!rs.eof())
//in which you can get each value as rs.fields().item(i).value();
//in my case, i did rs.fields().item(i).value().toString()
//or you can check for its type and go from there.
}
I worked on this a while ago and don't currently have an active directory to test and verify. but this should get you started.

Is there a simple method to check if there are changes in a SFTP server?

My objective is to poll the SFTP server for changes. My first thought is to check if the number of files in the dir changed. Then maybe some additional checks for changes in the dir.
Currently I'm using the following:
try {
FileSystemOptions opts = new FileSystemOptions();
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 60000);
FileSystemManager manager = VFS.getManager();
FileObject remoteFile = manager.resolveFile(SFTP_URL, opts);
FileObject[] fileObjects = remoteFile.getChildren();
System.out.println(DateTime.now() + " --> total number of files: " + Objects.length);
for (FileObject fileObject : fileObjects) {
if (fileObject.getName().getBaseName().startsWith("zzzz")) {
System.out.println("found one: " + Object.getName().getBaseName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
This is using apache commons vfs2 2.2.0. It works "fine", but when the server has too many files, it takes over minutes just to get the count(currently, it takes over 2 minutes to get the count for a server that has ~10k files). Any way to get the count or other changes on the server faster?
Unfortunately there's no simple way in the SFTP protocol to get the changes. If you can have some daemon running on the server OR if the source of the new files can create/update a helper file, creation of such file with the last modification time in its name or contents can be an option.
I know the SFTP protocol fairly well, having developed commercial SFTP clients and an SFTP server (CompleteFTP), and as far as I know there's no way within the protocol to get a count of files in a directory without listing it. Some servers, such as ours, provide ways of adding custom commands to servers that you can invoke from the client, so it would be possible to add a custom command that returns the number of files in a directory. CompleteFTP also allows you to write custom file-systems so you could potentially write one that only shows files that have changed after a given timestamp when you do a listing, which might be another approach. Our server only runs on Windows though, so that might be show-stopper for you.

Different view content when accessing a Lotus Domino server

When I run the following Java code on a Lotus Domino server, I get different results depending on where the code runs.
private void doViewStuff(Session session, PrintStream out) throws NotesException {
Database db = session.getDatabase(null, "myDatabase.nsf");
View view = db.getView("myViewName");
Document doc = view.getFirstDocument();
while (doc != null) {
out.println("doc: " + doc.getUniversalID());
doc = view.getNextDocument(doc);
}
ViewEntryCollection entries = view.getAllEntries();
ViewEntry entry = entries.getFirstEntry();
while (entry != null) {
System.out.println("entry: " + entry.getColumnValues());
entry = entries.getNextEntry(entry);
}
}
When I run the code on the server as a Java agent, there are 37235 documents in the view.
When I run the code in a standalone client, there are only 37217 documents in the view, and the code is much, much slower.
Details and execution environment:
The server version is 8.5.3, the NCSO.jar I used for the client has SHA-1 d879f8992aae49a06769a564217633a9e0fbd1b6.
The database myDatabase.nsf contains about 150000 documents, each with a file attachment.
The missing documents do not appear in a block, they appear between index 10000 and 20000.
In both cases the code runs as the same user account.
What might be the reason that 18 of the documents cannot be found?
Update and Clarification
Upon further inspection, it turned out that I had indeed run the code with different user accounts, and that the inaccessible document had some Reader Names fields.
On the server I had this configuration, although I configured the agent to "Run on behalf of" CN=User Name/O=domain. It didn't matter whether I ran the agent from the Domino Console or via HTTP:
effectiveUserName=CN=User Name/O=domain
commonUserName=domino01
userName=CN=domino01/O=domain
On the client I had this configuration:
effectiveUserName=[NotesException: Not implemented]
commonUserName=User Name
userName=User Name/O=domain
And that was even though I used this code in the client:
Session session = NotesFactory.createSession("127.0.0.1", "User Name", "password");
You say that in both cases the code runs as the same user account, so I want to trust that this is true. I presume, therefore, that you have ruled out Reader Names fields as cause of the discrepancy.
In that case, have you checked the IsValid() property of the ViewEntry objects when you process them in the agent running on the server? Perhaps the NCSO.jar implementation that you are using for the client-side code is filtering out the objects where IsValid() would return false.

How to Ban / Unban IP for failed login attempts

I want to IP Ban a user if he fails to login 5 times at my website. How do I get someone's IP in Java and then ban it from my site?
Currently, my login servlet looks like this. I know the counting system should be implemented differently and I will get working on that later. For now, I just want to test out IP bans.
if(user.isValid())
{
HttpSession session = request.getSession(true);
session.setAttribute("currentSessionUser",user);
response.sendRedirect("Unit_Info.jsp");
}else{
x=x+1;
System.out.println("Failed Login " + x + "\n");
if(x==5)
response.sendRedirect("http://google.com"); //IP BAN HIM
else
response.sendRedirect("index.jsp");
}
EDIT: Now that I think about it more, what happens if someone is using a proxy and constantly changing IPs when trying out username/passwords. Is there a sure way to ban the guy, whether he is using a proxy or not?
You could use fail2ban to achieve the same. Here is the resource. Else you can have a bit in the db which sets once it crosses the number of attempts parameter. You can put those IP addresses in the blocked list of the server.

How to write java program to read new emails from any emailid

Hi I want to write a java program where I will provide my email id and password. and I want to read all new unread messages that arrived to that email id. I donot know how to write program for that.
The below program works fine for gmail. but it does not work for yahoomail because for yahoo pop3 is not configured. I want a generic code which will work for all email id.
import java.io.*;
import java.util.*;
import javax.mail.*;
public class ReadMail {
public static void main(String args[]) throws Exception {
// String host = "pop.gmail.com";
// String user = "xyz";
// String password = "12345";
// Get system properties
Properties properties = System.getProperties();
// Get the default Session object.
Session session = Session.getDefaultInstance(properties, null);
// Get a Store object that implements the specified protocol.
Store store = session.getStore("pop3s");
//Connect to the current host using the specified username and password.
store.connect(host, user, password);
//Create a Folder object corresponding to the given name.
Folder folder = store.getFolder("inbox");
// Open the Folder.
folder.open(Folder.READ_ONLY);
Message[] message = folder.getMessages();
// Display message.
for (int i = 0; i < message.length; i++) {
System.out.println("------------ Message " + (i + 1) + " ------------");
System.out.println("SentDate : " + message[i].getSentDate());
System.out.println("From : " + message[i].getFrom()[0]);
System.out.println("Subject : " + message[i].getSubject());
System.out.print("Message : ");
InputStream stream = message[i].getInputStream();
while (stream.available() != 0) {
System.out.print((char) stream.read());
}
System.out.println();
}
folder.close(true);
store.close();
}
}
You need to know more than just login-pass. Things like mail server address, mail server type, port for connections, etc.
You should probably check out Java Mail API, or Commons Email.
UPD:
You create a Session using Session.getDefaultInstance() method (which takes connection Properties object and authenticator), get a Store from this Session using Session.getStore() method, get a Folder from that store using Store.getFolder("FOLDER_NAME") method, open that Folder, using Folder.open(Folder.READ) method, and get all messages, using something like Message[] messages = inboxFolder.getMessages();
Is that what you were looking for?
UPD2:
There is simply no way to write a generic program, which will work with any mail provider, using just server path, userID and password. Because different mail servers are configured differently. They talk differen protocols (imap/pop3/pop3 ssl) on different ports. There's always some guy, who has configured his mail server to talk imap over ssl on 31337 port only, all the other ports and protocols are banned. And this guy breaks your program. So, you'll have to specify all this properties in your properties object. Look here for properties, you'll have to specify.
UPD3:
On second thought, you actually have one option. Just try connecting to the server using different protocols. If that does not help, start iterating through ports. The one that fits is your configuration. If that's really what you want.
You need the javax.mail package, and the documentation of it. Read the documentation. Then you know.
There are two ways to do it:
1) Google provides API's to access mail you could use that library which provides more control over your mails. See here: http://code.google.com/apis/gmail/. In the same way try for other email providers.
2) Simple mail client(you could find it easily googling), but you need to look at headers to identify which mails are read/unread etc.
You need a registry where you can get the properties for a given mail service.
For instance, instead of specifying a pop3 host, you could specify the name of a .properties file that would contain the host, the port, the protocol, etc...
If your .properties file contains the protocol, for instance mail.store.protocol=pop3, you could use session.getStore() (with no argument), and the same code could be used for pop3, imap, pop3s, imaps.

Categories

Resources