/**
* $RCSfile: ,v $
* $Revision: $
* $Date: $
*
* Copyright (C) 2004-2011 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.text.JTextComponent;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.resource.Default;
import org.jivesoftware.resource.Res;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.ChatStateManager;
import org.jivesoftware.spark.SessionManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.Workspace;
import org.jivesoftware.spark.component.RolloverButton;
import org.jivesoftware.spark.util.BrowserLauncher;
import org.jivesoftware.spark.util.DummySSLSocketFactory;
import org.jivesoftware.spark.util.GraphicUtils;
import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.spark.util.ResourceUtils;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettings;
import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettingsManager;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
/**
* Dialog to log in a user into the Spark Server. The LoginDialog is used only
* for login in registered users into the Spark Server.
*/
public class LoginDialog {
private JFrame loginDialog;
private static final String BUTTON_PANEL = "buttonpanel"; // NOTRANS
private static final String PROGRESS_BAR = "progressbar"; // NOTRANS
private LocalPreferences localPref;
private ArrayList<String> _usernames = new ArrayList<String>();
private String loginUsername;
private String loginPassword;
private String loginServer;
/**
* Empty Constructor
*/
public LoginDialog() {
localPref = SettingsManager.getLocalPreferences();
// Check if upgraded needed.
try {
checkForOldSettings();
}
catch (Exception e) {
Log.error(e);
}
}
/**
* Invokes the LoginDialog to be visible.
*
* @param parentFrame the parentFrame of the Login Dialog. This is used
* for correct parenting.
*/
public void invoke(final JFrame parentFrame) {
// Before creating any connections. Update proxy if needed.
try {
updateProxyConfig();
}
catch (Exception e) {
Log.error(e);
}
// Construct Dialog
EventQueue.invokeLater(new Runnable() {
public void run() {
loginDialog = new JFrame(Default.getString(Default.APPLICATION_NAME));
loginDialog.setIconImage(SparkManager.getApplicationImage().getImage());
LoginPanel loginPanel = new LoginPanel();
final JPanel mainPanel = new LoginBackgroundPanel();
final GridBagLayout mainLayout = new GridBagLayout();
mainPanel.setLayout(mainLayout);
final ImagePanel imagePanel = new ImagePanel();
mainPanel.add(imagePanel,
new GridBagConstraints(0, 0, 4, 1,
1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
final String showPoweredBy = Default.getString(Default.SHOW_POWERED_BY);
if (ModelUtil.hasLength(showPoweredBy) && "true".equals(showPoweredBy)) {
// Handle Powered By for custom clients.
final JLabel poweredBy = new JLabel(SparkRes.getImageIcon(SparkRes.POWERED_BY_IMAGE));
mainPanel.add(poweredBy,
new GridBagConstraints(0, 1, 4, 1,
1.0, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 2, 0), 0, 0));
}
loginPanel.setOpaque(false);
mainPanel.add(loginPanel,
new GridBagConstraints(0, 2, 2, 1,
1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
loginDialog.setContentPane(mainPanel);
loginDialog.setLocationRelativeTo(parentFrame);
loginDialog.setResizable(false);
loginDialog.pack();
// Center dialog on screen
GraphicUtils.centerWindowOnScreen(loginDialog);
// Show dialog
loginDialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
quitLogin();
}
});
if (loginPanel.getUsername().trim().length() > 0) {
loginPanel.getPasswordField().requestFocus();
}
if (!localPref.isStartedHidden() || !localPref.isAutoLogin()) {
// Make dialog top most.
loginDialog.setVisible(true);
}
}
});
}
//This method can be overwritten by subclasses to provide additional validations
//(such as certificate download functionality when connecting)
protected boolean beforeLoginValidations() {
return true;
}
protected void afterLogin() {
// Does noting by default - but can be overwritten by subclasses to provide additional
// settings
}
protected ConnectionConfiguration retrieveConnectionConfiguration() {
int port = localPref.getXmppPort();
int checkForPort = loginServer.indexOf(":");
if (checkForPort != -1) {
String portString = loginServer.substring(checkForPort + 1);
if (ModelUtil.hasLength(portString)) {
// Set new port.
port = Integer.valueOf(portString);
}
}
boolean useSSL = localPref.isSSL();
boolean hostPortConfigured = localPref.isHostAndPortConfigured();
ConnectionConfiguration config = null;
if (useSSL) {
if (!hostPortConfigured) {
config = new ConnectionConfiguration(loginServer, 5223);
config.setSocketFactory(new DummySSLSocketFactory());
}
else {
config = new ConnectionConfiguration(localPref.getXmppHost(), port, loginServer);
config.setSocketFactory(new DummySSLSocketFactory());
}
}
else {
if (!hostPortConfigured) {
config = new ConnectionConfiguration(loginServer);
}
else {
config = new ConnectionConfiguration(localPref.getXmppHost(), port, loginServer);
}
}
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(true);
config.setSendPresence(false);
if (localPref.isPKIEnabled()) {
SASLAuthentication.supportSASLMechanism("EXTERNAL");
config.setKeystoreType(localPref.getPKIStore());
if(localPref.getPKIStore().equals("PKCS11")) {
config.setPKCS11Library(localPref.getPKCS11Library());
}
else if(localPref.getPKIStore().equals("JKS")) {
config.setKeystoreType("JKS");
config.setKeystorePath(localPref.getJKSPath());
}
else if(localPref.getPKIStore().equals("X509")) {
//do something
}
else if(localPref.getPKIStore().equals("Apple")) {
config.setKeystoreType("Apple");
}
}
boolean compressionEnabled = localPref.isCompressionEnabled();
config.setCompressionEnabled(compressionEnabled);
if(ModelUtil.hasLength(localPref.getTrustStorePath())) {
config.setTruststorePath(localPref.getTrustStorePath());
config.setTruststorePassword(localPref.getTrustStorePassword());
}
return config;
}
/**
* Define Login Panel implementation.
*/
private final class LoginPanel extends JPanel implements KeyListener, ActionListener, FocusListener, CallbackHandler {
private static final long serialVersionUID = 2445523786538863459L;
private final JLabel usernameLabel = new JLabel();
private final JTextField usernameField = new JTextField();
private final JLabel passwordLabel = new JLabel();
private final JPasswordField passwordField = new JPasswordField();
private final JLabel serverLabel = new JLabel();
private final JTextField serverField = new JTextField();
private final JCheckBox savePasswordBox = new JCheckBox();
private final JCheckBox autoLoginBox = new JCheckBox();
private final RolloverButton loginButton = new RolloverButton();
private final RolloverButton advancedButton = new RolloverButton();
private final RolloverButton quitButton = new RolloverButton();
private final RolloverButton createAccountButton = new RolloverButton();
private final RolloverButton passwordResetButton = new RolloverButton();
private final JLabel progressBar = new JLabel();
// Panel used to hold buttons
private final CardLayout cardLayout = new CardLayout(0, 5);
final JPanel cardPanel = new JPanel(cardLayout);
final JPanel buttonPanel = new JPanel(new GridBagLayout());
private final GridBagLayout GRIDBAGLAYOUT = new GridBagLayout();
private XMPPConnection connection = null;
private JLabel headerLabel = new JLabel();
private JLabel accountLabel = new JLabel();
private JLabel accountNameLabel = new JLabel();
private JLabel serverNameLabel = new JLabel();
private JLabel ssoServerLabel = new JLabel();
private RolloverButton otherUsers = new RolloverButton(SparkRes.getImageIcon(SparkRes.PANE_UP_ARROW_IMAGE));
LoginPanel() {
//setBorder(BorderFactory.createTitledBorder("Sign In Now"));
ResourceUtils.resButton(savePasswordBox, Res.getString("checkbox.save.password"));
ResourceUtils.resButton(autoLoginBox, Res.getString("checkbox.auto.login"));
ResourceUtils.resLabel(serverLabel, serverField, Res.getString("label.server"));
ResourceUtils.resButton(createAccountButton, Res.getString("label.accounts"));
ResourceUtils.resButton(passwordResetButton, Res.getString("label.passwordreset"));
savePasswordBox.setOpaque(false);
autoLoginBox.setOpaque(false);
setLayout(GRIDBAGLAYOUT);
// Set default visibility
headerLabel.setVisible(false);
accountLabel.setVisible(false);
accountNameLabel.setVisible(false);
serverNameLabel.setVisible(false);
headerLabel.setText(Res.getString("title.advanced.connection.sso"));
headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD));
accountLabel.setText("Account:");
ssoServerLabel.setText("Server:");
accountNameLabel.setFont(accountLabel.getFont().deriveFont(Font.BOLD));
serverNameLabel.setFont(ssoServerLabel.getFont().deriveFont(Font.BOLD));
accountNameLabel.setForeground(new Color(106, 127, 146));
serverNameLabel.setForeground(new Color(106, 127, 146));
otherUsers.setFocusable(false);
add(usernameLabel,
new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
add(usernameField,
new GridBagConstraints(1, 0, 2, 1,
1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 5, 0, 0), 0, 0));
add(otherUsers, new GridBagConstraints(3, 0, 1, 1,
0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 0, 0, 0), 0, 0));
add(accountLabel,
new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
add(accountNameLabel,
new GridBagConstraints(1, 1, 1, 1,
1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 5, 0, 5), 0, 0));
add(passwordLabel,
new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 5, 0));
add(passwordField,
new GridBagConstraints(1, 1, 2, 1,
1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 5, 0, 0), 0, 0));
// Add Server Field Properties
add(serverLabel,
new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 5, 0));
add(serverField,
new GridBagConstraints(1, 2, 2, 1,
1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 5, 0, 0), 0, 0));
add(serverNameLabel,
new GridBagConstraints(1, 2, 2, 1,
1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(5, 5, 0, 5), 0, 0));
add(headerLabel,
new GridBagConstraints(0, 5, 2, 1, 1.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0));
add(savePasswordBox,
new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 5), 0, 0));
add(autoLoginBox,
new GridBagConstraints(1, 6, 2, 1, 1.0, 0.0,
GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 5), 0, 0));
// Add button but disable the login button initially
savePasswordBox.addActionListener(this);
autoLoginBox.addActionListener(this);
if (!Default.getBoolean(Default.ACCOUNT_DISABLED)) {
buttonPanel.add(createAccountButton,
new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
}
if (Default.getBoolean(Default.PASSWORD_RESET_ENABLED)) {
buttonPanel.add(passwordResetButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
passwordResetButton.addActionListener(new ActionListener() {
final String url = Default.getString(Default.PASSWORD_RESET_URL);
private static final long serialVersionUID = 2680369963282231348L;
public void actionPerformed(ActionEvent actionEvent) {
try {
BrowserLauncher.openURL(url);
} catch (Exception e) {
Log.error("Unable to load password " +
"reset.", e);
}
}
});
}
if(!Default.getBoolean(Default.ADVANCED_DISABLED)){
buttonPanel.add(advancedButton,
new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0,
GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
}
buttonPanel.add(loginButton,
new GridBagConstraints(3, 0, 4, 1, 1.0, 0.0,
GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 0, 5), 0, 0));
cardPanel.add(buttonPanel, BUTTON_PANEL);
cardPanel.setOpaque(false);
buttonPanel.setOpaque(false);
ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("images/ajax-loader.gif"));
progressBar.setIcon(icon);
cardPanel.add(progressBar, PROGRESS_BAR);
add(cardPanel, new GridBagConstraints(0, 8, 4, 1,
1.0, 1.0, GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
new Insets(2, 2, 2, 2), 0, 0));
loginButton.setEnabled(false);
// Add KeyListener
usernameField.addKeyListener(this);
passwordField.addKeyListener(this);
serverField.addKeyListener(this);
passwordField.addFocusListener(this);
usernameField.addFocusListener(this);
serverField.addFocusListener(this);
// Add ActionListener
quitButton.addActionListener(this);
loginButton.addActionListener(this);
advancedButton.addActionListener(this);
otherUsers.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
getPopup().show(otherUsers, e.getX(), e.getY());
}
});
// Make same size
GraphicUtils.makeSameSize(usernameField, passwordField);
// Set progress bar description
progressBar.setText(Res.getString("message.autenticating"));
progressBar.setVerticalTextPosition(JLabel.BOTTOM);
progressBar.setHorizontalTextPosition(JLabel.CENTER);
progressBar.setHorizontalAlignment(JLabel.CENTER);
// Set Resources
ResourceUtils.resLabel(usernameLabel, usernameField, Res.getString("label.username"));
ResourceUtils.resLabel(passwordLabel, passwordField, Res.getString("label.password"));
ResourceUtils.resButton(quitButton, Res.getString("button.quit"));
ResourceUtils.resButton(loginButton, Res.getString("button.login"));
ResourceUtils.resButton(advancedButton, Res.getString("button.advanced"));
// Load previous instances
String userProp = localPref.getLastUsername();
if(userProp == null || userProp.length() == 0)
userProp = System.getProperty("user.name");
String serverProp = localPref.getServer();
File file = new File(Spark.getSparkUserHome(), "/user/");
File[] userprofiles = file.listFiles();
for (File f : userprofiles) {
if (f.getName().contains("@")) {
_usernames.add(f.getName());
} else {
Log.error("Profile contains wrong format: \"" + f.getName()
+ "\" located at: " + f.getAbsolutePath());
}
}
if (userProp != null) {
usernameField.setText(StringUtils.unescapeNode(userProp));
}
if (serverProp != null) {
serverField.setText(serverProp);
serverNameLabel.setText(serverProp);
}
// Check Settings
if (localPref.isSavePassword()) {
String encryptedPassword = localPref.getPasswordForUser(getBareJid());
if (encryptedPassword != null) {
passwordField.setText(encryptedPassword);
}
savePasswordBox.setSelected(true);
loginButton.setEnabled(true);
}
autoLoginBox.setSelected(localPref.isAutoLogin());
useSSO(localPref.isSSOEnabled());
if (autoLoginBox.isSelected()) {
savePasswordBox.setEnabled(false);
autoLoginBox.setEnabled(false);
validateLogin();
return;
}
// Handle arguments
String username = Spark.getArgumentValue("username");
String password = Spark.getArgumentValue("password");
String server = Spark.getArgumentValue("server");
if (username != null) {
usernameField.setText(username);
}
if (password != null) {
passwordField.setText(password);
}
if (server != null) {
serverField.setText(server);
}
if (username != null && server != null && password != null) {
savePasswordBox.setEnabled(false);
autoLoginBox.setEnabled(false);
validateLogin();
}
createAccountButton.addActionListener(this);
final String lockedDownURL = Default.getString(Default.HOST_NAME);
if (ModelUtil.hasLength(lockedDownURL)) {
serverField.setText(lockedDownURL);
}
if (Default.getBoolean("HOST_NAME_CHANGE_DISABLED"))
serverField.setEnabled(false);
}
/**
* Returns the username the user defined.
*
* @return the username.
*/
private String getUsername() {
return StringUtils.escapeNode(usernameField.getText().trim());
}
/**
* Returns the resulting bareJID from username and server
* @return
*/
private String getBareJid()
{
return usernameField.getText()+"@"+serverField.getText();
}
/**
* Returns the password specified by the user.
*
* @return the password.
*/
private String getPassword() {
return new String(passwordField.getPassword());
}
/**
* Returns the server name specified by the user.
*
* @return the server name.
*/
private String getServerName() {
return serverField.getText().trim();
}
/**
* ActionListener implementation.
*
* @param e the ActionEvent
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == quitButton) {
quitLogin();
}
else if (e.getSource() == createAccountButton) {
AccountCreationWizard createAccountPanel = new AccountCreationWizard();
createAccountPanel.invoke(loginDialog);
if (createAccountPanel.isRegistered()) {
usernameField.setText(createAccountPanel.getUsernameWithoutEscape());
passwordField.setText(createAccountPanel.getPassword());
serverField.setText(createAccountPanel.getServer());
loginButton.setEnabled(true);
}
}
else if (e.getSource() == loginButton) {
validateLogin();
}
else if (e.getSource() == advancedButton) {
final LoginSettingDialog loginSettingsDialog = new LoginSettingDialog();
loginSettingsDialog.invoke(loginDialog);
useSSO(localPref.isSSOEnabled());
}
else if (e.getSource() == savePasswordBox) {
autoLoginBox.setEnabled(savePasswordBox.isSelected());
if (!savePasswordBox.isSelected()) {
autoLoginBox.setSelected(false);
}
}
else if (e.getSource() == autoLoginBox) {
if (autoLoginBox.isSelected()) {
savePasswordBox.setSelected(true);
}
}
}
private JPopupMenu getPopup()
{
JPopupMenu popup = new JPopupMenu();
for(final String key : _usernames)
{
JMenuItem menu = new JMenuItem(key);
final String username = key.split("@")[0];
final String host = key.split("@")[1];
menu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
usernameField.setText(username);
serverField.setText(host);
try {
passwordField.setText(localPref.getPasswordForUser(getBareJid()));
if(passwordField.getPassword().length<1) {
loginButton.setEnabled(false);
}
else {
loginButton.setEnabled(true);
}
} catch (Exception e1) {
}
}
});
popup.add(menu);
}
return popup;
}
/**
* KeyListener implementation.
*
* @param e the KeyEvent to process.
*/
public void keyTyped(KeyEvent e) {
validate(e);
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_RIGHT &&
((JTextField)e.getSource()).getCaretPosition()==((JTextField)e.getSource()).getText().length())
{
getPopup().show(otherUsers,0,0);
}
}
public void keyReleased(KeyEvent e) {
validateDialog();
}
/**
* Checks the users input and enables/disables the login button depending on state.
*/
private void validateDialog() {
loginButton.setEnabled(
ModelUtil.hasLength(getUsername()) &&
( ModelUtil.hasLength(getPassword()) || localPref.isSSOEnabled() ) &&
ModelUtil.hasLength(getServerName()) );
}
/**
* Validates key input.
*
* @param e the keyEvent.
*/
private void validate(KeyEvent e) {
if (loginButton.isEnabled() && e.getKeyChar() == KeyEvent.VK_ENTER) {
validateLogin();
}
}
public void focusGained(FocusEvent e) {
Object o = e.getSource();
if (o instanceof JTextComponent) {
((JTextComponent)o).selectAll();
}
}
public void focusLost(FocusEvent e) {
}
/**
* Enables/Disables the editable components in the login screen.
*
* @param editable true to enable components, otherwise false to disable.
*/
private void enableComponents(boolean editable) {
// Need to set both editable and enabled for best behavior.
usernameField.setEditable(editable);
usernameField.setEnabled(editable);
passwordField.setEditable(editable);
passwordField.setEnabled(editable);
final String lockedDownURL = Default.getString(Default.HOST_NAME);
if (!ModelUtil.hasLength(lockedDownURL)) {
serverField.setEditable(editable);
serverField.setEnabled(editable);
}
if (editable) {
// Reapply focus to username field
passwordField.requestFocus();
}
}
/**
* Displays the progress bar.
*
* @param visible true to display progress bar, false to hide it.
*/
private void setProgressBarVisible(boolean visible) {
if (visible) {
cardLayout.show(cardPanel, PROGRESS_BAR);
// progressBar.setIndeterminate(true);
}
else {
cardLayout.show(cardPanel, BUTTON_PANEL);
}
}
/**
* Validates the users login information.
*/
private void validateLogin() {
final SwingWorker loginValidationThread = new SwingWorker() {
public Object construct() {
setLoginUsername(getUsername());
setLoginPassword(getPassword());
setLoginServer(getServerName());
boolean loginSuccessfull = beforeLoginValidations() && login();
if (loginSuccessfull) {
afterLogin();
progressBar.setText(Res.getString("message.connecting.please.wait"));
// Startup Spark
startSpark();
// dispose login dialog
loginDialog.dispose();
// Show ChangeLog if we need to.
// new ChangeLogDialog().showDialog();
}
else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
savePasswordBox.setEnabled(true);
autoLoginBox.setEnabled(true);
enableComponents(true);
setProgressBarVisible(false);
}
});
}
return loginSuccessfull;
}
};
// Start the login process in seperate thread.
// Disable textfields
enableComponents(false);
// Show progressbar
setProgressBarVisible(true);
loginValidationThread.start();
}
public JPasswordField getPasswordField() {
return passwordField;
}
public Dimension getPreferredSize() {
final Dimension dim = super.getPreferredSize();
dim.height = 200;
return dim;
}
public void useSSO(boolean use) {
if (use) {
usernameLabel.setVisible(true);
usernameField.setVisible(true);
passwordLabel.setVisible(false);
passwordField.setVisible(false);
savePasswordBox.setVisible(false);
accountLabel.setVisible(true);
accountNameLabel.setVisible(true);
serverField.setVisible(true);
autoLoginBox.setVisible(true);
serverLabel.setVisible(true);
headerLabel.setVisible(true);
if(localPref.getDebug()) {
System.setProperty("java.security.krb5.debug","true");
System.setProperty("sun.security.krb5.debug","true");
} else {
System.setProperty("java.security.krb5.debug","false");
System.setProperty("sun.security.krb5.debug","false");
}
String ssoMethod = localPref.getSSOMethod();
if(!ModelUtil.hasLength(ssoMethod)) {
ssoMethod = "file";
}
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
GSSAPIConfiguration config = new GSSAPIConfiguration( ssoMethod.equals("file") );
Configuration.setConfiguration(config);
LoginContext lc;
String princName = localPref.getLastUsername();
String princRealm = null;
try {
lc = new LoginContext("com.sun.security.jgss.krb5.initiate");
lc.login();
Subject mySubject = lc.getSubject();
for (Principal p : mySubject.getPrincipals()) {
//TODO: check if principal is a kerberos principal first...
String name = p.getName();
int indexOne = name.indexOf("@");
if (indexOne != -1) {
princName = name.substring(0, indexOne);
accountNameLabel.setText(name);
princRealm = name.substring(indexOne+1);
}
loginButton.setEnabled(true);
}
}
catch (LoginException le) {
Log.debug(le.getMessage());
accountNameLabel.setText(Res.getString("title.login.no.account"));
//useSSO(false);
}
String ssoKdc;
if(ssoMethod.equals("dns")) {
if (princRealm != null) { //princRealm is null if we got a LoginException above.
ssoKdc = getDnsKdc(princRealm);
System.setProperty("java.security.krb5.realm",princRealm);
System.setProperty("java.security.krb5.kdc",ssoKdc);
}
} else if(ssoMethod.equals("manual")) {
princRealm = localPref.getSSORealm();
ssoKdc = localPref.getSSOKDC();
System.setProperty("java.security.krb5.realm",princRealm);
System.setProperty("java.security.krb5.kdc",ssoKdc);
} else {
//Assume "file" method. We don't have to do anything special,
//java takes care of it for us. Unset the props if they are set
System.clearProperty("java.security.krb5.realm");
System.clearProperty("java.security.krb5.kdc");
}
String userName = localPref.getLastUsername();
if (ModelUtil.hasLength(userName)) {
usernameField.setText(userName);
} else {
usernameField.setText(princName);
}
}
else {
autoLoginBox.setVisible(true);
usernameField.setVisible(true);
passwordField.setVisible(true);
savePasswordBox.setVisible(true);
usernameLabel.setVisible(true);
passwordLabel.setVisible(true);
serverLabel.setVisible(true);
serverField.setVisible(true);
headerLabel.setVisible(false);
accountLabel.setVisible(false);
serverNameLabel.setVisible(false);
accountNameLabel.setVisible(false);
Configuration.setConfiguration(null);
validateDialog();
}
}
/**
* Login to the specified server using username, password, and workgroup.
* Handles error representation as well as logging.
*
* @return true if login was successful, false otherwise
*/
private boolean login() {
final SessionManager sessionManager = SparkManager.getSessionManager();
boolean hasErrors = false;
String errorMessage = null;
// Handle specifyed Workgroup
String serverName = getServerName();
if (!hasErrors) {
localPref = SettingsManager.getLocalPreferences();
if (localPref.isDebuggerEnabled()) {
XMPPConnection.DEBUG_ENABLED = true;
}
SmackConfiguration.setPacketReplyTimeout(localPref.getTimeOut() * 1000);
// Get connection
try {
ConnectionConfiguration config = retrieveConnectionConfiguration();
connection = new XMPPConnection(config,this);
//If we want to use the debug version of smack, we have to check if
//we are on the dispatch thread because smack will create an UI
if (localPref.isDebuggerEnabled()) {
if (EventQueue.isDispatchThread()) {
connection.connect();
} else {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
try {
connection.connect();
} catch (XMPPException e) {
Log.error("connection error",e);
}
}
});
}
} else {
connection.connect();
}
String resource = localPref.getResource();
connection.login(getLoginUsername(), getLoginPassword(),
org.jivesoftware.spark.util.StringUtils.modifyWildcards(resource).trim());
sessionManager.setServerAddress(connection.getServiceName());
sessionManager.initializeSession(connection, getLoginUsername(), getLoginPassword());
sessionManager.setJID(connection.getUser());
}
catch (Exception xee) {
if (!loginDialog.isVisible()) {
loginDialog.setVisible(true);
}
if (xee instanceof XMPPException) {
XMPPException xe = (XMPPException)xee;
final XMPPError error = xe.getXMPPError();
int errorCode = 0;
if (error != null) {
errorCode = error.getCode();
}
if (errorCode == 401) {
errorMessage = Res.getString("message.invalid.username.password");
}
else if (errorCode == 502 || errorCode == 504) {
errorMessage = Res.getString("message.server.unavailable");
}
else if (errorCode == 409) {
errorMessage = Res.getString("label.conflict.error");
}
else {
errorMessage = Res.getString("message.unrecoverable.error");
}
}
else {
errorMessage = SparkRes.getString(SparkRes.UNRECOVERABLE_ERROR);
}
// Log Error
Log.warning("Exception in Login:", xee);
hasErrors = true;
}
}
if (hasErrors) {
final String finalerrorMessage = errorMessage;
EventQueue.invokeLater(new Runnable() {
@Override
public void run()
{
progressBar.setVisible(false);
//progressBar.setIndeterminate(false);
// Show error dialog
if (loginDialog.isVisible()) {
if (!localPref.isSSOEnabled()) {
JOptionPane.showMessageDialog(loginDialog, finalerrorMessage, Res.getString("title.login.error"),
JOptionPane.ERROR_MESSAGE);
}
else {
JOptionPane.showMessageDialog(loginDialog, Res.getString("title.advanced.connection.sso.unable"), Res.getString("title.login.error"),
JOptionPane.ERROR_MESSAGE);
//useSSO(false);
//localPref.setSSOEnabled(false);
}
}
}
});
setEnabled(true);
return false;
}
// Since the connection and workgroup are valid. Add a ConnectionListener
connection.addConnectionListener(SparkManager.getSessionManager());
//Initialize chat state notification mechanism in smack
ChatStateManager.getInstance(SparkManager.getConnection());
// Persist information
localPref.setLastUsername(getLoginUsername());
// Check to see if the password should be saved.
if (savePasswordBox.isSelected()) {
try {
localPref.setPasswordForUser(getBareJid(), getPassword());
}
catch (Exception e) {
Log.error("Error encrypting password.", e);
}
}
localPref.setSavePassword(savePasswordBox.isSelected());
localPref.setAutoLogin(autoLoginBox.isSelected());
// if (localPref.isSSOEnabled()) {
// localPref.setAutoLogin(true);
// }
localPref.setServer(serverField.getText());
SettingsManager.saveSettings();
return !hasErrors;
}
public void handle(Callback[] callbacks) throws IOException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback ncb = (NameCallback) callback;
ncb.setName(getLoginUsername());
} else if (callback instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback) callback;
pcb.setPassword(getPassword().toCharArray());
} else {
Log.error("Unknown callback requested: " + callback.getClass().getSimpleName());
}
}
}
}
/**
* If the user quits, just shut down the
* application.
*/
private void quitLogin() {
System.exit(1);
}
/**
* Initializes Spark and initializes all plugins.
*/
private void startSpark() {
// Invoke the MainWindow.
try
{
EventQueue.invokeLater(new Runnable() {
public void run() {
final MainWindow mainWindow = MainWindow.getInstance();
/*
if (tray != null) {
// Remove trayIcon
tray.removeTrayIcon(trayIcon);
}
*/
// Creates the Spark Workspace and add to MainWindow
Workspace workspace = Workspace.getInstance();
LayoutSettings settings = LayoutSettingsManager.getLayoutSettings();
int x = settings.getMainWindowX();
int y = settings.getMainWindowY();
int width = settings.getMainWindowWidth();
int height = settings.getMainWindowHeight();
LocalPreferences pref = SettingsManager.getLocalPreferences();
if (pref.isDockingEnabled()) {
JSplitPane splitPane = mainWindow.getSplitPane();
workspace.getCardPanel().setMinimumSize(null);
splitPane.setLeftComponent(workspace.getCardPanel());
SparkManager.getChatManager().getChatContainer().setMinimumSize(null);
splitPane.setRightComponent(SparkManager.getChatManager().getChatContainer());
int dividerLoc = settings.getSplitPaneDividerLocation();
if (dividerLoc != -1) {
mainWindow.getSplitPane().setDividerLocation(dividerLoc);
}
else {
mainWindow.getSplitPane().setDividerLocation(240);
}
mainWindow.getContentPane().add(splitPane, BorderLayout.CENTER);
}
else {
mainWindow.getContentPane().add(workspace.getCardPanel(), BorderLayout.CENTER);
}
if (x == 0 && y == 0) {
// Use Default size
mainWindow.setSize(310, 520);
// Center Window on Screen
GraphicUtils.centerWindowOnScreen(mainWindow);
}
else {
mainWindow.setBounds(x, y, width, height);
}
if (loginDialog.isVisible()) {
mainWindow.setVisible(true);
}
loginDialog.setVisible(false);
// Build the layout in the workspace
workspace.buildLayout();
}
});
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Updates System properties with Proxy configuration.
*
* @throws Exception thrown if an exception occurs.
*/
private void updateProxyConfig() throws Exception {
if (ModelUtil.hasLength(Default.getString(Default.PROXY_PORT)) && ModelUtil.hasLength(Default.getString(Default.PROXY_HOST))) {
String port = Default.getString(Default.PROXY_PORT);
String host = Default.getString(Default.PROXY_HOST);
System.setProperty("socksProxyHost", host);
System.setProperty("socksProxyPort", port);
return;
}
boolean proxyEnabled = localPref.isProxyEnabled();
if (proxyEnabled) {
String host = localPref.getHost();
String port = localPref.getPort();
String username = localPref.getProxyUsername();
String password = localPref.getProxyPassword();
String protocol = localPref.getProtocol();
if (protocol.equals("SOCKS")) {
System.setProperty("socksProxyHost", host);
System.setProperty("socksProxyPort", port);
if (ModelUtil.hasLength(username) && ModelUtil.hasLength(password)) {
System.setProperty("java.net.socks.username", username);
System.setProperty("java.net.socks.password", password);
}
}
else {
System.setProperty("http.proxyHost", host);
System.setProperty("http.proxyPort", port);
System.setProperty("https.proxyHost", host);
System.setProperty("https.proxyPort", port);
if (ModelUtil.hasLength(username) && ModelUtil.hasLength(password)) {
System.setProperty("http.proxyUser", username);
System.setProperty("http.proxyPassword", password);
}
}
}
}
/**
* Defines the background to use with the Login panel.
*/
public class LoginBackgroundPanel extends JPanel {
private static final long serialVersionUID = -2449309600851007447L;
final ImageIcon icons = Default.getImageIcon(Default.LOGIN_DIALOG_BACKGROUND_IMAGE);
/**
* Empty constructor.
*/
public LoginBackgroundPanel() {
}
/**
* Uses an image to paint on background.
*
* @param g the graphics.
*/
public void paintComponent(Graphics g) {
Image backgroundImage = icons.getImage();
double scaleX = getWidth() / (double)backgroundImage.getWidth(null);
double scaleY = getHeight() / (double)backgroundImage.getHeight(null);
AffineTransform xform = AffineTransform.getScaleInstance(scaleX, scaleY);
((Graphics2D)g).drawImage(backgroundImage, xform, this);
}
}
/**
* The image panel to display the Spark Logo.
*/
public class ImagePanel extends JPanel {
private static final long serialVersionUID = -1778389077647562606L;
private final ImageIcon icons = Default.getImageIcon(Default.MAIN_IMAGE);
/**
* Uses the Spark logo to paint as the background.
*
* @param g the graphics to use.
*/
public void paintComponent(Graphics g) {
final Image backgroundImage = icons.getImage();
final double scaleX = getWidth() / (double)backgroundImage.getWidth(null);
final double scaleY = getHeight() / (double)backgroundImage.getHeight(null);
AffineTransform xform = AffineTransform.getScaleInstance(scaleX, scaleY);
((Graphics2D)g).drawImage(backgroundImage, xform, this);
}
public Dimension getPreferredSize() {
final Dimension size = super.getPreferredSize();
size.width = icons.getIconWidth();
size.height = icons.getIconHeight();
return size;
}
}
/**
* Checks for historic Spark settings and upgrades the user.
*
* @throws Exception thrown if an error occurs.
*/
private void checkForOldSettings() throws Exception {
// Check for old settings.xml
File settingsXML = new File(Spark.getSparkUserHome(), "/settings.xml");
if (settingsXML.exists()) {
SAXReader saxReader = new SAXReader();
Document pluginXML;
try {
pluginXML = saxReader.read(settingsXML);
}
catch (DocumentException e) {
Log.error(e);
return;
}
List<?> plugins = pluginXML.selectNodes("/settings");
for (Object plugin1 : plugins) {
Element plugin = (Element) plugin1;
String username = plugin.selectSingleNode("username").getText();
localPref.setLastUsername(username);
String server = plugin.selectSingleNode("server").getText();
localPref.setServer(server);
String autoLogin = plugin.selectSingleNode("autoLogin").getText();
localPref.setAutoLogin(Boolean.parseBoolean(autoLogin));
String savePassword = plugin.selectSingleNode("savePassword").getText();
localPref.setSavePassword(Boolean.parseBoolean(savePassword));
String password = plugin.selectSingleNode("password").getText();
localPref.setPasswordForUser(username+"@"+server, password);
SettingsManager.saveSettings();
}
// Delete settings File
settingsXML.delete();
}
}
/**
* Use DNS to lookup a KDC
* @param realm The realm to look up
* @return the KDC hostname
*/
private String getDnsKdc(String realm) {
//Assumption: the KDC will be found with the SRV record
// _kerberos._udp.$realm
try {
Hashtable<String,String> env= new Hashtable<String,String>();
env.put("java.naming.factory.initial","com.sun.jndi.dns.DnsContextFactory");
DirContext context = new InitialDirContext(env);
Attributes dnsLookup = context.getAttributes("_kerberos._udp."+realm, new String[]{"SRV"});
ArrayList<Integer> priorities = new ArrayList<Integer>();
HashMap<Integer,List<String>> records = new HashMap<Integer,List<String>>();
for (Enumeration<?> e = dnsLookup.getAll() ; e.hasMoreElements() ; ) {
Attribute record = (Attribute)e.nextElement();
for (Enumeration<?> e2 = record.getAll() ; e2.hasMoreElements() ; ) {
String sRecord = (String)e2.nextElement();
String [] sRecParts = sRecord.split(" ");
Integer pri = Integer.valueOf(sRecParts[0]);
if(priorities.contains(pri)) {
List<String> recs = records.get(pri);
if(recs == null) recs = new ArrayList<String>();
recs.add(sRecord);
} else {
priorities.add(pri);
List<String> recs = new ArrayList<String>();
recs.add(sRecord);
records.put(pri,recs);
}
}
}
Collections.sort(priorities);
List<String> l = records.get(priorities.get(0));
String toprec = l.get(0);
String [] sRecParts = toprec.split(" ");
return sRecParts[3];
} catch (NamingException e){
return "";
}
}
protected String getLoginUsername() {
return loginUsername;
}
protected void setLoginUsername(String loginUsername) {
this.loginUsername = loginUsername;
}
protected String getLoginPassword() {
return loginPassword;
}
protected void setLoginPassword(String loginPassword) {
this.loginPassword = loginPassword;
}
protected String getLoginServer() {
return loginServer;
}
protected void setLoginServer(String loginServer) {
this.loginServer = loginServer;
}
protected ArrayList<String> getUsernames() {
return _usernames;
}
}