I have a piece of Java code (see below), which has not been changed in a while and worked two weeks ago. When I run it now, I suddenly get an "AUTHENTICATE FAILED." I get the error on two different PCs, and I have validated that the credentials used still work when I log into may office365 mailbox using the browser.
Has something changed on the office365 side I should know of?
The error I get is:
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
at javax.mail.Service.connect(Service.java:366)
at javax.mail.Service.connect(Service.java:246)
at my.application.input.imap.ImapMailBoxReader.processOnMessages(ImapMailBoxReader.java:69)
Digging deeper, the cause seems to be an A3 NO AUTHENTICATE failed. response (line 730 of javax.mail.IMAPStore).
The code I use is the following (using javax.mail version 1.6.2):
package my.application.input.imap;
import my.application.dao.PhysicalTransactionDao;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.io.*;
import java.util.function.Consumer;
public class ImapMailBoxReader {
private String host;
private String username;
private String password;
public static void main(String[] args) {
ImapMailBoxReader imapReader = new ImapMailBoxReader(
"outlook.office365.com",
"myemail",
"mypassword");
LocalDate startDate = LocalDate.of(2022,4,1);
LocalDate endDate = LocalDate.of(2022,7,1);
imapReader.processOnMessages("Inbox", startDate, endDate, SomeClass::processMessage);
}
public ImapMailBoxReader(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
/**
* Returns all messages on or after the given since date, until today. If the given since date is null, all messages
* are returned
* #param folder the folder to search through
* #param since the given since date
* #param mailConsumer the consumer that will process the messages retrieved
*/
public void processOnMessages(String folder, LocalDate since, Consumer<Message> mailConsumer) {
processOnMessages(folder, since, null, mailConsumer);
}
/**
* Runs a given mailconsumer on all messages in the given imap folder that have been received on, or after, the given
* since date and before the given until date. If since is null, all messages are returned up to the until date.
* If until is null, all messages are returned from the since date until now. If both are null, all messages are
* returned.
* #param folder the folder to search through
* #param since if specified, only messages from this date on are returned
* #param mailconsumer the consumer that will be executed on the messages
*/
public void processOnMessages(String folder, LocalDate since, LocalDate until, Consumer<Message> mailconsumer) {
try {
Properties prop = new Properties();
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.setProperty("mail.imap.starttls.enable", "true");
prop.put("mail.imap.starttls.enable", "true");
prop.put("mail.imap.ssl.socketFactory", sf);
//Connect to the server
Session session = Session.getDefaultInstance(prop, null);
Store store = session.getStore("imap");
store.connect(host, username, password);
//open the inbox folder
Folder inbox = store.getFolder(folder);
inbox.open(Folder.READ_ONLY);
Message[] messages;
if (since != null) {
Date startDate = Date.from(since.atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm newerThan = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
SearchTerm both = new AndTerm(olderThan, newerThan);
messages = inbox.search(both);
} else {
messages = inbox.search(newerThan);
}
} else if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
messages = inbox.search(olderThan);
} else {
messages = inbox.getMessages();
}
for (Message m: messages) {
mailconsumer.accept(m);
}
inbox.close(false);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Will search through all attachments of the message, and will pass those with the given extension (if provided)
* to the consumer. Note that the connection to the imap should be open for all this magic to work. this method
* is intended to be called from a messageconsumer during the processOnMessages method from this class.
* #param message the message for which the attachments are needed.
* #param extension if provided, only attachments with this extension will be provided
* #param attachmentConsumer the consumer that will process the attachments
* #throws IOException if for some reason the attachments can't be accessed
* #throws MessagingException for other messaging errors
*/
public static void processOnAttachments(Message message, String extension, Consumer<InputStream> attachmentConsumer)
throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.getFileName() != null && bodyPart.getFileName().endsWith(extension)) {
attachmentConsumer.accept(bodyPart.getInputStream());
}
}
}
}
Again, this code worked perfectly two weeks ago, nothing was changed on my side and the credentials still work...
All suggestions are appreciated.
You must use OAuth2, legacy security may have been deprecated.
Works with Thunderbird for example.
Just see how to reactivate legacy auth or use OAuth2 with your java client.
#see https://learn.microsoft.com/fr-fr/exchange/troubleshoot/administration/cannot-connect-mailbox-pop-imap-outlook to reactivate legacy
PS : To use shared mailbox, you must user mailbox name as user, and OAuth2 user + password and MFA if needed during Auth part, all that instead of old way (user#domain\sharedmailbox + password)
Related
I have a piece of Java code (see below), which has not been changed in a while and worked two weeks ago. When I run it now, I suddenly get an "AUTHENTICATE FAILED." I get the error on two different PCs, and I have validated that the credentials used still work when I log into may office365 mailbox using the browser.
Has something changed on the office365 side I should know of?
The error I get is:
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
at javax.mail.Service.connect(Service.java:366)
at javax.mail.Service.connect(Service.java:246)
at my.application.input.imap.ImapMailBoxReader.processOnMessages(ImapMailBoxReader.java:69)
Digging deeper, the cause seems to be an A3 NO AUTHENTICATE failed. response (line 730 of javax.mail.IMAPStore).
The code I use is the following (using javax.mail version 1.6.2):
package my.application.input.imap;
import my.application.dao.PhysicalTransactionDao;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.io.*;
import java.util.function.Consumer;
public class ImapMailBoxReader {
private String host;
private String username;
private String password;
public static void main(String[] args) {
ImapMailBoxReader imapReader = new ImapMailBoxReader(
"outlook.office365.com",
"myemail",
"mypassword");
LocalDate startDate = LocalDate.of(2022,4,1);
LocalDate endDate = LocalDate.of(2022,7,1);
imapReader.processOnMessages("Inbox", startDate, endDate, SomeClass::processMessage);
}
public ImapMailBoxReader(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
/**
* Returns all messages on or after the given since date, until today. If the given since date is null, all messages
* are returned
* #param folder the folder to search through
* #param since the given since date
* #param mailConsumer the consumer that will process the messages retrieved
*/
public void processOnMessages(String folder, LocalDate since, Consumer<Message> mailConsumer) {
processOnMessages(folder, since, null, mailConsumer);
}
/**
* Runs a given mailconsumer on all messages in the given imap folder that have been received on, or after, the given
* since date and before the given until date. If since is null, all messages are returned up to the until date.
* If until is null, all messages are returned from the since date until now. If both are null, all messages are
* returned.
* #param folder the folder to search through
* #param since if specified, only messages from this date on are returned
* #param mailconsumer the consumer that will be executed on the messages
*/
public void processOnMessages(String folder, LocalDate since, LocalDate until, Consumer<Message> mailconsumer) {
try {
Properties prop = new Properties();
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.setProperty("mail.imap.starttls.enable", "true");
prop.put("mail.imap.starttls.enable", "true");
prop.put("mail.imap.ssl.socketFactory", sf);
//Connect to the server
Session session = Session.getDefaultInstance(prop, null);
Store store = session.getStore("imap");
store.connect(host, username, password);
//open the inbox folder
Folder inbox = store.getFolder(folder);
inbox.open(Folder.READ_ONLY);
Message[] messages;
if (since != null) {
Date startDate = Date.from(since.atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm newerThan = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
SearchTerm both = new AndTerm(olderThan, newerThan);
messages = inbox.search(both);
} else {
messages = inbox.search(newerThan);
}
} else if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
messages = inbox.search(olderThan);
} else {
messages = inbox.getMessages();
}
for (Message m: messages) {
mailconsumer.accept(m);
}
inbox.close(false);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Will search through all attachments of the message, and will pass those with the given extension (if provided)
* to the consumer. Note that the connection to the imap should be open for all this magic to work. this method
* is intended to be called from a messageconsumer during the processOnMessages method from this class.
* #param message the message for which the attachments are needed.
* #param extension if provided, only attachments with this extension will be provided
* #param attachmentConsumer the consumer that will process the attachments
* #throws IOException if for some reason the attachments can't be accessed
* #throws MessagingException for other messaging errors
*/
public static void processOnAttachments(Message message, String extension, Consumer<InputStream> attachmentConsumer)
throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.getFileName() != null && bodyPart.getFileName().endsWith(extension)) {
attachmentConsumer.accept(bodyPart.getInputStream());
}
}
}
}
Again, this code worked perfectly two weeks ago, nothing was changed on my side and the credentials still work...
All suggestions are appreciated.
You must use OAuth2, legacy security may have been deprecated.
Works with Thunderbird for example.
Just see how to reactivate legacy auth or use OAuth2 with your java client.
#see https://learn.microsoft.com/fr-fr/exchange/troubleshoot/administration/cannot-connect-mailbox-pop-imap-outlook to reactivate legacy
PS : To use shared mailbox, you must user mailbox name as user, and OAuth2 user + password and MFA if needed during Auth part, all that instead of old way (user#domain\sharedmailbox + password)
I am trying to establish an SSH connection through my Java code, but getting below exception .. I tested my connection through Putty/Winscp tools and it works fine. The problem is with my Java code...
SEVERE: The Transport Protocol thread failed
java.io.IOException: The socket is EOF
at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readBufferedData(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readMessage(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolCommon.readMessage(Unknown Source)
at com.sshtools.j2ssh.transport.kex.DhGroup1Sha1.performClientExchange(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolClient.performKeyExchange(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolCommon.beginKeyExchange(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolCommon.onMsgKexInit(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolCommon.startBinaryPacketProtocol(Unknown Source)
at com.sshtools.j2ssh.transport.TransportProtocolCommon.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Below is my piece of Java code to establish the connection
public class MySSHClient {
static SshClient ssh = null;
static SshConnectionProperties properties = null;
SessionChannelClient session = null;
private static void MySSHClient(String hostName, String userName, String passwd )
{
try
{
// Make a client connection
ssh = new SshClient();
properties = new SshConnectionProperties();
properties.setHost("192.168.1.175");
// Connect to the host
ssh.connect(properties, new IgnoreHostKeyVerification());
// Create a password authentication instance
PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();
pwd.setUsername("root");
pwd.setPassword("123456");
// Try the authentication
int result = ssh.authenticate(pwd);
// Evaluate the result
if (result==AuthenticationProtocolState.COMPLETE) {
System.out.println("Connection Authenticated");
}
}
catch(Exception e)
{
System.out.println("Exception : " + e.getMessage());
}
}//end of method.
public String execCmd(String cmd)
{
String theOutput = "";
try
{
// The connection is authenticated we can now do some real work!
session = ssh.openSessionChannel();
if ( session.executeCommand(cmd) )
{
IOStreamConnector output = new IOStreamConnector();
java.io.ByteArrayOutputStream bos = new
java.io.ByteArrayOutputStream();
output.connect(session.getInputStream(), bos );
session.getState().waitForState(ChannelState.CHANNEL_CLOSED);
theOutput = bos.toString();
}
//else
//throw Exception("Failed to execute command : " + cmd);
//System.out.println("Failed to execute command : " + cmd);
}
catch(Exception e)
{
System.out.println("Exception : " + e.getMessage());
}
return theOutput;
}
public static void main(String[] args){
MySSHClient(null, null, null);
}
Motivation
I stumbled across this question and answers while investigating the error in question java.io.IOException: The socket is EOF. Because changing the code to use some other SSH Java library is not immediately possible in my case and the stated explanation by #a3.14_Infinity was not detailed enough for me, I'd like to add my take on it.
java.io.IOException: The socket is EOF - Why?
Because this exception is not very helpful, I first tried Wireshark to see what's going on over the wire, but to no avail. So I configured the sshd_config (OpenSSH 6.9) to log on DEBUG3 level and got the answer in the /var/log/auth.log file of my test machine. It stated a fatal error while trying to negotiate the key exchange algorithm with the SSH client (the Java SSH library).
Because the SSH server and client could not agree on a mutual key exchange algorithm the OpenSSH server terminates the connection to the client. In consequence, the Java SSH library code throws the exception.
But why does it happen?
The sshtools.j2ssh (sshtools : j2ssh-core : 0.2.9) library code is pretty old and discontinued. Starting with OpenSSH 6.7 (released October, 2014) default ciphers and MAC have been altered to remove unsafe algorithms which includes the blowfish-cbc cipher. And with OpenSSH 6.9 (released June, 2015) the support for the 1024-bit diffie-hellman-group1-sha1 key exchange is disabled by default.
When you still use the prehistoric SSH Tools j2ssh library (God forbid) connecting to a newer OpenSSH server you will get the described error. The library code only offers the diffie-hellman-group1-sha1 key exchange algorithm to the OpenSSH server which it does not support by default. Thus, a secure connection cannot be established.
Cannot change the code?
If moving to another Java SSH library is not immediately possible (my case) then you can re-enable the disabled diffie-hellman-group1-sha1 key exchange algorithm in the OpenSSH's server config file sshd_config. For example like this.
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm#openssh.com,aes256-gcm#openssh.com,chacha20-poly1305#openssh.com,blowfish-cbc
KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,curve25519-sha256#libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1
But let me be clear on this. The diffie-hellman-group1-sha1 key exchange algorithm as well as the blowfish-cbc cipher are turned off by default because they are insecure. Reenabling them should only be a temporary measure until you can replace this obsolete Java SSH library.
Finally, I like to point out that the suggested Java Secure Channel (JSch) library in other answers is discontinued. So, you might want to consider sshj or even ssh2j-maverick instead.
Edit: I was wrong, the Java Secure Channel JSch library is alive (JSCH 0.1.54 was released on 2016-09-03 on MavenCentral) and certainly worth your consideration. Alternatively, you may want to consider also sshj or ssh2j-maverick.
Addendum: Migration
To keep the migration effort for the sshtools.j2ssh (sshtools : j2ssh-core : 0.2.9) library minimal I looked at the commercial legacy SSH client library from SSHTOOLS (version 1.7.1). This allowed to keep the existing library integration code with few minor changes regarding library API and exception handling. Thus, if you do not want to restart from scratch then biting the bullet and sticking with SSHTOOLS is probably your best option. Finally, to gauge the migration effort I first replaced the library with SSHTOOLS' open source library ssh2j-maverick which almost has the same API as its latest commercial version (version 1.7.1).
This error ("The Transport Protocol thread failed. java.io.IOException: The socket is EOF”) occurs when j2ssh.jar file is not compatible with current SSH version of SFTP server.
You can try using Java Secure Channel (JSch) from here.
Courtesy: http://techydiary.com/the-transport-protocol-thread-failed-java-io-ioexception-the-socket-is-eof/
The following sample Code may help you,
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class SSHClient {
/**
* Constant EXCUTE_CHANNEL
*/
public static final String EXCUTE_CHANNEL = "exec";
/**
* Constant STRICT_KEY_CHECKING
*/
public static final String STRICT_KEY_CHECKING = "StrictHostKeyChecking";
/** Name/ip of the remote machine/device **/
private String host;
private String userName;
private String password;
/**
* This method used to initilze user and host
*
* #param userName
* #param password
* #param host
*/
public SSHClient(String userName,String password, String host) {
super();
this.userName = userName;
this.password = password;
this.host = host;
}
/**
* This method used to execute commands remotly by using SSHV2
*
* #param host
* #param username
* #param password
* #param command
* #return
*/
public String executeCommand(String command) {
StringBuilder log = new StringBuilder();
String response = null;
Channel channel = null;
Session session = null;
try {
JSch jsch = new JSch();
JSch.setConfig(STRICT_KEY_CHECKING, Constants.NO);
session = jsch.getSession(userName, host, 22);
// If two machines have SSH passwordless logins setup, the following
// line is not needed:
session.setPassword(password);
session.connect();
channel = session.openChannel(EXCUTE_CHANNEL);
((ChannelExec) channel).setCommand(command);
// channel.setInputStream(System.in);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
response = IOUtils.toString(in);
} catch (Exception ex) {
//handle exception
} finally {
try {
if (session != null) {
session.disconnect();
}
} catch (Exception ex) {
//handle exception
}
try {
if (channel != null) {
channel.disconnect();
}
} catch (Exception ex) {
//handle exception
}
}
System.ou.println( "Response received :"+ response));
return response;
}
}
Here is the working code reused from some google source -
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.StreamGobbler;
Connection conn = new Connection(server);
conn.connect();
boolean isAuthenticated = conn.authenticateWithPassword(user_id, password);
System.out.println("Is server - " + server + " Authenticated? " + isAuthenticated);
if (isAuthenticated == false)
throw new IOException("Authentication failed.");
ch.ethz.ssh2.Session sess = conn.openSession();
String new_commands = "";
for (int i = 0; i < commands.size(); i++) {
new_commands = new_commands + commands.get(i) + "\n";
}
System.out.println("The command executed is: " + new_commands);
sess.requestDumbPTY();
sess.execCommand(new_commands);
InputStream stdout = new StreamGobbler(sess.getStdout());
BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
InputStream errStrm = new StreamGobbler(sess.getStderr());
BufferedReader stderrRdr = new BufferedReader(new InputStreamReader(errStrm));
sess.getStdin().write("EXIT\n".getBytes());
System.out.println("the output of the command is");
while (true) {
String line_out = br.readLine();
if (line_out == null) {
break;
} else {
System.out.println(line_out);
output_logs.add(line_out);
}
}
while (true) {
String line_error = stderrRdr.readLine();
if (line_error == null) {
break;
} else {
System.out.println(line_error);
output_logs.add(line_error);
}
}
output_logs.add("Exit Code:" + sess.getExitStatus());
System.out.println("ExitCode: " + sess.getExitSignal());
sess.close();
conn.close();
found a simple solution on the OS:
comment out the Cipher line in /etc/ssh/sshd_config
and run service sshd restart
I'm trying to add a "delayed disconnect" mechanism to a WebSocket chat I'm developing. What this means is that if the user is disconnected, but reconnects within a certain time limit - I'm going to use 30 seconds as an example - the disconnection is ignored. The reason for this is a proof-of-concept for if the user briefly loses their connection - e.g. a mobile user entering a lift.
I've decided to use cookies for this. The logic I've figured out is that when a WebSocket is opened, it also opens a HttpSession. From that, I can check to see if a cookie with a particular id exists. If it does, then they are not treated as a new user. However, for this I need to be able to set the expiry time of the cookie for 30 seconds after the socket has been closed.
I already know that Cookie.setMaxAge() would do that, but when I tried this inside the OnClose() method on the server, the server threw a NullPointerException. That's not really a surprise, since I was obviously trying to access the user session after it had been closed.
So, is there a way to do this?
Update 16th of Feb I've decided to try resetting the cookie entirely when a message is sent. This partly works, since the cookies are generated and added to the HttpSession, but upon reconnecting the server thinks the user is entirely new. So, I think my problem is that the cookie isn't being sent to the user.
Update 2 After reading this question, I've moved the cookie generation into a configuration class that is called on a successful handshake. If the request does not have a cookie, it is treated as an entirely new connection, and logs that to the System console as proof of concept. One thing I've had to do was to extend the lifetime of the cookie at the start: currently, it's 10 minutes as a ballpark figure. If I can't find out how to do exactly what I said up above, I'll go with this.
Update 19th of February I've ditched cookies altogether. See my solution.
I solved this by ditching cookies altogether. I've just shown the methods in the relevant classes; if this isn't enough, I will edit my answer to include the full code.
Inside the configuration class, I get the x-forwarded-for header of the request. This matches the IP address of the client, especially since my backend server is behind a proxy. If the user's IP address is in a list of users, their connection is "refreshed"; otherwise, they are added to the list. Upon disconnection, for whatever reason, the user is marked as disconnected.
A separate ConnectionMonitor class implements the Runnable interface and runs every 10 seconds, and checks to see if any clients have been disconnected for more than 30 seconds. If they have been, then they are removed from the list of users.
MyConfigClass.modifyHandshake()
#Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response)
{
HttpSession theSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put(HttpSession.class.getName(), theSession);
String ID = request.getHeaders().get("x-forwarded-for").get(0);
if (ChatroomServerEndpoint.users.containsKey(ID))
{
// if this user isn't new, add them back onto the list
User oldUser = ChatroomServerEndpoint.users.get(ID);
System.out.println("An old user with " + ID + " has returned.");
ChatroomServerEndpoint.users.remove(oldUser);
ChatroomServerEndpoint.users.put(ID, oldUser);
oldUser.toggleConnection(true);
System.out.println(oldUser + ", " + ChatroomServerEndpoint.users.size() );
}
else
{
// add a new user to the list
System.out.println("A new user with ID " + ID + " has arrived!");
User newUser = new User(ID);
ChatroomServerEndpoint.users.put(ID, newUser);
System.out.println(newUser + ", " + ChatroomServerEndpoint.users.size() );
}
// put this ID into the configuration for proof of concept
config.getUserProperties().put("newUser", ID);
}
ConnectionMonitor.updateUsers() runs in a separate thread.
void updateUsers()
{
for(String id : ChatroomServerEndpoint.users.keySet())
{
User theUser = ChatroomServerEndpoint.users.get(id);
if (theUser.getStatus() == User.Connection.DISCONNECTED)
{
// get the time at which the user disconnected
Calendar disconnectDate = theUser.getdisconnectionDate();
// Calendar.getTime.getTime returns milliseconds,
// so, multiply maxDisconnectTime by 1000 to see if the user has expired
if (theDate.getTime().getTime() - disconnectDate.getTime().getTime()
>= maxDisconnectTime * 1000 )
{
System.out.println(id + " has timed out");
ChatroomServerEndpoint.users.remove(id);
}
}
}
}
User
public class User {
// the ID is the user's IP address
private String id;
// connection status
public enum Connection
{
CONNECTED,
DISCONNECTED
}
private Connection status;
// the time of disconnection
private Calendar disconnectionDate;
// each user needs a WebSocket Session to be able to send and receive messages
private Session userSession;
/**
* #return the id of this user
*/
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* #return connection status
*/
public Connection getStatus() {
return status;
}
public void setStatus(Connection status) {
this.status = status;
}
public Calendar getdisconnectionDate() {
return disconnectionDate;
}
public void setdisconnectionDate(Calendar disconnectionDate) {
this.disconnectionDate = disconnectionDate;
}
/**
* #return the userSession
*/
public Session getUserSession() {
return userSession;
}
/**
* #param userSession the userSession to set
*/
public void setUserSession(Session userSession) {
this.userSession = userSession;
}
/**
* #param newID the new ID of the user
*/
public User (String newID)
{
this.id = newID;
this.status = Connection.CONNECTED;
}
/**
* Toggles the connection
* #param toggle - if true, the user is connected
*/
public void toggleConnection(boolean toggle)
{
if (toggle == false)
{
status = Connection.DISCONNECTED;
disconnectionDate = Calendar.getInstance();
}
else
{
status = Connection.CONNECTED;
disconnectionDate = Calendar.getInstance();
disconnectionDate.add(Calendar.HOUR, 1); // give an extra hour to prevent them being disconnected too soon
}
}
}
I am trying to sent an email using my company's mail server. But I am getting the following exception
Caused by: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.1 Client was not authenticated
at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:1388)
at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:959)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:583)
at javax.mail.Transport.send0(Transport.java:169)
at javax.mail.Transport.send(Transport.java:98)
Here is my sample code,
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", "example.server.com");
props.put("mail.smtp.auth", "true");
props.put("mail.debug", "true");
props.put("mail.smtp.port", "25");
// Get session
//Session session = Session.getDefaultInstance(props, null);
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password");
}
});
// Define message
MimeMessage message = new MimeMessage(session);
// Set the from address
message.setFrom(new InternetAddress(from));
// Set the to address
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
// Set the subject
message.setSubject("Hello JavaMail");
// Set the content
message.setText("Welcome to JavaMail");
// Send message
Transport.send(message);
What piece of code is wrong?
As as username and password , I am using my company's email address and password.
The 5.7.1 is probably caused by exchange and not your code. You may just need to enable relaying on the server. Either for anonymous users or from a certain IP address. I'm not an expert on Exchange but I have got this working before. Here is the last solution I tested that works:
If a 5.7.1 error is encountered when trying to send an email via SMTP on an exchange server when the user has been authenticated..
For ref the issue you just had was caused by a setting on the Exchange 2007 server – this would not normally be a problem on 2003 server
Fixed by doing below...
You can set this authentication setting via the GUI
In Server configuration / Hub Transport / Default <ServerName>
Right click, properties, Permission Groups
Check "Anonymous users" and then click OK
Obviously anon users is not too secure but you could see if this solves the problem.
When I will use a MS Exhange SMTP Server to send an email, I use the above maven dependency.
<dependency>
<groupId>com.microsoft.ews-java-api</groupId>
<artifactId>ews-java-api</artifactId>
<version>2.0</version>
</dependency>
For that reason I created a class that represents an email client for MS Exchange Servers. I use log4j for logging.
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Below the MS Exchange client class (I use the builder pattern for the construction of the object for thread safety),
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
import microsoft.exchange.webservices.data.core.service.item.EmailMessage;
import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
import microsoft.exchange.webservices.data.credential.WebCredentials;
import microsoft.exchange.webservices.data.property.complex.MessageBody;
import org.apache.log4j.Logger;
/**
* A client to connect to a MS Exchange SMTP Server.
*/
public final class ExchangeClient {
private static final Logger LOGGER = Logger.getLogger(ExchangeClient.class);
private final String hostname;
private final ExchangeVersion exchangeVersion;
private final String domain;
private final String username;
private final String password;
private final String subject;
private final String recipientTo;
private final List<String> recipientCc;
private final List<String> recipientBcc;
private final List<String> attachments;
private final String message;
private ExchangeClient(ExchangeClientBuilder builder) {
this.hostname = builder.hostname;
this.exchangeVersion = builder.exchangeVersion;
this.domain = builder.domain;
this.username = builder.username;
this.password = builder.password;
this.subject = builder.subject;
this.recipientTo = builder.recipientTo;
this.recipientCc = builder.recipientCc;
this.recipientBcc = builder.recipientBcc;
this.attachments = builder.attachments;
this.message = builder.message;
}
public static class ExchangeClientBuilder {
private String hostname;
private ExchangeVersion exchangeVersion;
private String domain;
private String username;
private String password;
private String subject;
private String recipientTo;
private List<String> recipientCc;
private List<String> recipientBcc;
private List<String> attachments;
private String message;
public ExchangeClientBuilder() {
this.exchangeVersion = ExchangeVersion.Exchange2010_SP1;
this.hostname = "";
this.username = "";
this.password = "";
this.subject = "";
this.recipientTo = "";
this.recipientCc = new ArrayList<>(0);
this.recipientBcc = new ArrayList<>(0);
this.attachments = new ArrayList<>(0);
this.message = "";
}
/**
* The hostname of the Exchange Web Service. It will be used for
* connecting with URI https://hostname/ews/exchange.asmx
*
* #param hostname the hostname of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder hostname(String hostname) {
this.hostname = hostname;
return this;
}
/**
* The Exchange Web Server version.
*
* #param exchangeVersion the Exchange Web Server version.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder exchangeVersion(ExchangeVersion exchangeVersion) {
this.exchangeVersion = exchangeVersion;
return this;
}
/**
* The domain of the MS Exchange Smtp Server.
*
* #param domain the domain of the Active Directory. The first part of
* the username. For example: MYDOMAIN\\username, set the MYDOMAIN.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder domain(String domain) {
this.domain = domain;
return this;
}
/**
* The username of the MS Exchange Smtp Server. The second part of the
* username. For example: MYDOMAIN\\username, set the username.
*
* #param username the username of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder username(String username) {
this.username = username;
return this;
}
/**
* The password of the MS Exchange Smtp Server.
*
* #param password the password of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder password(String password) {
this.password = password;
return this;
}
/**
* The subject for this send.
*
* #param subject the subject for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder subject(String subject) {
this.subject = subject;
return this;
}
/**
* The recipient for this send.
*
* #param recipientTo the recipient for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientTo(String recipientTo) {
this.recipientTo = recipientTo;
return this;
}
/**
* You can specify one or more email address that will be used as cc
* recipients.
*
* #param recipientCc the first cc email address.
* #param recipientsCc the other cc email address for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientCc(String recipientCc, String... recipientsCc) {
// Prepare the list.
List<String> recipients = new ArrayList<>(1 + recipientsCc.length);
recipients.add(recipientCc);
recipients.addAll(Arrays.asList(recipientsCc));
// Set the list.
this.recipientCc = recipients;
return this;
}
/**
* You can specify a list with email addresses that will be used as cc
* for this email send.
*
* #param recipientCc the list with email addresses that will be used as
* cc for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientCc(List<String> recipientCc) {
this.recipientCc = recipientCc;
return this;
}
/**
* You can specify one or more email address that will be used as bcc
* recipients.
*
* #param recipientBcc the first bcc email address.
* #param recipientsBcc the other bcc email address for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientBcc(String recipientBcc, String... recipientsBcc) {
// Prepare the list.
List<String> recipients = new ArrayList<>(1 + recipientsBcc.length);
recipients.add(recipientBcc);
recipients.addAll(Arrays.asList(recipientsBcc));
// Set the list.
this.recipientBcc = recipients;
return this;
}
/**
* You can specify a list with email addresses that will be used as bcc
* for this email send.
*
* #param recipientBcc the list with email addresses that will be used
* as bcc for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientBcc(List<String> recipientBcc) {
this.recipientBcc = recipientBcc;
return this;
}
/**
* You can specify one or more email address that will be used as cc
* recipients.
*
* #param attachment the first attachment.
* #param attachments the other attachments for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder attachments(String attachment, String... attachments) {
// Prepare the list.
List<String> attachmentsToUse = new ArrayList<>(1 + attachments.length);
attachmentsToUse.add(attachment);
attachmentsToUse.addAll(Arrays.asList(attachments));
// Set the list.
this.attachments = attachmentsToUse;
return this;
}
/**
* You can specify a list with email attachments that will be used for
* this email send.
*
* #param attachments the list with email attachments that will be used
* for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder attachments(List<String> attachments) {
this.attachments = attachments;
return this;
}
/**
* The body of the email message.
*
* #param message the body of the email message.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder message(String message) {
this.message = message;
return this;
}
/**
* Build a mail.
*
* #return an EmailApacheUtils object.
*/
public ExchangeClient build() {
return new ExchangeClient(this);
}
}
public boolean sendExchange() {
// The Exchange Server Version.
ExchangeService exchangeService = new ExchangeService(exchangeVersion);
// Credentials to sign in the MS Exchange Server.
ExchangeCredentials exchangeCredentials = new WebCredentials(username, password, domain);
exchangeService.setCredentials(exchangeCredentials);
// URL of exchange web service for the mailbox.
try {
exchangeService.setUrl(new URI("https://" + hostname + "/ews/Exchange.asmx"));
} catch (URISyntaxException ex) {
LOGGER.error("An exception occured while creating the uri for exchange service.", ex);
return false;
}
// The email.
EmailMessage emailMessage;
try {
emailMessage = new EmailMessage(exchangeService);
emailMessage.setSubject(subject);
emailMessage.setBody(MessageBody.getMessageBodyFromText(message));
} catch (Exception ex) {
LOGGER.error("An exception occured while setting the email message.", ex);
return false;
}
// TO recipient.
try {
emailMessage.getToRecipients().add(recipientTo);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the TO recipient(" + recipientTo + ").", ex);
return false;
}
// CC recipient.
for (String recipient : recipientCc) {
try {
emailMessage.getCcRecipients().add(recipient);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the CC recipient(" + recipient + ").", ex);
return false;
}
}
// BCC recipient
for (String recipient : recipientBcc) {
try {
emailMessage.getBccRecipients().add(recipient);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the BCC recipient(" + recipient + ").", ex);
return false;
}
}
// Attachements.
for (String attachmentPath : attachments) {
try {
emailMessage.getAttachments().addFileAttachment(attachmentPath);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while setting the attachment.", ex);
return false;
}
}
try {
emailMessage.send();
LOGGER.debug("An email is send.");
} catch (Exception ex) {
LOGGER.error("An exception occured while sending an email.", ex);
return false;
}
return true;
}
}
A working example,
// import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
ExchangeClient client = new ExchangeClient.ExchangeClientBuilder()
.hostname("webmail.domainOfWeb.com")
.exchangeVersion(ExchangeVersion.Exchange2010)
.domain("ActiveDirectoryDomain")
.username("ActiveDirectoryUsername")
.password("ActiveDirectoryPassword")
.recipientTo("recipient#whatever.com")
.recipientCc("recipient#whatever.com") // Ignore it in case you will not use Cc recipients.
.recipientBcc("recipient#whatever.com") // Ignore it in case you will not use Bcc recipients.
.attachments("/home/username/image.png") // Ignore it in case you will not use attachements.
.subject("Test Subject")
.message("Test Message")
.build();
client.sendExchange();
Mail.jar (Version 1.4.0) has a compatibility issue with MS Exchange Server and throws 530 5.7.1 Client was not authenticated, even when Username and password are configured.
Upgrading the mail API to 1.4.4 OR 1.4.7 should resolve the issue.
Mail API's 1.4.7 can be download from the following URL: http://www.oracle.com/technetwork/java/javamail/index.html
In some companies, the Exchange server SMTP support is disable and you cannot ask them to enable it. In these cases, a reasonable solution is this one:
http://davmail.sourceforge.net/
Simple Java Mail worked for me. The only thing you have to check is for correct hostname, username, port and password TransportStrategy.SMTP_TLS:
new Mailer(host, port, username, password, TransportStrategy.SMTP_TLS).sendMail(email);
I had to use javamail + exchange. The messages returned were helpless.
Thanks to the stack, I got some hints.
Add this to your code
props.put("mail.smtp.starttls.enable","true");
Think of adding the certificates of the machines used too.
To find them, just go to your browser, export them and import to the cacerts file in use.
Please use the following code parts instead of Transport.send(message);
MimeMessage message = new MimeMessage(session);
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect(host, "user", "pwd");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
I have tested in the local and it is working
The ews-java-api package is at end of life.
From https://github.com/OfficeDev/ews-java-api
Starting July 19th 2018, Exchange Web Services (EWS) will no longer receive feature updates. While the service will continue to receive security updates and certain non-security updates, product design and features will remain unchanged. This change also applies to the EWS SDKs for Java and .NET. More information here: https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/
Here's the code I have installed at the top of my document:
copied from: http://coders-and-programmers-struts.blogspot.com/2009/05/sending-email-using-javamail-e-mailing.html
package my.planterstatus;
import java.awt.event.ActionEvent;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import javax.activation.*;
/**
*
* #author Kris
*/
public class PlanterStatusUI extends javax.swing.JFrame
{
/** Creates new form PlanterStatusUI */
public PlanterStatusUI() {
initComponents();
}
public String status = new String(); {
}
public class TestEmail {
// Send a simple, single part, text/plain e-mail
public void main(String[] args, String status) {
// SUBSTITUTE YOUR EMAIL ADDRESSES HERE!!!
String to = "blah#blahblahblah.com";
String from = "Planter Status";
// SUBSTITUTE YOUR ISP'S MAIL SERVER HERE!!!
String host = "smtp.blahblah.com";
// Create properties, get Session
Properties props = new Properties();
// If using static Transport.send(),
// need to specify which host to send it to
props.put("pop.blahblah.net", host);
// To see what is going on behind the scene
props.put("mail.debug", "true");
Session session = Session.getInstance(props);
try {
// Instantiatee a message
Message msg = new MimeMessage(session);
//Set message attributes
msg.setFrom(new InternetAddress(from));
InternetAddress[] address = {new InternetAddress(to)};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject("PS# " + display.getText()+ " is currently " + status);
msg.setSentDate(new Date());
// Set message content
msg.setText("PS# " + display.getText()+ " is currently " + status);
//Send the message
Transport.send(msg);
}
catch (MessagingException mex) {
// Prints all nested (chained) exceptions as well
mex.printStackTrace();
}
}
}//End of class
and here's the code I have installed in my button's event handler:
private void confirmationYesButtonHandler(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Transport.send(msg);
}
The error message I get from netbeans is:
"cannot find variable msg"
The 2 options NetBeans gives me to "solve" the issue are:
"Create Field msg in my.planterstatus.PlanterStatusUI"
"Create Local Variable msg"
I don't know how to fix this. From my extremely limited understanding of Java, it looks like the "msg" variable has been fleshed out at the top of the document, but apparently not.
Help is appreciated!
The scope of the msg variable you've shown is limited to the try block it is within.
Here's a page from the "Java Made Easy" tutorial on scope that appears fairly easy to understand.
msg is declared in the try block and thus is only visible in the try block.