diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form index f6423007f7..e64e95e503 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form @@ -28,12 +28,6 @@ - - - - - - @@ -60,9 +54,21 @@ - + + + + + + + + + + + + + @@ -103,7 +109,7 @@ - + @@ -111,6 +117,8 @@ + + @@ -226,5 +234,13 @@ + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 0945bf67d3..ddd4c10923 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -56,6 +56,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.SwingWorker; import mage.client.MageFrame; @@ -65,7 +66,6 @@ import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; import mage.client.util.Config; import mage.client.util.gui.countryBox.CountryItemEditor; import mage.remote.Connection; -import mage.remote.Connection.ProxyType; import org.apache.log4j.Logger; /** @@ -76,6 +76,7 @@ public class ConnectDialog extends MageDialog { private static final Logger logger = Logger.getLogger(ConnectDialog.class); private Connection connection; private ConnectTask task; + private RegisterUserDialog registerUserDialog; private final ActionListener connectAction = new ActionListener() { @Override @@ -94,6 +95,9 @@ public class ConnectDialog extends MageDialog { this.txtPort.addActionListener(connectAction); this.txtUserName.addActionListener(connectAction); this.txtPassword.addActionListener(connectAction); + + registerUserDialog = new RegisterUserDialog(); + MageFrame.getDesktop().add(registerUserDialog, JLayeredPane.POPUP_LAYER); } public void showDialog() { @@ -152,6 +156,7 @@ public class ConnectDialog extends MageDialog { btnConnect = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); lblStatus = new javax.swing.JLabel(); + btnRegister = new javax.swing.JButton(); setTitle("Connect to server"); setNormalBounds(new java.awt.Rectangle(100, 100, 410, 307)); @@ -180,21 +185,9 @@ public class ConnectDialog extends MageDialog { lblUserName.setLabelFor(txtUserName); lblUserName.setText("User name:"); - txtUserName.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtUserNameActionPerformed(evt); - } - }); - lblPassword.setLabelFor(txtPassword); lblPassword.setText("Password:"); - txtPassword.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtPasswordActionPerformed(evt); - } - }); - lblFlag.setLabelFor(txtUserName); lblFlag.setText("User flag:"); @@ -237,6 +230,13 @@ public class ConnectDialog extends MageDialog { } }); + btnRegister.setText("Register new user"); + btnRegister.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnRegisterActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -244,11 +244,6 @@ public class ConnectDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(btnConnect) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCancel)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) @@ -271,7 +266,16 @@ public class ConnectDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnFind)) .addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 358, Short.MAX_VALUE)))) + .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 375, Short.MAX_VALUE))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(btnRegister) + .addGroup(layout.createSequentialGroup() + .addComponent(btnConnect) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnCancel))) + .addGap(26, 26, 26))) .addContainerGap()) ); layout.setVerticalGroup( @@ -304,13 +308,15 @@ public class ConnectDialog extends MageDialog { .addComponent(chkForceUpdateDB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jProxySettingsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 50, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 38, Short.MAX_VALUE) .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnConnect) .addComponent(btnCancel)) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnRegister) + .addGap(3, 3, 3)) ); pack(); @@ -360,28 +366,7 @@ public class ConnectDialog extends MageDialog { connection.setPassword(this.txtPassword.getText().trim()); connection.setForceDBComparison(this.chkForceUpdateDB.isSelected()); MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor) cbFlag.getEditor()).getImageItem()); - - ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); - - if (configProxyType != null) { - connection.setProxyType(configProxyType); - if (!configProxyType.equals(ProxyType.NONE)) { - String host = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, ""); - String port = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, ""); - if (!host.isEmpty() && !port.isEmpty()) { - connection.setProxyHost(host); - connection.setProxyPort(Integer.valueOf(port)); - String username = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_USERNAME, ""); - connection.setProxyUsername(username); - if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_REMEMBER, "false").equals("true")) { - String password = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PSWD, ""); - connection.setProxyPassword(password); - } - } else { - logger.warn("host or\\and port are empty: host=" + host + ", port=" + port); - } - } - } + PreferencesDialog.setProxyInformation(connection); // pref settings MageFrame.getInstance().setUserPrefsToConnection(connection); @@ -580,10 +565,15 @@ public class ConnectDialog extends MageDialog { // TODO add your handling code here: }//GEN-LAST:event_txtPasswordActionPerformed + private void btnRegisterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRegisterActionPerformed + registerUserDialog.showDialog(); + }//GEN-LAST:event_btnRegisterActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnCancel; private javax.swing.JButton btnConnect; private javax.swing.JButton btnFind; + private javax.swing.JButton btnRegister; private mage.client.util.gui.countryBox.CountryComboBox cbFlag; private javax.swing.JCheckBox chkAutoConnect; private javax.swing.JCheckBox chkForceUpdateDB; diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 6199384a98..88e68ca865 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -2298,6 +2298,30 @@ public class PreferencesDialog extends javax.swing.JDialog { this.repaint(); } + public static void setProxyInformation(Connection connection) { + ProxyType configProxyType = Connection.ProxyType.valueByText(getCachedValue(KEY_PROXY_TYPE, "None")); + if (configProxyType == null) { + return; + } + + connection.setProxyType(configProxyType); + if (!configProxyType.equals(ProxyType.NONE)) { + String host = getCachedValue(KEY_PROXY_ADDRESS, ""); + String port = getCachedValue(KEY_PROXY_PORT, ""); + if (!host.isEmpty() && !port.isEmpty()) { + connection.setProxyHost(host); + connection.setProxyPort(Integer.valueOf(port)); + String username = getCachedValue(KEY_PROXY_USERNAME, ""); + connection.setProxyUsername(username); + if (getCachedValue(KEY_PROXY_REMEMBER, "false").equals("true")) { + String password = getCachedValue(KEY_PROXY_PSWD, ""); + connection.setProxyPassword(password); + } + } else { + log.warn("host or\\and port are empty: host=" + host + ", port=" + port); + } + } + } /** * @param args the command line arguments */ diff --git a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form new file mode 100644 index 0000000000..5f99dece42 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form @@ -0,0 +1,161 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java new file mode 100644 index 0000000000..38d0756820 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java @@ -0,0 +1,231 @@ +package mage.client.dialog; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.swing.SwingWorker; +import mage.client.MageFrame; +import mage.client.util.Config; +import mage.remote.Connection; +import mage.remote.Session; +import mage.remote.SessionImpl; +import org.apache.log4j.Logger; + +public class RegisterUserDialog extends MageDialog { + + private static final Logger logger = Logger.getLogger(ConnectDialog.class); + private Connection connection; + private ConnectTask task; + private Session session; + + /** + * Creates new form RegisterUserDialog + */ + public RegisterUserDialog() { + initComponents(); + } + + public void showDialog() { + this.txtServer.setText(MageFrame.getPreferences().get("serverAddress", Config.serverName)); + this.txtPort.setText(MageFrame.getPreferences().get("serverPort", Integer.toString(Config.port))); + this.lblStatus.setText(""); + + this.setModal(true); + this.setLocation(50, 50); + this.setVisible(true); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + lblServer = new javax.swing.JLabel(); + lblPort = new javax.swing.JLabel(); + lblUserName = new javax.swing.JLabel(); + lblPassword = new javax.swing.JLabel(); + txtUserName = new javax.swing.JTextField(); + txtPassword = new javax.swing.JPasswordField(); + btnRegister = new javax.swing.JButton(); + btnCancel = new javax.swing.JButton(); + lblStatus = new javax.swing.JLabel(); + txtServer = new javax.swing.JTextField(); + txtPort = new javax.swing.JTextField(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Register"); + + lblServer.setLabelFor(txtServer); + lblServer.setText("Server:"); + + lblPort.setLabelFor(txtPort); + lblPort.setText("Port:"); + + lblUserName.setLabelFor(txtUserName); + lblUserName.setText("User name:"); + + lblPassword.setLabelFor(txtPassword); + lblPassword.setText("Password:"); + + txtUserName.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + txtUserNameActionPerformed(evt); + } + }); + + btnRegister.setText("Register"); + btnRegister.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnRegisterActionPerformed(evt); + } + }); + + btnCancel.setText("Cancel"); + btnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnCancelActionPerformed(evt); + } + }); + + lblStatus.setToolTipText(""); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(14, 14, 14) + .addComponent(lblUserName)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblPassword, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblPort, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblServer, javax.swing.GroupLayout.Alignment.TRAILING)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(btnRegister) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnCancel)) + .addComponent(txtUserName, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(txtPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 292, Short.MAX_VALUE) + .addComponent(txtServer, javax.swing.GroupLayout.Alignment.LEADING)) + .addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(16, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(9, 9, 9) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblServer) + .addComponent(txtServer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPort) + .addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblUserName) + .addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPassword) + .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnRegister) + .addComponent(btnCancel)) + .addContainerGap(14, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void txtUserNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtUserNameActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_txtUserNameActionPerformed + + private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed + this.hideDialog(); + }//GEN-LAST:event_btnCancelActionPerformed + + private void btnRegisterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRegisterActionPerformed + connection = new Connection(); + connection.setHost(this.txtServer.getText().trim()); + connection.setPort(Integer.valueOf(this.txtPort.getText().trim())); + connection.setUsername(this.txtUserName.getText().trim()); + connection.setPassword(this.txtPassword.getText().trim()); + PreferencesDialog.setProxyInformation(connection); + task = new ConnectTask(); + task.execute(); + }//GEN-LAST:event_btnRegisterActionPerformed + + private class ConnectTask extends SwingWorker { + + private boolean result = false; + + private static final int CONNECTION_TIMEOUT_MS = 2100; + + @Override + protected Boolean doInBackground() throws Exception { + lblStatus.setText("Connecting..."); + btnRegister.setEnabled(false); + session = new SessionImpl(MageFrame.getInstance()); + result = session.register(connection); + return result; + } + + @Override + protected void done() { + try { + get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS); + if (result) { + lblStatus.setText("Registration succeeded"); + MageFrame.getInstance().showMessage("Registration succeeded"); + hideDialog(); + } else { + lblStatus.setText("Could not register"); + } + } catch (InterruptedException ex) { + logger.fatal("Update Players Task error", ex); + } catch (ExecutionException ex) { + logger.fatal("Update Players Task error", ex); + } catch (CancellationException ex) { + logger.info("Registration was canceled"); + lblStatus.setText("Registration was canceled (but an account might have been actually created)"); + } catch (TimeoutException ex) { + logger.fatal("Registration timeout: ", ex); + } finally { + MageFrame.stopConnecting(); + btnRegister.setEnabled(true); + } + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnCancel; + private javax.swing.JButton btnRegister; + private javax.swing.JLabel lblPassword; + private javax.swing.JLabel lblPort; + private javax.swing.JLabel lblServer; + private javax.swing.JLabel lblStatus; + private javax.swing.JLabel lblUserName; + private javax.swing.JPasswordField txtPassword; + private javax.swing.JTextField txtPort; + private javax.swing.JTextField txtServer; + private javax.swing.JTextField txtUserName; + // End of variables declaration//GEN-END:variables +} diff --git a/Mage.Common/src/mage/interfaces/MageServer.java b/Mage.Common/src/mage/interfaces/MageServer.java index 85bae2817e..ac4dbf84ab 100644 --- a/Mage.Common/src/mage/interfaces/MageServer.java +++ b/Mage.Common/src/mage/interfaces/MageServer.java @@ -55,14 +55,17 @@ import mage.view.UserView; */ public interface MageServer { + // registers a user to the user DB. + boolean registerUser(String sessionId, String userName, String password, String email) throws MageException; + // connection methods - // DEPRECATED - Use registerClientWithPassword instead. This is kept for older clients. + // DEPRECATED - Use connectUser instead. This is only kept for older clients. // This can be deleted once users transitioned to newer clients (1.4.6v1 and later). boolean registerClient(String userName, String sessionId, MageVersion version) throws MageException; - boolean registerClientWithPassword(String userName, String password, String sessionId, MageVersion version) throws MageException; + boolean connectUser(String userName, String password, String sessionId, MageVersion version) throws MageException; - boolean registerAdmin(String password, String sessionId, MageVersion version) throws MageException; + boolean connectAdmin(String password, String sessionId, MageVersion version) throws MageException; // Not used // void deregisterClient(String sessionId) throws MageException; diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index eab3306e5b..ddc6b99d87 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -122,183 +122,18 @@ public class SessionImpl implements Session { return sessionId; } - @Override - public synchronized boolean connect(Connection connection) { - if (isConnected()) { - disconnect(true); - } - this.connection = connection; - this.canceled = false; - return connect(); + // RemotingTask encapsulates a task which is involved with some JBoss Remoting. This is + // intended to be used with handleRemotingTaskExceptions for sharing the common exception + // handling. + public interface RemotingTask { + public boolean run() throws Throwable; } - @Override - public boolean stopConnecting() { - canceled = true; - return true; - } - - @Override - public boolean connect() { - sessionState = SessionState.CONNECTING; + // handleRemotingTaskExceptions runs the given task and handles exceptions appropriately. This + // way we can share the common exception handling. + private boolean handleRemotingTaskExceptions(RemotingTask remoting) { try { - System.setProperty("http.nonProxyHosts", "code.google.com"); - System.setProperty("socksNonProxyHosts", "code.google.com"); - - // clear previous values - System.clearProperty("socksProxyHost"); - System.clearProperty("socksProxyPort"); - System.clearProperty("http.proxyHost"); - System.clearProperty("http.proxyPort"); - - switch (connection.getProxyType()) { - case SOCKS: - System.setProperty("socksProxyHost", connection.getProxyHost()); - System.setProperty("socksProxyPort", Integer.toString(connection.getProxyPort())); - break; - case HTTP: - System.setProperty("http.proxyHost", connection.getProxyHost()); - System.setProperty("http.proxyPort", Integer.toString(connection.getProxyPort())); - Authenticator.setDefault(new MageAuthenticator(connection.getProxyUsername(), connection.getProxyPassword())); - break; - } - InvokerLocator clientLocator = new InvokerLocator(connection.getURI()); - - Map metadata = new HashMap<>(); - /* - 5.8.3.1.1. Write timeouts - The socket timeout facility offered by the JDK applies only to read operations on the socket. As of release 2.5.2, - the socket and bisocket (and also sslsocket and sslbisocket) transports offer a write timeout facility. When a client - or server is configured, in any of the usual ways, with the parameter org.jboss.remoting.transport.socket.SocketWrapper.WRITE_TIMEOUT - (actual value "writeTimeout") set to a positive value (in milliseconds), all write operations will time out if they do - not complete within the configured period. When a write operation times out, the socket upon which the write was invoked - will be closed, which is likely to result in a java.net.SocketException. - Note. A SocketException is considered to be a "retriable" exception, so, if the parameter "numberOfCallRetries" is set - to a value greater than 1, an invocation interrupted by a write timeout can be retried. - Note. The write timeout facility applies to writing of both invocations and responses. It applies to push callbacks as well. - */ - metadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); - metadata.put("generalizeSocketException", "true"); - server = (MageServer) TransporterClient.createTransporterClient(clientLocator.getLocatorURI(), MageServer.class, metadata); - - // http://docs.jboss.org/jbossremoting/docs/guide/2.5/html_single/#d0e1057 - Map clientMetadata = new HashMap<>(); - - clientMetadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); - /* generalizeSocketException - * If set to false, a failed invocation will be retried in the case of - * SocketExceptions. If set to true, a failed invocation will be retried in the case of - * SocketExceptions and also any IOException - * whose message matches the regular expression - * ^.*(?:connection.*reset|connection.*closed|broken.*pipe).*$. - * See also the "numberOfCallRetries" parameter, above. The default value is false.*/ - clientMetadata.put("generalizeSocketException", "true"); - - /* A remoting server also has the capability to detect when a client is no longer available. - * This is done by estabilishing a lease with the remoting clients that connect to a server. - * On the client side, an org.jboss.remoting.LeasePinger periodically sends PING messages to - * the server, and on the server side an org.jboss.remoting.Lease informs registered listeners - * if the PING doesn't arrive withing the specified timeout period. */ - clientMetadata.put(Client.ENABLE_LEASE, "true"); - /* - When the socket client invoker makes its first invocation, it will check to see if there is an available - socket connection in its pool. Since is the first invocation, there will not be and will create a new socket - connection and use it for making the invocation. Then when finished making invocation, will return the still - active socket connection to the pool. As more client invocations are made, is possible for the number of - socket connections to reach the maximum allowed (which is controlled by 'clientMaxPoolSize' property). At this - point, when the next client invocation is made, it will wait up to some configured number of milliseconds, at - which point it will throw an org.jboss.remoting.CannotConnectException. The number of milliseconds is given by - the parameter MicroSocketClientInvoker.CONNECTION_WAIT (actual value "connectionWait"), with a default of - 30000 milliseconds. Note that if more than one call retry is configured (see next paragraph), - the CannotConnectException will be swallowed. - Once the socket client invoker get an available socket connection from the pool, are not out of the woods yet. - For example, a network problem could cause a java.net.SocketException. There is also a possibility that the socket - connection, while still appearing to be valid, has "gone stale" while sitting in the pool. For example, a ServerThread - on the other side of the connection could time out and close its socket. If the attempt to complete an invocation - fails, then MicroSocketClientInvoker will make a number of attempts, according to the parameter "numberOfCallRetries", - with a default value of 3. Once the configured number of retries has been exhausted, - an org.jboss.remoting.InvocationFailureException will be thrown. - */ - clientMetadata.put("numberOfCallRetries", "1"); - - /** - * I'll explain the meaning of "secondaryBindPort" and - * "secondaryConnectPort", and maybe that will help. The Remoting - * bisocket transport creates two ServerSockets on the server. The - * "primary" ServerSocket is used to create connections used for - * ordinary invocations, e.g., a request to create a JMS consumer, - * and the "secondary" ServerSocket is used to create "control" - * connections for internal Remoting messages. The port for the - * primary ServerSocket is configured by the "serverBindPort" - * parameter, and the port for the secondary ServerSocket is, by - * default, chosen randomly. The "secondaryBindPort" parameter can - * be used to assign a specific port to the secondary ServerSocket. - * Now, if there is a translating firewall between the client and - * server, the client should be given the value of the port that is - * translated to the actual binding port of the secondary - * ServerSocket. For example, your configuration will tell the - * secondary ServerSocket to bind to port 14000, and it will tell - * the client to connect to port 14001. It assumes that there is a - * firewall which will translate 14001 to 14000. Apparently, that's - * not happening. - */ - // secondaryBindPort - the port to which the secondary server socket is to be bound. By default, an arbitrary port is selected. - // secondaryConnectPort - the port clients are to use to connect to the secondary server socket. - // By default, the value of secondaryBindPort is used. secondaryConnectPort is useful if the server is behind a translating firewall. - // Indicated the max number of threads used within oneway thread pool. - clientMetadata.put(Client.MAX_NUM_ONEWAY_THREADS, "10"); - clientMetadata.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, "true"); - callbackClient = new Client(clientLocator, "callback", clientMetadata); - - Map listenerMetadata = new HashMap<>(); - if (debugMode) { - // prevent client from disconnecting while debugging - listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "1000000"); - listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "900000"); - } else { - listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "15000"); - listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "13000"); - } - callbackClient.connect(new ClientConnectionListener(), listenerMetadata); - - Map callbackMetadata = new HashMap<>(); - callbackMetadata.put(Bisocket.IS_CALLBACK_SERVER, "true"); - if (callbackHandler == null) { - callbackHandler = new CallbackHandler(); - } - callbackClient.addListener(callbackHandler, callbackMetadata); - - Set callbackConnectors = callbackClient.getCallbackConnectors(callbackHandler); - if (callbackConnectors.size() != 1) { - logger.warn("There should be one callback Connector (number existing = " + callbackConnectors.size() + ")"); - } - - logger.info("Trying to connect as " + (this.getUserName() == null ? "" : this.getUserName()) + " to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); - callbackClient.invoke(null); - - this.sessionId = callbackClient.getSessionId(); - boolean registerResult; - if (connection.getAdminPassword() == null) { - // for backward compatibility. don't remove twice call - first one does nothing but for version checking - registerResult = server.registerClientWithPassword(connection.getUsername(), connection.getPassword(), sessionId, client.getVersion()); - if (registerResult) { - server.setUserData(connection.getUsername(), sessionId, connection.getUserData()); - } - } else { - registerResult = server.registerAdmin(connection.getAdminPassword(), sessionId, client.getVersion()); - } - if (registerResult) { - sessionState = SessionState.CONNECTED; - serverState = server.getServerState(); - if (!connection.getUsername().equals("Admin")) { - updateDatabase(connection.isForceDBComparison(), serverState); - } - logger.info("Connected as " + (this.getUserName() == null ? "" : this.getUserName()) + " to MAGE server at " + connection.getHost() + ":" + connection.getPort()); - client.connected(this.getUserName() == null ? "" : this.getUserName() + "@" + connection.getHost() + ":" + connection.getPort() + " "); - return true; - } - disconnect(false); - // client.showMessage("Unable to connect to server."); + return remoting.run(); } catch (MalformedURLException ex) { logger.fatal("", ex); client.showMessage("Unable to connect to server. " + ex.getMessage()); @@ -353,6 +188,217 @@ public class SessionImpl implements Session { return false; } + @Override + public synchronized boolean register(final Connection connection) { + return establishJBossRemotingConnection(connection) && handleRemotingTaskExceptions(new RemotingTask() { + @Override + public boolean run() throws Throwable { + logger.info("Trying to register as " + getUserName() + " to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); + boolean registerResult = server.registerUser(sessionId, connection.getUsername(), + connection.getPassword(), ""); + if (registerResult) { + logger.info("Registered as " + getUserName() + " to MAGE server at " + connection.getHost() + ":" + connection.getPort()); + } + return registerResult; + } + }); + } + + @Override + public synchronized boolean connect(final Connection connection) { + return establishJBossRemotingConnection(connection) && handleRemotingTaskExceptions(new RemotingTask() { + @Override + public boolean run() throws Throwable { + logger.info("Trying to log-in as " + getUserName() + " to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); + boolean registerResult; + if (connection.getAdminPassword() == null) { + // for backward compatibility. don't remove twice call - first one does nothing but for version checking + registerResult = server.connectUser(connection.getUsername(), connection.getPassword(), sessionId, client.getVersion()); + if (registerResult) { + server.setUserData(connection.getUsername(), sessionId, connection.getUserData()); + } + } else { + registerResult = server.connectAdmin(connection.getAdminPassword(), sessionId, client.getVersion()); + } + if (registerResult) { + serverState = server.getServerState(); + if (!connection.getUsername().equals("Admin")) { + updateDatabase(connection.isForceDBComparison(), serverState); + } + logger.info("Logged-in as " + getUserName() + " to MAGE server at " + connection.getHost() + ":" + connection.getPort()); + client.connected(getUserName() + "@" + connection.getHost() + ":" + connection.getPort() + " "); + return true; + } + disconnect(false); + return false; + } + }); + } + + @Override + public boolean stopConnecting() { + canceled = true; + return true; + } + + private boolean establishJBossRemotingConnection(final Connection connection) { + if (isConnected()) { + disconnect(true); + } + this.connection = connection; + this.canceled = false; + sessionState = SessionState.CONNECTING; + boolean result = handleRemotingTaskExceptions(new RemotingTask() { + @Override + public boolean run() throws Throwable { + logger.info("Trying to connect to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); + + System.setProperty("http.nonProxyHosts", "code.google.com"); + System.setProperty("socksNonProxyHosts", "code.google.com"); + + // clear previous values + System.clearProperty("socksProxyHost"); + System.clearProperty("socksProxyPort"); + System.clearProperty("http.proxyHost"); + System.clearProperty("http.proxyPort"); + + switch (connection.getProxyType()) { + case SOCKS: + System.setProperty("socksProxyHost", connection.getProxyHost()); + System.setProperty("socksProxyPort", Integer.toString(connection.getProxyPort())); + break; + case HTTP: + System.setProperty("http.proxyHost", connection.getProxyHost()); + System.setProperty("http.proxyPort", Integer.toString(connection.getProxyPort())); + Authenticator.setDefault(new MageAuthenticator(connection.getProxyUsername(), connection.getProxyPassword())); + break; + } + InvokerLocator clientLocator = new InvokerLocator(connection.getURI()); + + Map metadata = new HashMap<>(); + /* + 5.8.3.1.1. Write timeouts + The socket timeout facility offered by the JDK applies only to read operations on the socket. As of release 2.5.2, + the socket and bisocket (and also sslsocket and sslbisocket) transports offer a write timeout facility. When a client + or server is configured, in any of the usual ways, with the parameter org.jboss.remoting.transport.socket.SocketWrapper.WRITE_TIMEOUT + (actual value "writeTimeout") set to a positive value (in milliseconds), all write operations will time out if they do + not complete within the configured period. When a write operation times out, the socket upon which the write was invoked + will be closed, which is likely to result in a java.net.SocketException. + Note. A SocketException is considered to be a "retriable" exception, so, if the parameter "numberOfCallRetries" is set + to a value greater than 1, an invocation interrupted by a write timeout can be retried. + Note. The write timeout facility applies to writing of both invocations and responses. It applies to push callbacks as well. + */ + metadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); + metadata.put("generalizeSocketException", "true"); + server = (MageServer) TransporterClient.createTransporterClient(clientLocator.getLocatorURI(), MageServer.class, metadata); + + // http://docs.jboss.org/jbossremoting/docs/guide/2.5/html_single/#d0e1057 + Map clientMetadata = new HashMap<>(); + + clientMetadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); + /* generalizeSocketException + * If set to false, a failed invocation will be retried in the case of + * SocketExceptions. If set to true, a failed invocation will be retried in the case of + * SocketExceptions and also any IOException + * whose message matches the regular expression + * ^.*(?:connection.*reset|connection.*closed|broken.*pipe).*$. + * See also the "numberOfCallRetries" parameter, above. The default value is false.*/ + clientMetadata.put("generalizeSocketException", "true"); + + /* A remoting server also has the capability to detect when a client is no longer available. + * This is done by estabilishing a lease with the remoting clients that connect to a server. + * On the client side, an org.jboss.remoting.LeasePinger periodically sends PING messages to + * the server, and on the server side an org.jboss.remoting.Lease informs registered listeners + * if the PING doesn't arrive withing the specified timeout period. */ + clientMetadata.put(Client.ENABLE_LEASE, "true"); + /* + When the socket client invoker makes its first invocation, it will check to see if there is an available + socket connection in its pool. Since is the first invocation, there will not be and will create a new socket + connection and use it for making the invocation. Then when finished making invocation, will return the still + active socket connection to the pool. As more client invocations are made, is possible for the number of + socket connections to reach the maximum allowed (which is controlled by 'clientMaxPoolSize' property). At this + point, when the next client invocation is made, it will wait up to some configured number of milliseconds, at + which point it will throw an org.jboss.remoting.CannotConnectException. The number of milliseconds is given by + the parameter MicroSocketClientInvoker.CONNECTION_WAIT (actual value "connectionWait"), with a default of + 30000 milliseconds. Note that if more than one call retry is configured (see next paragraph), + the CannotConnectException will be swallowed. + Once the socket client invoker get an available socket connection from the pool, are not out of the woods yet. + For example, a network problem could cause a java.net.SocketException. There is also a possibility that the socket + connection, while still appearing to be valid, has "gone stale" while sitting in the pool. For example, a ServerThread + on the other side of the connection could time out and close its socket. If the attempt to complete an invocation + fails, then MicroSocketClientInvoker will make a number of attempts, according to the parameter "numberOfCallRetries", + with a default value of 3. Once the configured number of retries has been exhausted, + an org.jboss.remoting.InvocationFailureException will be thrown. + */ + clientMetadata.put("numberOfCallRetries", "1"); + + /** + * I'll explain the meaning of "secondaryBindPort" and + * "secondaryConnectPort", and maybe that will help. The Remoting + * bisocket transport creates two ServerSockets on the server. The + * "primary" ServerSocket is used to create connections used for + * ordinary invocations, e.g., a request to create a JMS consumer, + * and the "secondary" ServerSocket is used to create "control" + * connections for internal Remoting messages. The port for the + * primary ServerSocket is configured by the "serverBindPort" + * parameter, and the port for the secondary ServerSocket is, by + * default, chosen randomly. The "secondaryBindPort" parameter can + * be used to assign a specific port to the secondary ServerSocket. + * Now, if there is a translating firewall between the client and + * server, the client should be given the value of the port that is + * translated to the actual binding port of the secondary + * ServerSocket. For example, your configuration will tell the + * secondary ServerSocket to bind to port 14000, and it will tell + * the client to connect to port 14001. It assumes that there is a + * firewall which will translate 14001 to 14000. Apparently, that's + * not happening. + */ + // secondaryBindPort - the port to which the secondary server socket is to be bound. By default, an arbitrary port is selected. + // secondaryConnectPort - the port clients are to use to connect to the secondary server socket. + // By default, the value of secondaryBindPort is used. secondaryConnectPort is useful if the server is behind a translating firewall. + // Indicated the max number of threads used within oneway thread pool. + clientMetadata.put(Client.MAX_NUM_ONEWAY_THREADS, "10"); + clientMetadata.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, "true"); + callbackClient = new Client(clientLocator, "callback", clientMetadata); + + Map listenerMetadata = new HashMap<>(); + if (debugMode) { + // prevent client from disconnecting while debugging + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "1000000"); + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "900000"); + } else { + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "15000"); + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "13000"); + } + callbackClient.connect(new ClientConnectionListener(), listenerMetadata); + + Map callbackMetadata = new HashMap<>(); + callbackMetadata.put(Bisocket.IS_CALLBACK_SERVER, "true"); + if (callbackHandler == null) { + callbackHandler = new CallbackHandler(); + } + callbackClient.addListener(callbackHandler, callbackMetadata); + + Set callbackConnectors = callbackClient.getCallbackConnectors(callbackHandler); + if (callbackConnectors.size() != 1) { + logger.warn("There should be one callback Connector (number existing = " + callbackConnectors.size() + ")"); + } + + callbackClient.invoke(null); + + sessionId = callbackClient.getSessionId(); + sessionState = SessionState.CONNECTED; + logger.info("Connected to MAGE server at " + connection.getHost() + ":" + connection.getPort()); + return true; + } + }); + if (result) { + return true; + } + disconnect(false); + return false; + } + private void updateDatabase(boolean forceDBComparison, ServerState serverState) { long cardDBVersion = CardRepository.instance.getContentVersionFromDB(); if (forceDBComparison || serverState.getCardsContentVersion() > cardDBVersion) { @@ -1395,7 +1441,8 @@ public class SessionImpl implements Session { @Override public String getUserName() { - return connection.getUsername(); + String username = connection.getUsername(); + return username == null ? "" : username; } @Override diff --git a/Mage.Common/src/mage/remote/interfaces/Connect.java b/Mage.Common/src/mage/remote/interfaces/Connect.java index 948fc228d1..2b5921e2e7 100644 --- a/Mage.Common/src/mage/remote/interfaces/Connect.java +++ b/Mage.Common/src/mage/remote/interfaces/Connect.java @@ -34,12 +34,12 @@ import mage.remote.Connection; */ public interface Connect { + boolean register(Connection connection); + boolean connect(Connection connection); boolean stopConnecting(); - boolean connect(); - void disconnect(boolean showMessage); void reconnect(Throwable throwable); diff --git a/Mage.Server/src/main/java/mage/server/AuthorizedUser.java b/Mage.Server/src/main/java/mage/server/AuthorizedUser.java index d3a678ccd1..b9b754a698 100644 --- a/Mage.Server/src/main/java/mage/server/AuthorizedUser.java +++ b/Mage.Server/src/main/java/mage/server/AuthorizedUser.java @@ -29,15 +29,19 @@ public class AuthorizedUser { @DatabaseField protected int hashIterations; + @DatabaseField + protected String email; + public AuthorizedUser() { } - public AuthorizedUser(String name, Hash hash) { + public AuthorizedUser(String name, Hash hash, String email) { this.name = name; this.password = hash.toBase64(); this.salt = hash.getSalt().toBase64(); this.hashAlgorithm = hash.getAlgorithmName(); this.hashIterations = hash.getIterations(); + this.email = email; } public boolean doCredentialsMatch(String name, String password) { diff --git a/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java b/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java index 551c8cb6fc..3a56ec15a1 100644 --- a/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java +++ b/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java @@ -27,7 +27,7 @@ public enum AuthorizedUserRepository { private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE"; private static final String VERSION_ENTITY_NAME = "authorized_user"; // raise this if db structure was changed - private static final long DB_VERSION = 0; + private static final long DB_VERSION = 1; private static final RandomNumberGenerator rng = new SecureRandomNumberGenerator(); private Dao dao; @@ -52,14 +52,14 @@ public enum AuthorizedUserRepository { } } - public void add(final String userName, final String password) { + public void add(final String userName, final String password, final String email) { try { dao.callBatchTasks(new Callable() { @Override public Object call() throws Exception { try { Hash hash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, rng.nextBytes(), 1024); - AuthorizedUser user = new AuthorizedUser(userName, hash); + AuthorizedUser user = new AuthorizedUser(userName, hash, email); dao.create(user); } catch (SQLException ex) { Logger.getLogger(AuthorizedUserRepository.class).error("Error adding a user to DB - ", ex); @@ -68,6 +68,7 @@ public enum AuthorizedUserRepository { } }); } catch (Exception ex) { + Logger.getLogger(AuthorizedUserRepository.class).error("Error adding a authorized_user - ", ex); } } @@ -81,6 +82,7 @@ public enum AuthorizedUserRepository { } return null; } catch (SQLException ex) { + Logger.getLogger(AuthorizedUserRepository.class).error("Error getting a authorized_user - ", ex); } return null; } @@ -92,6 +94,7 @@ public enum AuthorizedUserRepository { conn.executeStatement("shutdown compact", 0); } } catch (SQLException ex) { + Logger.getLogger(AuthorizedUserRepository.class).error("Error closing authorized_user repository - ", ex); } } } diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index c4f3b420f4..2ec1ad0aec 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -105,6 +105,11 @@ public class MageServerImpl implements MageServer { ServerMessagesUtil.getInstance().getMessages(); } + @Override + public boolean registerUser(String sessionId, String userName, String password, String email) throws MageException { + return SessionManager.getInstance().registerUser(sessionId, userName, password, email); + } + @Override public boolean registerClient(String userName, String sessionId, MageVersion version) throws MageException { // This method is deprecated, so just inform the server version. @@ -114,14 +119,14 @@ public class MageServerImpl implements MageServer { } @Override - public boolean registerClientWithPassword(String userName, String password, String sessionId, MageVersion version) throws MageException { + public boolean connectUser(String userName, String password, String sessionId, MageVersion version) throws MageException { try { if (version.compareTo(Main.getVersion()) != 0) { logger.info("MageVersionException: userName=" + userName + ", version=" + version); LogServiceImpl.instance.log(LogKeys.KEY_WRONG_VERSION, userName, version.toString(), Main.getVersion().toString(), sessionId); throw new MageVersionException(version, Main.getVersion()); } - return SessionManager.getInstance().registerUser(sessionId, userName, password); + return SessionManager.getInstance().connectUser(sessionId, userName, password); } catch (MageException ex) { if (ex instanceof MageVersionException) { throw (MageVersionException) ex; @@ -142,7 +147,7 @@ public class MageServerImpl implements MageServer { } @Override - public boolean registerAdmin(String adminPassword, String sessionId, MageVersion version) throws MageException { + public boolean connectAdmin(String adminPassword, String sessionId, MageVersion version) throws MageException { try { if (version.compareTo(Main.getVersion()) != 0) { throw new MageException("Wrong client version " + version + ", expecting version " + Main.getVersion()); @@ -150,7 +155,7 @@ public class MageServerImpl implements MageServer { if (!adminPassword.equals(this.adminPassword)) { throw new MageException("Wrong password"); } - return SessionManager.getInstance().registerAdmin(sessionId); + return SessionManager.getInstance().connectAdmin(sessionId); } catch (Exception ex) { handleException(ex); } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 2daabc14a8..d57d710fc9 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -74,20 +74,24 @@ public class Session { this.lock = new ReentrantLock(); } - public String registerUser(String userName, String password) throws MageException { - String returnMessage = registerUserHandling(userName, password); - if (returnMessage != null) { - sendErrorMessageToClient(returnMessage); + public String registerUser(String userName, String password, String email) throws MageException { + synchronized(AuthorizedUserRepository.instance) { + String returnMessage = validateUserName(userName); + if (returnMessage != null) { + sendErrorMessageToClient(returnMessage); + return returnMessage; + } + returnMessage = validatePassword(password); + if (returnMessage != null) { + sendErrorMessageToClient(returnMessage); + return returnMessage; + } + AuthorizedUserRepository.instance.add(userName, password, email); + return null; } - return returnMessage; } - - public boolean isLocked() { - return lock.isLocked(); - } - - public String registerUserHandling(String userName, String password) throws MageException { - this.isAdmin = false; + + static private String validateUserName(String userName) { if (userName.equals("Admin")) { return "User name Admin already in use"; } @@ -102,22 +106,44 @@ public class Session { if (m.find()) { return "User name '" + userName + "' includes not allowed characters: use a-z, A-Z and 0-9"; } - AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.get(userName); - if (authorizedUser == null) { - // Do this in an explicit sign-up flow. - AuthorizedUserRepository.instance.add(userName, password); - } else { - if (!authorizedUser.doCredentialsMatch(userName, password)) { - return "Wrong username or password"; - } + if (authorizedUser != null) { + return "User name '" + userName + "' already in use"; + } + return null; + } + + static private String validatePassword(String password) { + if (password.length() == 0) { + return "Password needs to be non-empty"; + } + return null; + } + + public String connectUser(String userName, String password) throws MageException { + String returnMessage = connectUserHandling(userName, password); + if (returnMessage != null) { + sendErrorMessageToClient(returnMessage); + } + return returnMessage; + } + + public boolean isLocked() { + return lock.isLocked(); + } + + public String connectUserHandling(String userName, String password) throws MageException { + this.isAdmin = false; + AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.get(userName); + if (authorizedUser == null || !authorizedUser.doCredentialsMatch(userName, password)) { + return "Wrong username or password"; } - // TODO: Do an authentication with userName and password. User user = UserManager.getInstance().createUser(userName, host); boolean reconnect = false; if (user == null) { // user already exists user = UserManager.getInstance().findUser(userName); + // TODO: Remove this check since now we do a user authentication. if (user.getHost().equals(host)) { user.updateLastActivity(null); // minimizes possible expiration this.userId = user.getId(); @@ -147,7 +173,7 @@ public class Session { return null; } - public void registerAdmin() { + public void connectAdmin() { this.isAdmin = true; User user = UserManager.getInstance().createUser("Admin", host); if (user == null) { diff --git a/Mage.Server/src/main/java/mage/server/SessionManager.java b/Mage.Server/src/main/java/mage/server/SessionManager.java index bdfbce5a7d..c0507f4b9b 100644 --- a/Mage.Server/src/main/java/mage/server/SessionManager.java +++ b/Mage.Server/src/main/java/mage/server/SessionManager.java @@ -70,14 +70,14 @@ public class SessionManager { sessions.put(sessionId, session); } - public boolean registerUser(String sessionId, String userName, String password) throws MageException { + public boolean registerUser(String sessionId, String userName, String password, String email) throws MageException { Session session = sessions.get(sessionId); if (session != null) { - String returnMessage = session.registerUser(userName, password); + String returnMessage = session.registerUser(userName, password, email); if (returnMessage == null) { - LogServiceImpl.instance.log(LogKeys.KEY_USER_CONNECTED, userName, session.getHost(), sessionId); + LogServiceImpl.instance.log(LogKeys.KEY_USER_REGISTERED, userName, session.getHost(), sessionId); - logger.info(userName + " joined server"); + logger.info(userName + " registered"); logger.debug("- userId: " + session.getUserId()); logger.debug("- sessionId: " + sessionId); logger.debug("- host: " + session.getHost()); @@ -86,15 +86,36 @@ public class SessionManager { logger.debug(userName + " not registered: " + returnMessage); } } else { - logger.error(userName + " tried to join with no sessionId"); + logger.error(userName + " tried to register with no sessionId"); } return false; } - public boolean registerAdmin(String sessionId) { + public boolean connectUser(String sessionId, String userName, String password) throws MageException { Session session = sessions.get(sessionId); if (session != null) { - session.registerAdmin(); + String returnMessage = session.connectUser(userName, password); + if (returnMessage == null) { + LogServiceImpl.instance.log(LogKeys.KEY_USER_CONNECTED, userName, session.getHost(), sessionId); + + logger.info(userName + " connected to server"); + logger.debug("- userId: " + session.getUserId()); + logger.debug("- sessionId: " + sessionId); + logger.debug("- host: " + session.getHost()); + return true; + } else { + logger.debug(userName + " not connected: " + returnMessage); + } + } else { + logger.error(userName + " tried to connect with no sessionId"); + } + return false; + } + + public boolean connectAdmin(String sessionId) { + Session session = sessions.get(sessionId); + if (session != null) { + session.connectAdmin(); LogServiceImpl.instance.log(LogKeys.KEY_ADMIN_CONNECTED, "Admin", session.getHost(), sessionId); logger.info("Admin connected from " + session.getHost()); return true; diff --git a/Mage.Server/src/main/java/mage/server/services/LogKeys.java b/Mage.Server/src/main/java/mage/server/services/LogKeys.java index 45a77a7ef4..93b628025f 100644 --- a/Mage.Server/src/main/java/mage/server/services/LogKeys.java +++ b/Mage.Server/src/main/java/mage/server/services/LogKeys.java @@ -7,6 +7,8 @@ public interface LogKeys { String KEY_GAME_STARTED = "gameStarted"; + String KEY_USER_REGISTERED = "userRegistered"; + String KEY_USER_CONNECTED = "userConnected"; String KEY_ADMIN_CONNECTED = "adminConnected";