Index: trunk/src/org/openstreetmap/josm/gui/oauth/AbstractAuthorisationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AbstractAuthorisationUI.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AbstractAuthorisationUI.java	(revision 2746)
@@ -0,0 +1,126 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * This is the abstract base class for the three authorisation UIs.
+ * 
+ * 
+ */
+public abstract class AbstractAuthorisationUI extends VerticallyScrollablePanel{
+    /**
+     * The property name for the Access Token property
+     */
+    static public final String ACCESS_TOKEN_PROP = AbstractAuthorisationUI.class.getName() + ".accessToken";
+
+    private String apiUrl;
+    private AdvancedOAuthPropertiesPanel pnlAdvancedProperties;
+    private OAuthToken accessToken;
+
+    protected void fireAccessTokenChanged(OAuthToken oldValue, OAuthToken newValue) {
+        firePropertyChange(ACCESS_TOKEN_PROP, oldValue, newValue);
+    }
+
+    public AbstractAuthorisationUI() {
+        pnlAdvancedProperties = new AdvancedOAuthPropertiesPanel();
+    }
+
+    /**
+     * Replies the URL of the OSM API for which this UI is currently trying to retrieve an OAuth
+     * Access Token
+     * 
+     * @return the API URL
+     */
+    public String getApiUrl() {
+        return apiUrl;
+    }
+
+    /**
+     * Sets the URL of the OSM API for which this UI is currently trying to retrieve an OAuth
+     * Access Token
+     * 
+     * @param apiUrl the api URL
+     */
+    public void setApiUrl(String apiUrl) {
+        this.apiUrl = apiUrl;
+    }
+
+    /**
+     * Replies the panel for entering advanced OAuth parameters (see {@see OAuthParameters})
+     * 
+     * @return the panel for entering advanced OAuth parameters
+     * @see #getOAuthParameters()
+     */
+    protected AdvancedOAuthPropertiesPanel getAdvancedPropertiesPanel() {
+        return pnlAdvancedProperties;
+    }
+
+    /**
+     * Replies the current set of advanced OAuth parameters in this UI
+     * 
+     * @return the current set of advanced OAuth parameters in this UI
+     */
+    public OAuthParameters getOAuthParameters() {
+        return pnlAdvancedProperties.getAdvancedParameters();
+    }
+
+    /**
+     * Replies the retrieved Access Token. null, if no Access Token was retrieved.
+     * 
+     * @return the retrieved Access Token
+     */
+    public  OAuthToken getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets the current Access Token. This will fire a property change event for {@see #ACCESS_TOKEN_PROP}
+     * if the access token has changed
+     * 
+     * @param accessToken the new access token. null, to clear the current access token
+     */
+    protected void setAccessToken(OAuthToken accessToken) {
+        OAuthToken oldValue = this.accessToken;
+        this.accessToken = accessToken;
+        if (oldValue == null ^ this.accessToken == null) {
+            fireAccessTokenChanged(oldValue, this.accessToken);
+        } else if (oldValue == null && this.accessToken == null) {
+            // no change - don't fire an event
+        } else if (! oldValue.equals(this.accessToken)) {
+            fireAccessTokenChanged(oldValue, this.accessToken);
+        }
+    }
+
+    /**
+     * Replies true if this UI currently has an Access Token
+     * 
+     * @return true if this UI currently has an Access Token
+     */
+    public boolean hasAccessToken() {
+        return accessToken != null;
+    }
+
+    /**
+     * Replies whether the user has chosen to save the Access Token in the JOSM
+     * preferences or not.
+     * 
+     * @return true if the user has chosen to save the Access Token
+     */
+    public abstract boolean isSaveAccessTokenToPreferences();
+
+    /**
+     * Initializes the authorisation UI with preference values in <code>pref</code>.
+     * 
+     * @param pref the preferences. Must not be null.
+     * @throws IllegalArgumentException thrown if pref is null
+     */
+    public void initFromPreferences(Preferences pref) throws IllegalArgumentException{
+        CheckParameterUtil.ensureParameterNotNull(pref, "pref");
+        pnlAdvancedProperties.initFromPreferences(pref);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AccessTokenInfoPanel.java	(revision 2746)
@@ -0,0 +1,103 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
+
+/**
+ * Displays the key and the secret of an OAuth Access Token.
+ * 
+ */
+public class AccessTokenInfoPanel extends JPanel {
+
+    private JTextField tfAccessTokenKey;
+    private JTextField tfAccessTokenSecret;
+    private JCheckBox cbSaveAccessTokenInPreferences;
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+
+        // the access token key
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0,0,3,3);
+        add(new JLabel(tr("Access Token Key:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfAccessTokenKey = new JTextField(), gc);
+        tfAccessTokenKey.setEditable(false);
+
+        // the access token secret
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0,0,3,3);
+        add(new JLabel(tr("Access Token Secret:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfAccessTokenSecret = new JTextField(), gc);
+        tfAccessTokenSecret.setEditable(false);
+
+        // the checkbox
+        gc.gridx = 0;
+        gc.gridy = 2;
+        gc.gridwidth = 2;
+        add(cbSaveAccessTokenInPreferences = new JCheckBox(tr("Save Access Token in preferences")), gc);
+        cbSaveAccessTokenInPreferences.setToolTipText(tr(
+                "<html>Select to save the Access Token in the JOSM preferences.<br>"
+                + "Unselect to use the Access Token in this JOSM session only.</html>"
+        ));
+        cbSaveAccessTokenInPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences());
+
+        // filler - grab the remaining space
+        gc.gridx = 0;
+        gc.gridy = 3;
+        gc.weightx = 1.0;
+        gc.weighty = 1.0;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.gridwidth = 2;
+        add(new JPanel(), gc);
+    }
+
+    public AccessTokenInfoPanel() {
+        build();
+    }
+
+    /**
+     * Displays the key and secret in <code>token</code>.
+     * 
+     * @param token the access  token. If null, the content in the info panel is cleared
+     */
+    public void setAccessToken(OAuthToken token) {
+        if (token == null) {
+            tfAccessTokenKey.setText("");
+            tfAccessTokenSecret.setText("");
+            return;
+        }
+        tfAccessTokenKey.setText(token.getKey());
+        tfAccessTokenSecret.setText(token.getSecret());
+    }
+
+    public void setSaveToPreferences(boolean saveToPreferences) {
+        cbSaveAccessTokenInPreferences.setSelected(saveToPreferences);
+    }
+
+    public boolean isSaveToPreferences() {
+        return cbSaveAccessTokenInPreferences.isSelected();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java	(revision 2746)
@@ -0,0 +1,287 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class AdvancedOAuthPropertiesPanel extends VerticallyScrollablePanel {
+
+    private JCheckBox cbUseDefaults;
+    private JTextField tfConsumerKey;
+    private JTextField tfConsumerSecret;
+    private JTextField tfRequestTokenURL;
+    private JTextField tfAccessTokenURL;
+    private JTextField tfAutoriseURL;
+    private UseDefaultItemListener ilUseDefault;
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
+        GridBagConstraints gc = new GridBagConstraints();
+
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        gc.insets = new Insets(0,0, 3, 3);
+        gc.gridwidth = 2;
+        cbUseDefaults = new JCheckBox(tr("Use default settings"));
+        add(cbUseDefaults, gc);
+
+        // -- consumer key
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.gridwidth = 1;
+        add(new JLabel(tr("Consumer Key:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfConsumerKey = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfConsumerKey);
+
+        // -- consumer secret
+        gc.gridy = 2;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("Consumer Secret:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfConsumerSecret = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfConsumerSecret);
+
+        // -- request token URL
+        gc.gridy = 3;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("Request Token URL:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfRequestTokenURL = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfRequestTokenURL);
+
+        // -- access token URL
+        gc.gridy = 4;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("Access Token URL:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfAccessTokenURL = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenURL);
+
+
+        // -- autorise URL
+        gc.gridy = 5;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("Autorise URL:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfAutoriseURL = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfAutoriseURL);
+
+        cbUseDefaults.addItemListener(ilUseDefault = new UseDefaultItemListener());
+    }
+
+    protected boolean hasCustomSettings() {
+        return
+        ! tfConsumerKey.getText().equals( OAuthParameters.DEFAULT_JOSM_CONSUMER_KEY)
+        || ! tfConsumerSecret.getText().equals( OAuthParameters.DEFAULT_JOSM_CONSUMER_SECRET)
+        || ! tfRequestTokenURL.getText().equals( OAuthParameters.DEFAULT_REQUEST_TOKEN_URL)
+        || ! tfAccessTokenURL.getText().equals( OAuthParameters.DEFAULT_ACCESS_TOKEN_URL)
+        || ! tfAutoriseURL.getText().equals( OAuthParameters.DEFAULT_AUTHORISE_URL);
+    }
+
+    protected boolean confirmOverwriteCustomSettings() {
+        ButtonSpec[] buttons = new ButtonSpec[] {
+                new ButtonSpec(
+                        tr("Continue"),
+                        ImageProvider.get("ok"),
+                        tr("Click to reset the OAuth settings to default values"),
+                        null /* no dedicated help topic */
+                ),
+                new ButtonSpec(
+                        tr("Cancel"),
+                        ImageProvider.get("cancel"),
+                        tr("Click to abort resetting to the OAuth default values"),
+                        null /* no dedicated help topic */
+                )
+        };
+        int ret = HelpAwareOptionPane.showOptionDialog(
+                AdvancedOAuthPropertiesPanel.this,
+                tr(
+                        "<html>JOSM is about to reset the OAuth settings to default values.<br>"
+                        + "The current custom settings are not saved.</html>"
+                ),
+                tr("Overwrite custom OAuth settings?"),
+                JOptionPane.WARNING_MESSAGE,
+                null, /* no dedicated icon */
+                buttons,
+                buttons[0],
+                HelpUtil.ht("/Dialog/OAuthAuthorisationWizard")
+        );
+
+        return ret == 0; // OK button clicked
+    }
+
+    protected void resetToDefaultSettings() {
+        cbUseDefaults.setSelected(true);
+        tfConsumerKey.setText( OAuthParameters.DEFAULT_JOSM_CONSUMER_KEY);
+        tfConsumerSecret.setText( OAuthParameters.DEFAULT_JOSM_CONSUMER_SECRET);
+        tfRequestTokenURL.setText(OAuthParameters.DEFAULT_REQUEST_TOKEN_URL);
+        tfAccessTokenURL.setText(OAuthParameters.DEFAULT_ACCESS_TOKEN_URL);
+        tfAutoriseURL.setText(OAuthParameters.DEFAULT_AUTHORISE_URL);
+
+        setChildComponentsEnabled(false);
+    }
+
+    protected void setChildComponentsEnabled(boolean enabled){
+        for (Component c: getComponents()) {
+            if (c instanceof JTextField || c instanceof JLabel) {
+                c.setEnabled(enabled);
+            }
+        }
+    }
+
+    /**
+     * Replies the OAuth parameters currently edited in this properties panel.
+     * 
+     * @return the OAuth parameters
+     */
+    public OAuthParameters getAdvancedParameters() {
+        if (cbUseDefaults.isSelected())
+            return OAuthParameters.createDefault();
+        OAuthParameters parameters = new OAuthParameters();
+        parameters.setConsumerKey(tfConsumerKey.getText());
+        parameters.setConsumerSecret(tfConsumerSecret.getText());
+        parameters.setRequestTokenUrl(tfRequestTokenURL.getText());
+        parameters.setAccessTokenUrl(tfAccessTokenURL.getText());
+        parameters.setAuthoriseUrl(tfAutoriseURL.getText());
+        return parameters;
+    }
+
+    /**
+     * Sets the advanced parameters to be displayed
+     * 
+     * @param parameters the advanced parameters. Must not be null.
+     * @throws IllegalArgumentException thrown if parameters is null.
+     */
+    public void setAdvancedParameters(OAuthParameters parameters) throws IllegalArgumentException{
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        if (parameters.equals(OAuthParameters.createDefault())) {
+            cbUseDefaults.setSelected(true);
+            setChildComponentsEnabled(false);
+        } else {
+            cbUseDefaults.setSelected(false);
+            setChildComponentsEnabled(true);
+            tfConsumerKey.setText( parameters.getConsumerKey() == null ? "" : parameters.getConsumerKey());
+            tfConsumerSecret.setText( parameters.getConsumerSecret() == null ? "" : parameters.getConsumerSecret());
+            tfRequestTokenURL.setText(parameters.getRequestTokenUrl() == null ? "" : parameters.getRequestTokenUrl());
+            tfAccessTokenURL.setText(parameters.getAccessTokenUrl() == null ? "" : parameters.getAccessTokenUrl());
+            tfAutoriseURL.setText(parameters.getAuthoriseUrl() == null ? "" : parameters.getAuthoriseUrl());
+        }
+    }
+
+    public AdvancedOAuthPropertiesPanel() {
+        build();
+    }
+
+    /**
+     * Initializes the panel from the values in the preferences <code>preferences</code>.
+     * 
+     * @param pref the preferences. Must not be null.
+     * @throws IllegalArgumentException thrown if pref is null
+     */
+    public void initFromPreferences(Preferences pref) throws IllegalArgumentException{
+        CheckParameterUtil.ensureParameterNotNull(pref, "pref");
+        boolean useDefault = pref.getBoolean("oauth.settings.use-default", true);
+        ilUseDefault.setEnabled(false);
+        if (useDefault) {
+            resetToDefaultSettings();
+        } else {
+            cbUseDefaults.setSelected(false);
+            tfConsumerKey.setText(pref.get("oauth.settings.consumer-key", ""));
+            tfConsumerSecret.setText(pref.get("oauth.settings.consumer-secret", ""));
+            tfRequestTokenURL.setText(pref.get("oauth.settings.request-token-url", ""));
+            tfAccessTokenURL.setText(pref.get("oauth.settings.access-token-url", ""));
+            tfAutoriseURL.setText(pref.get("oauth.settings.authorise-url", ""));
+            setChildComponentsEnabled(true);
+        }
+        ilUseDefault.setEnabled(true);
+    }
+
+    /**
+     * Remembers the current values in the preferences <code>pref</code>.
+     * 
+     * @param pref the preferences. Must not be null.
+     * @throws IllegalArgumentException thrown if pref is null.
+     */
+    public void rememberPreferences(Preferences pref) throws IllegalArgumentException  {
+        CheckParameterUtil.ensureParameterNotNull(pref, "pref");
+        pref.put("oauth.settings.use-default", cbUseDefaults.isSelected());
+        if (cbUseDefaults.isSelected()) {
+            pref.put("oauth.settings.consumer-key", null);
+            pref.put("oauth.settings.consumer-secret", null);
+            pref.put("oauth.settings.request-token-url", null);
+            pref.put("oauth.settings.access-token-url", null);
+            pref.put("oauth.settings.authorise-url", null);
+        } else {
+            pref.put("oauth.settings.consumer-key", tfConsumerKey.getText().trim());
+            pref.put("oauth.settings.consumer-secret", tfConsumerSecret.getText().trim());
+            pref.put("oauth.settings.request-token-url", tfRequestTokenURL.getText().trim());
+            pref.put("oauth.settings.access-token-url", tfAccessTokenURL.getText().trim());
+            pref.put("oauth.settings.authorise-url", tfAutoriseURL.getText().trim());
+        }
+    }
+
+    class UseDefaultItemListener implements ItemListener {
+        private boolean enabled;
+
+        public void itemStateChanged(ItemEvent e) {
+            if (!enabled) return;
+            switch(e.getStateChange()) {
+            case ItemEvent.SELECTED:
+                if (hasCustomSettings()) {
+                    if (!confirmOverwriteCustomSettings()) {
+                        cbUseDefaults.setSelected(false);
+                        return;
+                    }
+                }
+                resetToDefaultSettings();
+                break;
+            case ItemEvent.DESELECTED:
+                setChildComponentsEnabled(true);
+                break;
+            }
+        }
+
+        public void setEnabled(boolean enabled) {
+            this.enabled = enabled;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedure.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedure.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedure.java	(revision 2746)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+public enum AuthorisationProcedure {
+    /**
+     * Run a fully automatic procedure to get an access token from the OSM website.
+     * JOSM accesses the OSM website on behalf of the JOSM user and interacts
+     * with the site using an OSM session, form posting and screen scraping.
+     */
+    FULLY_AUTOMATIC,
+
+    /**
+     * Run a semi-automatic procedure to get an access token from the OSM website.
+     * JOSM submits the standards OAuth requests to get a Request Token and an
+     * Access Token. It dispatches the user to the OSM website in an external browser
+     * to authenticate itself and to accept the request token submitted by JOSM.
+     */
+    SEMI_AUTOMATIC,
+
+    /**
+     * Enter an Access Token manually. The Access Token could have been generated
+     * by another JOSM user and sent to the current JOSM user via email, i.e. in order
+     * to grant the current OSM user the right download its private GPS traces. Or it could
+     * have been generated in a former session and filed away in a secure place.
+     */
+    MANUALLY
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedureComboBox.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedureComboBox.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AuthorisationProcedureComboBox.java	(revision 2746)
@@ -0,0 +1,103 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.UIManager;
+
+public class AuthorisationProcedureComboBox extends JComboBox {
+
+    public AuthorisationProcedureComboBox() {
+        setModel(new AuthorisationProcedureComboBoxModel());
+        setRenderer(new AuthorisationProcedureCellRenderer());
+        setSelectedItem(AuthorisationProcedure.FULLY_AUTOMATIC);
+    }
+
+    static private class AuthorisationProcedureComboBoxModel extends DefaultComboBoxModel {
+        @Override
+        public Object getElementAt(int index) {
+            switch(index) {
+            case 0: return AuthorisationProcedure.FULLY_AUTOMATIC;
+            case 1: return AuthorisationProcedure.SEMI_AUTOMATIC;
+            case 2: return AuthorisationProcedure.MANUALLY;
+            }
+            return null;
+        }
+
+        @Override
+        public int getSize() {
+            return 3;
+        }
+    }
+
+    static private class AuthorisationProcedureCellRenderer extends JLabel implements ListCellRenderer {
+        public AuthorisationProcedureCellRenderer() {
+            setOpaque(true);
+        }
+
+        protected void renderColors(boolean isSelected) {
+            if (isSelected) {
+                setForeground(UIManager.getColor("List.selectionForeground"));
+                setBackground(UIManager.getColor("List.selectionBackground"));
+            } else {
+                setForeground(UIManager.getColor("List.foreground"));
+                setBackground(UIManager.getColor("List.background"));
+            }
+        }
+
+        protected void renderText(AuthorisationProcedure value) {
+            switch(value) {
+            case FULLY_AUTOMATIC:
+                setText(tr("Fully automatic"));
+                break;
+            case SEMI_AUTOMATIC:
+                setText(tr("Semi-automatic"));
+                break;
+            case MANUALLY:
+                setText(tr("Manual"));
+                break;
+            }
+        }
+
+        protected void renderToolTipText(AuthorisationProcedure value) {
+            switch(value) {
+            case FULLY_AUTOMATIC:
+                setToolTipText(tr(
+                        "<html>Run a fully automatic procedure to get an access token from the OSM website.<br>"
+                        + "JOSM accesses the OSM website on behalf of the JOSM user and fully<br>"
+                        + "automatically authorises the user and retrieves an Access Token.</html>"
+                ));
+                break;
+            case SEMI_AUTOMATIC:
+                setToolTipText(tr(
+                        "<html>Run a semi-automatic procedure to get an access token from the OSM website.<br>"
+                        + "JOSM submits the standards OAuth requests to get a Request Token and an<br>"
+                        + "Access Token. It dispatches the user to the OSM website in an external browser<br>"
+                        + "to authenticate itself and to accept the request token submitted by JOSM.</html>"
+                ));
+                break;
+            case MANUALLY:
+                setToolTipText(tr(
+                        "<html>Enter an Access Token manually if it was generated and retrieved outside<br>"
+                        + "of JOSM.</html>"
+                ));
+                break;
+            }
+        }
+
+        public Component getListCellRendererComponent(JList list, Object value, int idx, boolean isSelected, boolean hasFocus) {
+            AuthorisationProcedure procedure = (AuthorisationProcedure)value;
+            renderColors(isSelected);
+            renderText(procedure);
+            renderToolTipText(procedure);
+            return this;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorisationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorisationUI.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorisationUI.java	(revision 2746)
@@ -0,0 +1,571 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.PasswordAuthentication;
+import java.net.Authenticator.RequestorType;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.html.HTMLEditorKit;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.auth.CredentialsManager;
+import org.openstreetmap.josm.io.auth.CredentialsManagerException;
+import org.openstreetmap.josm.io.auth.CredentialsManagerFactory;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.SAXException;
+
+/**
+ * This is an UI which supports a JOSM user to get an OAuth Access Token in a fully
+ * automatic process.
+ * 
+ */
+public class FullyAutomaticAuthorisationUI extends AbstractAuthorisationUI {
+
+    private JTextField tfUserName;
+    private JPasswordField tfPassword;
+    private UserNameValidator valUserName;
+    private PasswordValidator valPassword;
+    private AccessTokenInfoPanel pnlAccessTokenInfo;
+    private OsmPrivilegesPanel pnlOsmPrivileges;
+    private JPanel pnlPropertiesPanel;
+    private JPanel pnlActionButtonsPanel;
+    private JPanel pnlResult;
+
+    /**
+     * Builds the panel with the three privileges the user can grant JOSM
+     * 
+     * @return
+     */
+    protected VerticallyScrollablePanel buildGrantsPanel() {
+        pnlOsmPrivileges = new OsmPrivilegesPanel();
+        return pnlOsmPrivileges;
+    }
+
+    /**
+     * Builds the panel for entering the username and password
+     * 
+     * @return
+     */
+    protected VerticallyScrollablePanel buildUserNamePasswordPanel() {
+        VerticallyScrollablePanel pnl = new VerticallyScrollablePanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        gc.gridwidth = 2;
+        HtmlPanel pnlMessage = new HtmlPanel();
+        HTMLEditorKit kit = (HTMLEditorKit)pnlMessage.getEditorPane().getEditorKit();
+        kit.getStyleSheet().addRule(".warning-body {background-color:rgb(253,255,221);padding: 10pt; border-color:rgb(128,128,128);border-style: solid;border-width: 1px;}");
+        kit.getStyleSheet().addRule("ol {margin-left: 1cm}");
+        pnlMessage.setText("<html><body>"
+                + "Please enter your OSM user name and password. The password will <strong>not</strong> be saved "
+                + "in clear text in the JOSM preferences and it will be submitted to the OSM server <strong>only once</strong>. "
+                + "Subsequent data upload requests don't use your password any more. "
+                + "</p>"
+                + "</body></html>"
+        );
+        pnl.add(pnlMessage, gc);
+
+        // the user name input field
+        gc.gridy = 1;
+        gc.gridwidth = 1;
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0,0,3,3);
+        pnl.add(new JLabel(tr("Username: ")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfUserName = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfUserName);
+        valUserName = new UserNameValidator(tfUserName);
+        valUserName.validate();
+
+        // the password input field
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.gridy = 2;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Password: ")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfPassword = new JPasswordField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfPassword);
+        valPassword = new PasswordValidator(tfPassword);
+        valPassword.validate();
+
+        gc.gridy = 3;
+        gc.gridx = 0;
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        gc.gridwidth = 2;
+        pnlMessage = new HtmlPanel();
+        kit = (HTMLEditorKit)pnlMessage.getEditorPane().getEditorKit();
+        kit.getStyleSheet().addRule(".warning-body {background-color:rgb(253,255,221);padding: 10pt; border-color:rgb(128,128,128);border-style: solid;border-width: 1px;}");
+        kit.getStyleSheet().addRule("ol {margin-left: 1cm}");
+        pnlMessage.setText("<html><body>"
+                + "<p class=\"warning-body\">"
+                + "<strong>Warning:</strong> The password is transferred <strong>once</strong> in clear text "
+                + "to the OSM website. <strong>Do not</strong> use a sensitive "
+                + "password until the OSM server provides an encrypted communication channel (HTTPS)."
+                + "</p>"
+                + "</body></html>"
+        );
+        pnl.add(pnlMessage, gc);
+
+        // filler - grab remaining space
+        gc.gridy = 4;
+        gc.gridwidth = 2;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.weightx = 1.0;
+        gc.weighty = 1.0;
+        pnl.add(new JPanel(), gc);
+
+        return pnl;
+    }
+
+    protected JPanel buildPropertiesPanel() {
+        JPanel pnl = new JPanel(new BorderLayout());
+
+        JTabbedPane tpProperties = new JTabbedPane();
+        tpProperties.add(VerticallyScrollablePanel.embed(buildUserNamePasswordPanel()));
+        tpProperties.add(VerticallyScrollablePanel.embed(buildGrantsPanel()));
+        tpProperties.add(VerticallyScrollablePanel.embed(getAdvancedPropertiesPanel()));
+        tpProperties.setTitleAt(0, tr("Basic"));
+        tpProperties.setTitleAt(1, tr("Granted rights"));
+        tpProperties.setTitleAt(2, tr("Advanced OAuth properties"));
+
+        pnl.add(tpProperties, BorderLayout.CENTER);
+        return pnl;
+    }
+
+    /**
+     * Initializes the panel with values from the preferences
+     */
+    @Override
+    public void initFromPreferences(Preferences pref) {
+        super.initFromPreferences(pref);
+        CredentialsManager cm = CredentialsManagerFactory.getCredentialManager();
+        try {
+            PasswordAuthentication pa = cm.lookup(RequestorType.SERVER);
+            if (pa == null) {
+                tfUserName.setText("");
+                tfPassword.setText("");
+            } else {
+                tfUserName.setText(pa.getUserName() == null ? "" : pa.getUserName());
+                tfPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
+            }
+        } catch(CredentialsManagerException e) {
+            e.printStackTrace();
+            tfUserName.setText("");
+            tfPassword.setText("");
+        }
+    }
+
+    /**
+     * Builds the panel with the action button  for starting the authorisation
+     * 
+     * @return
+     */
+    protected JPanel buildActionButtonPanel() {
+        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+        RunAuthorisationAction runAuthorisationAction= new RunAuthorisationAction();
+        tfPassword.getDocument().addDocumentListener(runAuthorisationAction);
+        tfUserName.getDocument().addDocumentListener(runAuthorisationAction);
+        pnl.add(new SideButton(runAuthorisationAction));
+        return pnl;
+    }
+
+    /**
+     * Builds the panel which displays the generated Access Token.
+     * 
+     * @return
+     */
+    protected JPanel buildResultsPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());;
+        GridBagConstraints gc = new GridBagConstraints();
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+        // the message panel
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        JMultilineLabel msg = new JMultilineLabel("");
+        msg.setFont(msg.getFont().deriveFont(Font.PLAIN));
+        String lbl = tr("Accept Access Token");
+        msg.setText(tr("<html>"
+                + "You''ve sucessfully retrieved an OAuth Access Token from the OSM website. "
+                + "Click on <strong>{0}</strong> to accept the token. JOSM will use it in "
+                + "subsequent requests to gain access to the OSM API."
+                + "</html",
+                lbl
+        ));
+        pnl.add(msg, gc);
+
+        // infos about the access token
+        gc.gridy = 1;
+        gc.insets = new Insets(5,0,0,0);
+        pnl.add(pnlAccessTokenInfo = new AccessTokenInfoPanel(), gc);
+
+        // the actions
+        JPanel pnl1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        pnl1.add(new SideButton(new BackAction()));
+        pnl1.add(new SideButton(new TestAccessTokenAction()));
+        gc.gridy = 2;
+        pnl.add(pnl1, gc);
+
+        // filler - grab the remaining space
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.weightx = 1.0;
+        gc.weighty = 1.0;
+        pnl.add(new JPanel(), gc);
+
+        return pnl;
+    }
+
+    protected void build() {
+        setLayout(new BorderLayout());
+        pnlPropertiesPanel = buildPropertiesPanel();
+        pnlActionButtonsPanel = buildActionButtonPanel();
+        pnlResult = buildResultsPanel();
+
+        prepareUIForEnteringRequest();
+    }
+
+    /**
+     * Prepares the UI for the first step in the automatic process: entering the authentication
+     * and authorisation parameters.
+     * 
+     */
+    protected void prepareUIForEnteringRequest() {
+        removeAll();
+        add(pnlPropertiesPanel, BorderLayout.CENTER);
+        add(pnlActionButtonsPanel, BorderLayout.SOUTH);
+        pnlPropertiesPanel.revalidate();
+        pnlActionButtonsPanel.revalidate();
+        validate();
+        repaint();
+
+        setAccessToken(null);
+    }
+
+    /**
+     * Prepares the UI for the second step in the automatic process: displaying the access token
+     * 
+     */
+    protected void prepareUIForResultDisplay() {
+        removeAll();
+        add(pnlResult, BorderLayout.CENTER);
+        validate();
+        repaint();
+    }
+
+    protected String getOsmUserName() {
+        return tfUserName.getText();
+    }
+
+    protected String getOsmPassword() {
+        return String.valueOf(tfPassword.getPassword());
+    }
+
+    public FullyAutomaticAuthorisationUI() {
+        build();
+    }
+
+    @Override
+    public boolean isSaveAccessTokenToPreferences() {
+        return pnlAccessTokenInfo.isSaveToPreferences();
+    }
+
+    @Override
+    protected void setAccessToken(OAuthToken accessToken) {
+        super.setAccessToken(accessToken);
+        pnlAccessTokenInfo.setAccessToken(accessToken);
+    }
+
+    /**
+     * Starts the authorisation process
+     */
+    class RunAuthorisationAction extends AbstractAction implements DocumentListener{
+        public RunAuthorisationAction() {
+            putValue(NAME, tr("Authorise now"));
+            putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
+            putValue(SHORT_DESCRIPTION, tr("Click to redirect you to the authorisation form on the JOSM web site"));
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            Main.worker.submit(new FullyAutomaticAuthorisationTask(FullyAutomaticAuthorisationUI.this));
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(valPassword.isValid() && valUserName.isValid());
+        }
+
+        public void changedUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+
+        public void insertUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+
+        public void removeUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+    }
+
+    /**
+     * Action to go back to step 1 in the process
+     */
+    class BackAction extends AbstractAction {
+        public BackAction() {
+            putValue(NAME, tr("Back"));
+            putValue(SHORT_DESCRIPTION, tr("Run the automatic authorisation steps again"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous"));
+        }
+
+        public void actionPerformed(ActionEvent arg0) {
+            prepareUIForEnteringRequest();
+        }
+    }
+
+    /**
+     * Action to test an access token.
+     */
+    class TestAccessTokenAction extends AbstractAction {
+        public TestAccessTokenAction() {
+            putValue(NAME, tr("Test Access Token"));
+            putValue(SHORT_DESCRIPTION, tr(""));
+            putValue(SMALL_ICON, ImageProvider.get("about"));
+        }
+
+        public void actionPerformed(ActionEvent arg0) {
+            Main.worker.submit(new TestAccessTokenTask(
+                    FullyAutomaticAuthorisationUI.this,
+                    getApiUrl(),
+                    getAdvancedPropertiesPanel().getAdvancedParameters(),
+                    getAccessToken()
+            ));
+        }
+    }
+
+
+    static private class UserNameValidator extends AbstractTextComponentValidator {
+        public UserNameValidator(JTextComponent tc) {
+            super(tc);
+        }
+
+        @Override
+        public boolean isValid() {
+            return getComponent().getText().trim().length() > 0;
+        }
+
+        @Override
+        public void validate() {
+            if (isValid()) {
+                feedbackValid(tr("Please enter your OSM user name"));
+            } else {
+                feedbackInvalid(tr("The user name can't be empty. Please enter your OSM user name"));
+            }
+        }
+    }
+
+    static private class PasswordValidator extends AbstractTextComponentValidator {
+
+        public PasswordValidator(JTextComponent tc) {
+            super(tc);
+        }
+
+        @Override
+        public boolean isValid() {
+            return getComponent().getText().trim().length() > 0;
+        }
+
+        @Override
+        public void validate() {
+            if (isValid()) {
+                feedbackValid(tr("Please enter your OSM password"));
+            } else {
+                feedbackInvalid(tr("The password can't be empty. Please enter your OSM password"));
+            }
+        }
+    }
+
+    class FullyAutomaticAuthorisationTask extends PleaseWaitRunnable {
+        private boolean canceled;
+        private OsmOAuthAuthorisationClient authClient;
+
+        public FullyAutomaticAuthorisationTask(Component parent) {
+            super(parent, tr("Authorise JOSM to access the OSM API"), false /* don't ignore exceptions */);
+        }
+
+        @Override
+        protected void cancel() {
+            canceled = true;
+        }
+
+        @Override
+        protected void finish() {}
+
+        protected void alertAutorisationFailed(OsmOAuthAuthorisationException e) {
+            HelpAwareOptionPane.showOptionDialog(
+                    FullyAutomaticAuthorisationUI.this,
+                    tr("<html>"
+                            + "The automatic process for retrieving an OAuth Access Token<br>"
+                            + "from the OSM server failed.<br><br>"
+                            + "Please try again or choose another kind of authorisation process,<br>"
+                            + "i.e. semi-automatic or manual authorisation."
+                            +"</html>"
+                    ),
+                    tr("OAuth authorisation failed"),
+                    JOptionPane.ERROR_MESSAGE,
+                    HelpUtil.ht("/Dialog/OAuthAutorisationWizard#FullyAutomaticProcessFailed")
+            );
+        }
+
+        protected void alertInvalidLoginUrl() {
+            HelpAwareOptionPane.showOptionDialog(
+                    FullyAutomaticAuthorisationUI.this,
+                    tr("<html>"
+                            + "The automatic process for retrieving an OAuth Access Token<br>"
+                            + "from the OSM server failed because JOSM wasn't able to build<br>"
+                            + "a valid login URL from the OAuth Autorise Endpoint URL ''{0}''.<br><br>"
+                            + "Please check your advanced setting and try again."
+                            +"</html>",
+                            getAdvancedPropertiesPanel().getAdvancedParameters().getAuthoriseUrl()
+                    ),
+                    tr("OAuth authorisation failed"),
+                    JOptionPane.ERROR_MESSAGE,
+                    HelpUtil.ht("/Dialog/OAuthAutorisationWizard#FullyAutomaticProcessFailed")
+            );
+        }
+
+        protected void alertLoginFailed(OsmLoginFailedException e) {
+            String loginUrl = null;
+            try {
+                loginUrl = authClient.buildOsmLoginUrl();
+            } catch(OsmOAuthAuthorisationException e1) {
+                alertInvalidLoginUrl();
+                return;
+            }
+            HelpAwareOptionPane.showOptionDialog(
+                    FullyAutomaticAuthorisationUI.this,
+                    tr("<html>"
+                            + "The automatic process for retrieving an OAuth Access Token<br>"
+                            + "from the OSM server failed. JOSM failed to log into {0}<br>"
+                            + "for user {1}.<br><br>"
+                            + "Please check username and password and try again."
+                            +"</html>",
+                            loginUrl,
+                            getOsmUserName()
+                    ),
+                    tr("OAuth authorisation failed"),
+                    JOptionPane.ERROR_MESSAGE,
+                    HelpUtil.ht("/Dialog/OAuthAutorisationWizard#FullyAutomaticProcessFailed")
+            );
+        }
+
+        protected void handleException(final OsmOAuthAuthorisationException e) {
+            Runnable r = new Runnable() {
+                public void run() {
+                    if (e instanceof OsmLoginFailedException) {
+                        alertLoginFailed((OsmLoginFailedException)e);
+                    } else {
+                        alertAutorisationFailed(e);
+                    }
+                }
+            };
+            e.printStackTrace();
+            if (SwingUtilities.isEventDispatchThread()) {
+                r.run();
+            } else {
+                SwingUtilities.invokeLater(r);
+            }
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                getProgressMonitor().setTicksCount(3);
+                authClient = new OsmOAuthAuthorisationClient(
+                        getAdvancedPropertiesPanel().getAdvancedParameters()
+                );
+                OAuthToken requestToken = authClient.getRequestToken(
+                        getProgressMonitor().createSubTaskMonitor(1, false)
+                );
+                getProgressMonitor().worked(1);
+                if (canceled)return;
+                authClient.authorise(
+                        requestToken,
+                        getOsmUserName(),
+                        getOsmPassword(),
+                        pnlOsmPrivileges.getPrivileges(),
+                        getProgressMonitor().createSubTaskMonitor(1, false)
+                );
+                getProgressMonitor().worked(1);
+                if (canceled)return;
+                final OAuthToken accessToken = authClient.getAccessToken(
+                        getProgressMonitor().createSubTaskMonitor(1,false)
+                );
+                getProgressMonitor().worked(1);
+                if (canceled)return;
+                Runnable r = new Runnable() {
+                    public void run() {
+                        prepareUIForResultDisplay();
+                        setAccessToken(accessToken);
+                    }
+                };
+                if (SwingUtilities.isEventDispatchThread()) {
+                    r.run();
+                } else {
+                    SwingUtilities.invokeLater(r);
+                }
+            } catch(final OsmOAuthAuthorisationException e) {
+                handleException(e);
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticPropertiesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticPropertiesPanel.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticPropertiesPanel.java	(revision 2746)
@@ -0,0 +1,101 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+
+public class FullyAutomaticPropertiesPanel extends JPanel {
+
+    private JTextField tfUserName;
+    private JPasswordField tfPassword;
+    private UserNameValidator valUserName;
+
+    private JCheckBox cbWriteMapData;
+    private JCheckBox cbWriteGpsTraces;
+    private JCheckBox cbReadGpsTraces;
+
+
+    protected JPanel buildUserNamePasswordPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.insets = new Insets(0,0,3,3);
+        pnl.add(new JLabel(tr("Username: ")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfUserName = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfUserName);
+        valUserName = new UserNameValidator(tfUserName);
+        valUserName.validate();
+
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.gridy = 1;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Password: ")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfPassword = new JPasswordField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfPassword);
+
+        return pnl;
+    }
+
+
+    public FullyAutomaticPropertiesPanel() {
+        setLayout(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
+
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        add(buildUserNamePasswordPanel(), gc);
+
+        gc.gridy = 1;
+        gc.weighty = 1.0;
+        gc.fill = GridBagConstraints.BOTH;
+        add(new JPanel(), gc);
+    }
+
+    static private class UserNameValidator extends AbstractTextComponentValidator {
+
+        public UserNameValidator(JTextComponent tc) {
+            super(tc);
+        }
+
+        @Override
+        public boolean isValid() {
+            return getComponent().getText().trim().length() > 0;
+        }
+
+        @Override
+        public void validate() {
+            if (isValid()) {
+                feedbackValid(tr("Please enter your OSM user name"));
+            } else {
+                feedbackInvalid(tr("The user name can't be empty. Please enter your OSM user name"));
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/ManualAuthorisationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/ManualAuthorisationUI.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/ManualAuthorisationUI.java	(revision 2746)
@@ -0,0 +1,259 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class ManualAuthorisationUI extends AbstractAuthorisationUI{
+
+    private JTextField tfAccessTokenKey;
+    private AccessTokenKeyValidator valAccessTokenKey;
+    private JTextField tfAccessTokenSecret;
+    private AccessTokenSecretValidator valAccessTokenSecret;
+    private JCheckBox cbSaveToPreferences;
+    private HtmlPanel pnlMessage;
+
+    protected JPanel buildAccessTokenPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        GridBagConstraints gc = new GridBagConstraints();
+        AccessTokenBuilder accessTokenBuilder = new AccessTokenBuilder();
+
+        // the access token key input field
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        gc.gridwidth = 2;
+        gc.insets = new Insets(0,0,5,0);
+        pnlMessage= new HtmlPanel();
+        pnlMessage.setText(tr("<html><body>"
+                + "Please enter an OAuth Access Token which is authorised to access the OSM server "
+                + "''{0}''."
+                + "</body></html>",
+                getApiUrl()
+        ));
+        pnl.add(pnlMessage, gc);
+
+        // the access token key input field
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.gridwidth = 1;
+        gc.insets = new Insets(0,0,0,3);
+        pnl.add(new JLabel(tr("Access Token Key:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfAccessTokenKey = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenKey);
+        valAccessTokenKey = new AccessTokenKeyValidator(tfAccessTokenKey);
+        valAccessTokenKey.validate();
+        tfAccessTokenKey.getDocument().addDocumentListener(accessTokenBuilder);
+
+        // the access token key input field
+        gc.gridy = 2;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        pnl.add(new JLabel(tr("Access Token Secret:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        pnl.add(tfAccessTokenSecret = new JTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenSecret);
+        valAccessTokenSecret = new AccessTokenSecretValidator(tfAccessTokenSecret);
+        valAccessTokenSecret.validate();
+        tfAccessTokenSecret.getDocument().addDocumentListener(accessTokenBuilder);
+
+        // the checkbox for saving to preferences
+        gc.gridy = 3;
+        gc.gridx = 0;
+        gc.gridwidth =2;
+        gc.weightx = 1.0;
+        pnl.add(cbSaveToPreferences = new JCheckBox(tr("Save Access Token to preferences")), gc);
+        cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences());
+
+        // filler - grab remaining space
+        gc.gridy = 3;
+        gc.gridx = 0;
+        gc.gridwidth =2;
+        gc.weightx = 1.0;
+        gc.weighty = 1.0;
+        gc.fill = GridBagConstraints.BOTH;
+        pnl.add(new JPanel(), gc);
+        return pnl;
+    }
+
+    protected JPanel buildTabbedPreferencesPanel() {
+        JPanel pnl = new JPanel(new BorderLayout());
+
+        JTabbedPane tp = new JTabbedPane();
+        tp.add(buildAccessTokenPanel());
+        tp.add(getAdvancedPropertiesPanel());
+
+        tp.setTitleAt(0, tr("Access Token"));
+        tp.setTitleAt(1, tr("Advanced OAuth parameters"));
+
+        tp.setToolTipTextAt(0, tr("Enter the OAuth Access Token"));
+        tp.setToolTipTextAt(1, tr("Enter advanced OAuth properties"));
+
+        pnl.add(tp, BorderLayout.CENTER);
+        return pnl;
+    }
+
+    protected JPanel buildActionsPanel() {
+        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        TestAccessTokenAction actTestAccessToken = new TestAccessTokenAction();
+        pnl.add(new SideButton(actTestAccessToken));
+        this.addPropertyChangeListener(actTestAccessToken);
+        return pnl;
+    }
+
+    @Override
+    public void setApiUrl(String apiUrl) {
+        super.setApiUrl(apiUrl);
+        pnlMessage.setText(tr("<html><body>"
+                + "Please enter an OAuth Access Token which is authorised to access the OSM server "
+                + "''{0}''."
+                + "</body></html>",
+                getApiUrl()
+        ));
+    }
+
+    protected void build() {
+        setLayout(new BorderLayout());
+        setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        add(buildTabbedPreferencesPanel(), BorderLayout.CENTER);
+        add(buildActionsPanel(), BorderLayout.SOUTH);
+    }
+
+    public ManualAuthorisationUI() {
+        build();
+    }
+
+    @Override
+    public boolean isSaveAccessTokenToPreferences() {
+        return cbSaveToPreferences.isSelected();
+    }
+
+    static private class AccessTokenKeyValidator extends AbstractTextComponentValidator {
+
+        public AccessTokenKeyValidator(JTextComponent tc) throws IllegalArgumentException {
+            super(tc);
+        }
+
+        @Override
+        public boolean isValid() {
+            return ! getComponent().getText().trim().equals("");
+        }
+
+        @Override
+        public void validate() {
+            if (isValid()) {
+                feedbackValid(tr("Please enter an Access Token Key"));
+            } else {
+                feedbackInvalid(tr("The Access Token Key must not be empty. Please enter an Access Token Key"));
+            }
+        }
+    }
+
+    static private class AccessTokenSecretValidator extends AbstractTextComponentValidator {
+        public AccessTokenSecretValidator(JTextComponent tc) throws IllegalArgumentException {
+            super(tc);
+        }
+
+        @Override
+        public boolean isValid() {
+            return ! getComponent().getText().trim().equals("");
+        }
+
+        @Override
+        public void validate() {
+            if (isValid()) {
+                feedbackValid(tr("Please enter an Access Token Secret"));
+            } else {
+                feedbackInvalid(tr("The Access Token Secret must not be empty. Please enter an Access Token Secret"));
+            }
+        }
+    }
+
+    class AccessTokenBuilder implements DocumentListener {
+
+        public void build() {
+            if (! valAccessTokenKey.isValid() || !valAccessTokenSecret.isValid()) {
+                setAccessToken(null);
+            } else {
+                setAccessToken(new OAuthToken(tfAccessTokenKey.getText().trim(), tfAccessTokenSecret.getText().trim()));
+            }
+        }
+        public void changedUpdate(DocumentEvent e) {
+            build();
+        }
+
+        public void insertUpdate(DocumentEvent e) {
+            build();
+        }
+
+        public void removeUpdate(DocumentEvent e) {
+            build();
+        }
+    }
+
+    /**
+     * Action for testing an Access Token
+     */
+    class TestAccessTokenAction extends AbstractAction implements PropertyChangeListener {
+        public TestAccessTokenAction() {
+            putValue(NAME, tr("Test Access Token"));
+            putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
+            putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token"));
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            TestAccessTokenTask task = new TestAccessTokenTask(
+                    ManualAuthorisationUI.this,
+                    getApiUrl(),
+                    getAdvancedPropertiesPanel().getAdvancedParameters(),
+                    getAccessToken()
+            );
+            Main.worker.submit(task);
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(hasAccessToken());
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (! evt.getPropertyName().equals(AbstractAuthorisationUI.ACCESS_TOKEN_PROP))
+                return;
+            updateEnabledState();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorisationWizard.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorisationWizard.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorisationWizard.java	(revision 2746)
@@ -0,0 +1,386 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.logging.Logger;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+/**
+ * This wizard walks the user to the necessary steps to retrieve an OAuth Access Token which
+ * allows JOSM to access the OSM API on the users behalf.
+ * 
+ */
+public class OAuthAuthorisationWizard extends JDialog {
+    static private final Logger logger = Logger.getLogger(OAuthAuthorisationWizard.class.getName());
+
+    private HtmlPanel pnlMessage;
+    private boolean canceled;
+    private String apiUrl;
+
+    private AuthorisationProcedureComboBox cbAuthorisationProcedure;
+    private FullyAutomaticAuthorisationUI pnlFullyAutomaticAuthorisationUI;
+    private SemiAutomaticAuthorisationUI pnlSemiAutomaticAuthorisationUI;
+    private ManualAuthorisationUI pnlManualAuthorisationUI;
+    private JScrollPane spAuthorisationProcedureUI;
+
+    /**
+     * Builds the row with the action buttons
+     * 
+     * @return
+     */
+    protected JPanel buildButtonRow(){
+        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
+
+        AcceptAccessTokenAction actAcceptAccessToken = new AcceptAccessTokenAction();
+        pnlFullyAutomaticAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
+        pnlSemiAutomaticAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
+        pnlManualAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
+
+        pnl.add(new SideButton(actAcceptAccessToken));
+        pnl.add(new SideButton(new CancelAction()));
+        pnl.add(new SideButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/OAuthAuthorisationWizard"))));
+
+        return pnl;
+    }
+
+    /**
+     * Builds the panel with general information in the header
+     * 
+     * @return
+     */
+    protected JPanel buildHeaderInfoPanel() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        GridBagConstraints gc = new GridBagConstraints();
+
+        // the oauth logo in the header
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        gc.gridwidth = 2;
+        JLabel lbl = new JLabel();
+        lbl.setIcon(ImageProvider.get("oauth", "oauth-logo"));
+        lbl.setOpaque(true);
+        pnl.add(lbl, gc);
+
+        // OAuth in a nutshell ...
+        gc.gridy  = 1;
+        gc.insets = new Insets(5,0,0,5);
+        pnlMessage = new HtmlPanel();
+        pnlMessage.setText("<html><body>"
+                + "With OAuth you grant JOSM the right to upload map data and GPS tracks "
+                + "on your behalf (<a href=\"urn:josm-oauth-info\">more info...</a>)."
+                + "</body></html>"
+        );
+        pnl.add(pnlMessage, gc);
+
+        // the authorisation procedure
+        gc.gridy  = 2;
+        gc.gridwidth = 1;
+        gc.weightx = 0.0;
+        lbl = new JLabel(tr("Please select an authorisation procedure: "));
+        lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN));
+        pnl.add(lbl,gc);
+
+        gc.gridx = 1;
+        gc.gridwidth = 1;
+        gc.weightx = 1.0;
+        pnl.add(cbAuthorisationProcedure = new AuthorisationProcedureComboBox(),gc);
+        cbAuthorisationProcedure.addItemListener(new AuthorisationProcedureChangeListener());
+        return pnl;
+    }
+
+    /**
+     * Refreshes the view of the authorisation panel, depending on the authorisation procedure
+     * currently selected
+     */
+    protected void refreshAuthorisationProcedurePanel() {
+        AuthorisationProcedure procedure = (AuthorisationProcedure)cbAuthorisationProcedure.getSelectedItem();
+        switch(procedure) {
+        case FULLY_AUTOMATIC:
+            spAuthorisationProcedureUI.getViewport().setView(pnlFullyAutomaticAuthorisationUI);
+            pnlFullyAutomaticAuthorisationUI.revalidate();
+            break;
+        case SEMI_AUTOMATIC:
+            spAuthorisationProcedureUI.getViewport().setView(pnlSemiAutomaticAuthorisationUI);
+            pnlSemiAutomaticAuthorisationUI.revalidate();
+            break;
+        case MANUALLY:
+            spAuthorisationProcedureUI.getViewport().setView(pnlManualAuthorisationUI);
+            pnlManualAuthorisationUI.revalidate();
+            break;
+        }
+        validate();
+        repaint();
+    }
+
+    /**
+     * builds the UI
+     */
+    protected void build() {
+        getContentPane().setLayout(new BorderLayout());
+        getContentPane().add(buildHeaderInfoPanel(), BorderLayout.NORTH);
+
+        pnlFullyAutomaticAuthorisationUI = new FullyAutomaticAuthorisationUI();
+        pnlFullyAutomaticAuthorisationUI.setApiUrl(apiUrl);
+
+        pnlSemiAutomaticAuthorisationUI = new SemiAutomaticAuthorisationUI();
+        pnlSemiAutomaticAuthorisationUI.setApiUrl(apiUrl);
+
+        pnlManualAuthorisationUI = new ManualAuthorisationUI();
+        pnlManualAuthorisationUI.setApiUrl(apiUrl);
+
+        spAuthorisationProcedureUI = new JScrollPane(new JPanel());
+        spAuthorisationProcedureUI.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        spAuthorisationProcedureUI.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+        spAuthorisationProcedureUI.getVerticalScrollBar().addComponentListener(
+                new ComponentListener() {
+                    public void componentShown(ComponentEvent e) {
+                        spAuthorisationProcedureUI.setBorder(UIManager.getBorder("ScrollPane.border"));
+                    }
+
+                    public void componentHidden(ComponentEvent e) {
+                        spAuthorisationProcedureUI.setBorder(null);
+                    }
+
+                    public void componentResized(ComponentEvent e) {}
+                    public void componentMoved(ComponentEvent e) {}
+                }
+        );
+        getContentPane().add(spAuthorisationProcedureUI, BorderLayout.CENTER);
+        getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
+
+        addWindowListener(new WindowEventHandler());
+        getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
+        getRootPane().getActionMap().put("cancel", new CancelAction());
+
+        refreshAuthorisationProcedurePanel();
+
+        HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/OAuthAuthorisationWizard"));
+    }
+
+    /**
+     * Creates the wizard.
+     * 
+     * @param apiUrl the API URL. Must not be null.
+     * @throws IllegalArgumentException thrown if apiUrl is null
+     */
+    public OAuthAuthorisationWizard(String apiUrl) throws IllegalArgumentException {
+        super(JOptionPane.getFrameForComponent(Main.parent),true /* modal */);
+        CheckParameterUtil.ensureParameterNotNull(apiUrl, "apiUrl");
+        build();
+        setApiUrl(apiUrl);
+    }
+
+    /**
+     * Creates the wizard.
+     * 
+     * @param parent the component relative to which the dialog is displayed
+     * @param apiUrl the API URL. Must not be null.
+     * @throws IllegalArgumentException thrown if apiUrl is null
+     */
+    public OAuthAuthorisationWizard(Component parent, String apiUrl) {
+        super(JOptionPane.getFrameForComponent(parent),true /* modal */);
+        CheckParameterUtil.ensureParameterNotNull(apiUrl, "apiUrl");
+        build();
+        setApiUrl(apiUrl);
+    }
+
+    /**
+     * Sets the API URL for the API for which this wizard is generating
+     * an Access Token.
+     * 
+     * @param apiUrl the API URL. Must not be null.
+     * @throws IllegalArgumentException thrown if apiUrl is null
+     */
+    public void setApiUrl(String apiUrl) throws IllegalArgumentException{
+        CheckParameterUtil.ensureParameterNotNull(apiUrl, "apiUrl");
+        this.apiUrl = apiUrl;
+        setTitle(tr("Get an Access Token for ''{0}''", apiUrl));
+        if (pnlFullyAutomaticAuthorisationUI != null) {
+            pnlFullyAutomaticAuthorisationUI.setApiUrl(apiUrl);
+        }
+        if (pnlSemiAutomaticAuthorisationUI != null) {
+            pnlSemiAutomaticAuthorisationUI.setApiUrl(apiUrl);
+        }
+        if (pnlManualAuthorisationUI != null) {
+            pnlManualAuthorisationUI.setApiUrl(apiUrl);
+        }
+
+    }
+
+    /**
+     * Replies true if the dialog was cancelled
+     * 
+     * @return true if the dialog was cancelled
+     */
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    protected AbstractAuthorisationUI getCurrentAuthorisationUI() {
+        switch((AuthorisationProcedure)cbAuthorisationProcedure.getSelectedItem()) {
+        case FULLY_AUTOMATIC: return pnlFullyAutomaticAuthorisationUI;
+        case MANUALLY: return pnlManualAuthorisationUI;
+        case SEMI_AUTOMATIC: return pnlSemiAutomaticAuthorisationUI;
+        default: return null;
+        }
+    }
+
+    /**
+     * Replies the Access Token entered using the wizard
+     * 
+     * @return the access token. May be null if the wizard was canceled.
+     */
+    public OAuthToken getAccessToken() {
+        return getCurrentAuthorisationUI().getAccessToken();
+    }
+
+    /**
+     * Replies the current OAuth parameters.
+     * 
+     * @return the current OAuth parameters.
+     */
+    public OAuthParameters getOAuthParameters() {
+        return getCurrentAuthorisationUI().getOAuthParameters();
+    }
+
+    /**
+     * Replies true if the currently selected Access Token shall be saved to
+     * the preferences.
+     * 
+     * @return true if the currently selected Access Token shall be saved to
+     * the preferences
+     */
+    public boolean isSaveAccessTokenToPreferences() {
+        return getCurrentAuthorisationUI().isSaveAccessTokenToPreferences();
+    }
+
+    /**
+     * Initializes the dialog with values from the preferences
+     * 
+     */
+    public void initFromPreferences() {
+        pnlFullyAutomaticAuthorisationUI.initFromPreferences(Main.pref);
+        pnlSemiAutomaticAuthorisationUI.initFromPreferences(Main.pref);
+        pnlManualAuthorisationUI.initFromPreferences(Main.pref);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            new WindowGeometry(
+                    getClass().getName() + ".geometry",
+                    WindowGeometry.centerInWindow(
+                            Main.parent,
+                            new Dimension(400,400)
+                    )
+            ).apply(this);
+            initFromPreferences();
+        } else if (!visible && isShowing()){
+            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
+        }
+        super.setVisible(visible);
+    }
+
+    protected void setCanceled(boolean canceled) {
+        this.canceled = canceled;
+    }
+
+    class AuthorisationProcedureChangeListener implements ItemListener {
+        public void itemStateChanged(ItemEvent arg0) {
+            refreshAuthorisationProcedurePanel();
+        }
+    }
+
+    class CancelAction extends AbstractAction {
+        public CancelAction() {
+            putValue(NAME, tr("Cancel"));
+            putValue(SMALL_ICON, ImageProvider.get("cancel"));
+            putValue(SHORT_DESCRIPTION, tr("Close the dialog and cancel authorisation"));
+        }
+
+        public void cancel() {
+            setCanceled(true);
+            setVisible(false);
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            cancel();
+        }
+    }
+
+    class AcceptAccessTokenAction extends AbstractAction implements PropertyChangeListener {
+        private OAuthToken token;
+
+        public AcceptAccessTokenAction() {
+            putValue(NAME, tr("Accept Access Token"));
+            putValue(SMALL_ICON, ImageProvider.get("ok"));
+            putValue(SHORT_DESCRIPTION, tr("Close the dialog and accept the Access Token"));
+            updateEnabledState(null);
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            setCanceled(false);
+            setVisible(false);
+        }
+
+        public void updateEnabledState(OAuthToken token) {
+            setEnabled(token != null);
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (!evt.getPropertyName().equals(AbstractAuthorisationUI.ACCESS_TOKEN_PROP))
+                return;
+            token = (OAuthToken)evt.getNewValue();
+            updateEnabledState(token);
+        }
+    }
+
+    class WindowEventHandler extends WindowAdapter {
+        @Override
+        public void windowClosing(WindowEvent arg0) {
+            new CancelAction().cancel();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OsmLoginFailedException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OsmLoginFailedException.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OsmLoginFailedException.java	(revision 2746)
@@ -0,0 +1,25 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+public class OsmLoginFailedException extends OsmOAuthAuthorisationException{
+
+    public OsmLoginFailedException() {
+        super();
+        // TODO Auto-generated constructor stub
+    }
+
+    public OsmLoginFailedException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+        // TODO Auto-generated constructor stub
+    }
+
+    public OsmLoginFailedException(String arg0) {
+        super(arg0);
+        // TODO Auto-generated constructor stub
+    }
+
+    public OsmLoginFailedException(Throwable arg0) {
+        super(arg0);
+        // TODO Auto-generated constructor stub
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationClient.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationClient.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationClient.java	(revision 2746)
@@ -0,0 +1,517 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+import oauth.signpost.OAuth;
+import oauth.signpost.OAuthConsumer;
+import oauth.signpost.OAuthProvider;
+import oauth.signpost.basic.DefaultOAuthProvider;
+import oauth.signpost.exception.OAuthCommunicationException;
+import oauth.signpost.exception.OAuthException;
+
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.data.oauth.OsmPrivileges;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferCancelledException;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+public class OsmOAuthAuthorisationClient {
+    static private final Logger logger = Logger.getLogger(OsmOAuthAuthorisationClient.class.getName());
+
+    private OAuthParameters oauthProviderParameters;
+    private OAuthConsumer consumer;
+    private OAuthProvider provider;
+    private boolean canceled;
+    private HttpURLConnection connection;
+
+    /**
+     * Creates a new authorisation client with default OAuth parameters
+     * 
+     */
+    public OsmOAuthAuthorisationClient() {
+        oauthProviderParameters = OAuthParameters.createDefault();
+        consumer = oauthProviderParameters.buildConsumer();
+        provider = oauthProviderParameters.buildProvider(consumer);
+    }
+
+    /**
+     * Creates a new authorisation client with the parameters <code>parameters</code>.
+     * 
+     * @param parameters the OAuth parameters. Must not be null.
+     * @throws IllegalArgumentException thrown if parameters is null
+     */
+    public OsmOAuthAuthorisationClient(OAuthParameters parameters) throws IllegalArgumentException {
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        oauthProviderParameters = new OAuthParameters(parameters);
+        consumer = oauthProviderParameters.buildConsumer();
+        provider = oauthProviderParameters.buildProvider(consumer);
+    }
+
+    /**
+     * Creates a new authorisation client with the parameters <code>parameters</code>
+     * and an already known Request Token.
+     * 
+     * @param parameters the OAuth parameters. Must not be null.
+     * @param requestToken the request token. Must not be null.
+     * @throws IllegalArgumentException thrown if parameters is null
+     * @throws IllegalArgumentException thrown if requestToken is null
+     */
+    public OsmOAuthAuthorisationClient(OAuthParameters parameters, OAuthToken requestToken) throws IllegalArgumentException {
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        oauthProviderParameters = new OAuthParameters(parameters);
+        consumer = oauthProviderParameters.buildConsumer();
+        provider = oauthProviderParameters.buildProvider(consumer);
+        consumer.setTokenWithSecret(requestToken.getKey(), requestToken.getSecret());
+    }
+
+    public void cancel() {
+        DefaultOAuthProvider p  = (DefaultOAuthProvider)provider;
+        canceled = true;
+        if (p != null) {
+            try {
+                Field f =  p.getClass().getDeclaredField("connection");
+                f.setAccessible(true);
+                HttpURLConnection con = (HttpURLConnection)f.get(p);
+                if (con != null) {
+                    con.disconnect();
+                }
+            } catch(NoSuchFieldException e) {
+                e.printStackTrace();
+                System.err.println(tr("Warning: failed to cancel running OAuth operation"));
+            } catch(SecurityException e) {
+                e.printStackTrace();
+                System.err.println(tr("Warning: failed to cancel running OAuth operation"));
+            } catch(IllegalAccessException e) {
+                e.printStackTrace();
+                System.err.println(tr("Warning: failed to cancel running OAuth operation"));
+            }
+        }
+        synchronized(this) {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    /**
+     * Submits a request for a Request Token to the Request Token Endpoint Url of the OAuth Service
+     * Provider and replies the request token.
+     * 
+     * @param monitor a progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null
+     * @return the OAuth Request Token
+     * @throws OsmOAuthAuthorisationException thrown if something goes wrong when retrieving the request token
+     */
+    public OAuthToken getRequestToken(ProgressMonitor monitor) throws OsmOAuthAuthorisationException, OsmTransferCancelledException {
+        if (monitor == null) {
+            monitor = NullProgressMonitor.INSTANCE;
+        }
+        try {
+            monitor.beginTask("");
+            monitor.indeterminateSubTask(tr("Retrieving OAuth Request Token from ''{0}''", oauthProviderParameters.getRequestTokenUrl()));
+            provider.retrieveRequestToken(null);
+            return OAuthToken.createToken(consumer);
+        } catch(OAuthCommunicationException e){
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            throw new OsmOAuthAuthorisationException(e);
+        } catch(OAuthException e){
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            throw new OsmOAuthAuthorisationException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
+
+    /**
+     * Submits a request for an Access Token to the Access Token Endpoint Url of the OAuth Service
+     * Provider and replies the request token.
+     * 
+     * You must have requested a Request Token using {@see #getRequestToken(ProgressMonitor)} first.
+     * 
+     * @param monitor a progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null
+     * @return the OAuth Access Token
+     * @throws OsmOAuthAuthorisationException thrown if something goes wrong when retrieving the request token
+     * @see #getRequestToken(ProgressMonitor)
+     */
+    public OAuthToken getAccessToken(ProgressMonitor monitor) throws OsmOAuthAuthorisationException, OsmTransferCancelledException {
+        if (monitor == null) {
+            monitor = NullProgressMonitor.INSTANCE;
+        }
+        try {
+            monitor.beginTask("");
+            monitor.indeterminateSubTask(tr("Retrieving OAuth Access Token from ''{0}''", oauthProviderParameters.getAccessTokenUrl()));
+            provider.retrieveAccessToken(null);
+            return OAuthToken.createToken(consumer);
+        } catch(OAuthCommunicationException e){
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            throw new OsmOAuthAuthorisationException(e);
+        } catch(OAuthException e){
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            throw new OsmOAuthAuthorisationException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
+
+    /**
+     * Builds the authorise URL for a given Request Token. Users can be redirected to this URL.
+     * There they can login to OSM and authorise the request.
+     * 
+     * @param requestToken  the request token
+     * @return  the authorise URL for this request
+     */
+    public String getAuthoriseUrl(OAuthToken requestToken) {
+        StringBuilder sb = new StringBuilder();
+
+        // OSM is an OAuth 1.0 provider and JOSM isn't a web app. We just add the oauth request token to
+        // the authorisation request, no callback parameter.
+        //
+        sb.append(oauthProviderParameters.getAuthoriseUrl()).append("?")
+        .append(OAuth.OAUTH_TOKEN).append("=").append(requestToken.getKey());
+        return sb.toString();
+    }
+
+    protected String extractOsmSession(HttpURLConnection connection) {
+        List<String> setCookies = connection.getHeaderFields().get("Set-Cookie");
+        if (setCookies == null)
+            // no cookies set
+            return null;
+
+        for (String setCookie: setCookies) {
+            String[] kvPairs = setCookie.split(";");
+            if (kvPairs == null || kvPairs.length == 0) {
+                continue;
+            }
+            for (String kvPair : kvPairs) {
+                kvPair = kvPair.trim();
+                String [] kv = kvPair.split("=");
+                if (kv == null || kv.length != 2) {
+                    continue;
+                }
+                if (kv[0].equals("_osm_session"))
+                    // osm session cookie found
+                    return kv[1];
+            }
+        }
+        return null;
+    }
+
+    protected String buildPostRequest(Map<String,String> parameters) throws OsmOAuthAuthorisationException {
+        try {
+            StringBuilder sb = new StringBuilder();
+
+            for(Iterator<Entry<String,String>> it = parameters.entrySet().iterator(); it.hasNext();) {
+                Entry<String,String> entry = it.next();
+                String value = entry.getValue();
+                value = (value == null) ? "" : value;
+                sb.append(entry.getKey()).append("=").append(URLEncoder.encode(value, "UTF-8"));
+                if (it.hasNext()) {
+                    sb.append("&");
+                }
+            }
+            return sb.toString();
+        } catch(UnsupportedEncodingException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        }
+    }
+
+    /**
+     * Derives the OSM login URL from the OAuth Authorization Website URL
+     * 
+     * @return the OSM login URL
+     * @throws OsmOAuthAuthorisationException thrown if something went wrong, in particular if the
+     * URLs are malformed
+     */
+    public String buildOsmLoginUrl() throws OsmOAuthAuthorisationException{
+        try {
+            URL autUrl = new URL(oauthProviderParameters.getAuthoriseUrl());
+            // FIXME: as soon as the OSM website provides HTTPS protected access to the login
+            // page we can replace the protocol with https
+            //
+            URL url = new URL("http", autUrl.getHost(), autUrl.getPort(), "/login");
+            return url.toString();
+        } catch(MalformedURLException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        }
+    }
+
+    /**
+     * Derives the OSM logout URL from the OAuth Authorization Website URL
+     * 
+     * @return the OSM logout URL
+     * @throws OsmOAuthAuthorisationException thrown if something went wrong, in particular if the
+     * URLs are malformed
+     */
+    protected String buildOsmLogoutUrl() throws OsmOAuthAuthorisationException{
+        try {
+            URL autUrl = new URL(oauthProviderParameters.getAuthoriseUrl());
+            URL url = new URL("http", autUrl.getHost(), autUrl.getPort(), "/logout");
+            return url.toString();
+        } catch(MalformedURLException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        }
+    }
+
+    /**
+     * Submits a request to the OSM website for a login form. The OSM website replies a session ID in
+     * a cookie.
+     * 
+     * @return the session ID
+     * @throws OsmOAuthAuthorisationException thrown if something went wrong
+     */
+    protected String fetchOsmWebsiteSessionId() throws OsmOAuthAuthorisationException {
+        try {
+            StringBuilder sb = new StringBuilder();
+            sb.append(buildOsmLoginUrl()).append("?cookie_test=true");
+            URL url = new URL(sb.toString());
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+            }
+            connection.setRequestMethod("GET");
+            connection.setDoInput(true);
+            connection.setDoOutput(false);
+            setHttpRequestParameters(connection);
+            connection.connect();
+            String sessionId = extractOsmSession(connection);
+            if (sessionId == null)
+                throw new OsmOAuthAuthorisationException(tr("OSM website didn''t reply a session cookie in response to ''{0}'',", url.toString()));
+            return sessionId;
+        } catch(IOException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        } finally {
+            synchronized(this) {
+                connection = null;
+            }
+        }
+    }
+
+    protected void authenticateOsmSession(String sessionId, String userName, String password) throws OsmLoginFailedException {
+        DataOutputStream dout = null;
+        try {
+            URL url = new URL(buildOsmLoginUrl());
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+            }
+            connection.setRequestMethod("POST");
+            connection.setDoInput(true);
+            connection.setDoOutput(true);
+            connection.setUseCaches(false);
+
+            Map<String,String> parameters = new HashMap<String, String>();
+            parameters.put("user[email]", userName);
+            parameters.put("user[password]", password);
+            parameters.put("referer", "/");
+            parameters.put("commit", "Login");
+
+            String request = buildPostRequest(parameters);
+
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
+            connection.setRequestProperty("Cookie", "_osm_session=" + sessionId);
+            // make sure we can catch 302 Moved Temporarily below
+            connection.setInstanceFollowRedirects(false);
+            setHttpRequestParameters(connection);
+
+            connection.connect();
+
+            dout = new DataOutputStream(connection.getOutputStream());
+            dout.writeBytes(request);
+            dout.flush();
+            dout.close();
+
+            // after a successful login the OSM website sends a redirect to a follow up page. Everything
+            // else, including a 200 OK, is a failed login. A 200 OK is replied if the login form with
+            // an error page is sent to back to the user.
+            //
+            int retCode = connection.getResponseCode();
+            if (retCode != HttpURLConnection.HTTP_MOVED_TEMP)
+                throw new OsmOAuthAuthorisationException(tr("Failed to authenticate user ''{0}'' with password ''***'' as OAuth user", userName));
+        } catch(OsmOAuthAuthorisationException e) {
+            throw new OsmLoginFailedException(e.getCause());
+        } catch(IOException e) {
+            throw new OsmLoginFailedException(e);
+        } finally {
+            if (dout != null) {
+                try {
+                    dout.close();
+                } catch(IOException e) { /* ignore */ }
+            }
+            synchronized(this) {
+                connection = null;
+            }
+        }
+    }
+
+    protected void logoutOsmSession(String sessionId) throws OsmOAuthAuthorisationException {
+        try {
+            URL url = new URL(buildOsmLogoutUrl());
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+            }
+            connection.setRequestMethod("GET");
+            connection.setDoInput(true);
+            connection.setDoOutput(false);
+            setHttpRequestParameters(connection);
+            connection.connect();
+        }catch(MalformedURLException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        } catch(IOException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        }  finally {
+            synchronized(this) {
+                connection = null;
+            }
+        }
+    }
+
+    protected void sendAuthorisationRequest(String sessionId, OAuthToken requestToken, OsmPrivileges privileges) throws OsmOAuthAuthorisationException {
+        Map<String, String> parameters = new HashMap<String, String>();
+        parameters.put("oauth_token", requestToken.getKey());
+        parameters.put("oauth_callback", "");
+        if (privileges.isAllowWriteApi()) {
+            parameters.put("allow_write_api", "yes");
+        }
+        if (privileges.isAllowWriteGpx()) {
+            parameters.put("allow_write_gpx", "yes");
+        }
+        if (privileges.isAllowReadGpx()) {
+            parameters.put("allow_read_gpx", "yes");
+        }
+        if (privileges.isAllowWritePrefs()) {
+            parameters.put("allow_write_prefs", "yes");
+        }
+        if (privileges.isAllowReadPrefs()) {
+            parameters.put("allow_read_prefs", "yes");
+        }
+
+        parameters.put("commit", "Save changes");
+
+        String request = buildPostRequest(parameters);
+        DataOutputStream dout = null;
+        try {
+            URL url = new URL(oauthProviderParameters.getAuthoriseUrl());
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+            }
+            connection.setRequestMethod("POST");
+            connection.setDoInput(true);
+            connection.setDoOutput(true);
+            connection.setUseCaches(false);
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
+            connection.setRequestProperty("Cookie", "_osm_session=" + sessionId);
+            connection.setInstanceFollowRedirects(false);
+            setHttpRequestParameters(connection);
+
+            connection.connect();
+
+            dout = new DataOutputStream(connection.getOutputStream());
+            dout.writeBytes(request);
+            dout.flush();
+            dout.close();
+
+            int retCode = connection.getResponseCode();
+            if (retCode != HttpURLConnection.HTTP_MOVED_TEMP)
+                throw new OsmOAuthAuthorisationException(tr("Failed to autorise OAuth request  ''{0}''", requestToken.getKey()));
+        } catch(MalformedURLException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        } catch(IOException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        } finally {
+            if (dout != null) {
+                try {
+                    dout.close();
+                } catch(IOException e) { /* ignore */ }
+            }
+            synchronized(this) {
+                connection = null;
+            }
+        }
+    }
+
+    protected void setHttpRequestParameters(HttpURLConnection connection) {
+        connection.setRequestProperty("User-Agent", Version.getInstance().getAgentString());
+        connection.setRequestProperty("Host", connection.getURL().getHost());
+    }
+
+    /**
+     * Automatically authorises a request token for a set of privileges.
+     * 
+     * @param requestToken the request token. Must not be null.
+     * @param osmUserName the OSM user name. Must not be null.
+     * @param osmPassword the OSM password. Must not be null.
+     * @param privileges the set of privileges. Must not be null.
+     * @param monitor a progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null
+     * @throws IllegalArgumentException thrown if requestToken is null
+     * @throws IllegalArgumentException thrown if osmUserName is null
+     * @throws IllegalArgumentException thrown if osmPassword is null
+     * @throws IllegalArgumentException thrown if privileges is null
+     * @throws OsmOAuthAuthorisationException thrown if the authorisation fails
+     * @throws OsmTransferCancelledException thrown if the task is cancelled by the user
+     */
+    public void authorise(OAuthToken requestToken, String osmUserName, String osmPassword, OsmPrivileges privileges, ProgressMonitor monitor) throws IllegalArgumentException, OsmOAuthAuthorisationException, OsmTransferCancelledException{
+        CheckParameterUtil.ensureParameterNotNull(requestToken, "requestToken");
+        CheckParameterUtil.ensureParameterNotNull(osmUserName, "osmUserName");
+        CheckParameterUtil.ensureParameterNotNull(osmPassword, "osmPassword");
+        CheckParameterUtil.ensureParameterNotNull(privileges, "privileges");
+
+        if (monitor == null) {
+            monitor = NullProgressMonitor.INSTANCE;
+        }
+        try {
+            monitor.beginTask(tr("Authorising OAuth Request token ''{0}'' at the OSM website ...", requestToken.getKey()));
+            monitor.setTicksCount(4);
+            monitor.indeterminateSubTask(tr("Initializing a session at the OSM website..."));
+            String sessionId = fetchOsmWebsiteSessionId();
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            monitor.worked(1);
+
+            monitor.indeterminateSubTask(tr("Authenticating the session for user ''{0}''...", osmUserName));
+            authenticateOsmSession(sessionId, osmUserName, osmPassword);
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            monitor.worked(1);
+
+            monitor.indeterminateSubTask(tr("Authorising request token ''{0}''...", requestToken.getKey()));
+            sendAuthorisationRequest(sessionId, requestToken, privileges);
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            monitor.worked(1);
+
+            monitor.indeterminateSubTask(tr("Logging out session ''{0}''...", sessionId));
+            logoutOsmSession(sessionId);
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            monitor.worked(1);
+        } catch(OsmOAuthAuthorisationException e) {
+            if (canceled)
+                throw new OsmTransferCancelledException();
+            throw e;
+        } finally {
+            monitor.finishTask();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationException.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorisationException.java	(revision 2746)
@@ -0,0 +1,21 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+public class OsmOAuthAuthorisationException extends Exception {
+
+    public OsmOAuthAuthorisationException() {
+        super();
+    }
+
+    public OsmOAuthAuthorisationException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    public OsmOAuthAuthorisationException(String arg0) {
+        super(arg0);
+    }
+
+    public OsmOAuthAuthorisationException(Throwable arg0) {
+        super(arg0);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OsmPrivilegesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OsmPrivilegesPanel.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OsmPrivilegesPanel.java	(revision 2746)
@@ -0,0 +1,99 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.oauth.OsmPrivileges;
+import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
+
+public class OsmPrivilegesPanel extends VerticallyScrollablePanel{
+
+    private JCheckBox cbWriteApi;
+    private JCheckBox cbWriteGpx;
+    private JCheckBox cbReadGpx;
+    private JCheckBox cbWritePrefs;
+    private JCheckBox cbReadPrefs;
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+        // checkbox for "allow to upload map data"
+        //
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 1.0;
+        gc.insets = new Insets(0,0,3,3);
+        add(cbWriteApi = new JCheckBox(), gc);
+        cbWriteApi.setText(tr("Allow to upload map data"));
+        cbWriteApi.setToolTipText(tr("Select to grant JOSM the right to upload map data on your behalf"));
+        cbWriteApi.setSelected(true);
+
+        // checkbox for "allow to upload gps traces"
+        //
+        gc.gridy = 1;
+        add(cbWriteGpx = new JCheckBox(), gc);
+        cbWriteGpx.setText(tr("Allow to upload GPS traces"));
+        cbWriteGpx.setToolTipText(tr("Select to grant JOSM the right to upload GPS traces on your behalf"));
+        cbWriteGpx.setSelected(true);
+
+        // checkbox for "allow to download private gps traces"
+        //
+        gc.gridy = 2;
+        add(cbReadGpx = new JCheckBox(), gc);
+        cbReadGpx.setText(tr("Allow to download your private GPS traces"));
+        cbReadGpx.setToolTipText(tr("Select to grant JOSM the right to download your private GPS traces into JOSM layers"));
+        cbReadGpx.setSelected(true);
+
+        // checkbox for "allow to download private gps traces"
+        //
+        gc.gridy = 3;
+        add(cbReadPrefs = new JCheckBox(), gc);
+        cbReadPrefs.setText(tr("Allow to read your preferences"));
+        cbReadPrefs.setToolTipText(tr("Select to grant JOSM the right to read your server preferences"));
+        cbReadPrefs.setSelected(true);
+
+        // checkbox for "allow to download private gps traces"
+        //
+        gc.gridy = 4;
+        add(cbWritePrefs = new JCheckBox(), gc);
+        cbWritePrefs.setText(tr("Allow to write your preferences"));
+        cbWritePrefs.setToolTipText(tr("Select to grant JOSM the right to write your server preferences"));
+        cbWritePrefs.setSelected(true);
+
+        // filler - grab remaining space
+        gc.gridy = 5;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.weightx = 1.0;
+        gc.weighty = 1.0;
+        add(new JPanel(), gc);
+    }
+
+    public OsmPrivilegesPanel() {
+        build();
+    }
+
+    /**
+     * Replies the currently entered privileges
+     * 
+     * @return the privileges
+     */
+    public OsmPrivileges getPrivileges() {
+        OsmPrivileges privileges = new OsmPrivileges();
+        privileges.setAllowWriteApi(cbWriteApi.isSelected());
+        privileges.setAllowWriteGpx(cbWriteGpx.isSelected());
+        privileges.setAllowReadGpx(cbReadGpx.isSelected());
+        privileges.setAllowWritePrefs(cbWritePrefs.isSelected());
+        privileges.setAllowReadPrefs(cbReadPrefs.isSelected());
+        return privileges;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveAccessTokenTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveAccessTokenTask.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveAccessTokenTask.java	(revision 2746)
@@ -0,0 +1,116 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.io.IOException;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.io.OsmTransferCancelledException;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.xml.sax.SAXException;
+
+/**
+ * Asynchronous task for retrieving an Access Token.
+ * 
+ */
+public class RetrieveAccessTokenTask extends PleaseWaitRunnable {
+
+    private boolean canceled;
+    private OAuthToken accessToken;
+    private OAuthParameters parameters;
+    private OsmOAuthAuthorisationClient client;
+    private OAuthToken requestToken;
+    private Component parent;
+
+    /**
+     * Creates the task
+     * 
+     * @param parent the parent component relative to which the {@see PleaseWaitRunnable}-Dialog
+     * is displayed
+     * @param parameters the OAuth parameters. Must not be null.
+     * @param requestToken the request token for which an Access Token is retrieved. Must not be null.
+     * @throws IllegalArgumentException thrown if parameters is null.
+     * @throws IllegalArgumentException thrown if requestToken is null.
+     */
+    public RetrieveAccessTokenTask(Component parent, OAuthParameters parameters, OAuthToken requestToken) {
+        super(parent, tr("Retrieving OAuth Access Token..."), false /* don't ignore exceptions */);
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        CheckParameterUtil.ensureParameterNotNull(requestToken, "requestToken");
+        this.parameters = parameters;
+        this.requestToken = requestToken;
+        this.parent = parent;
+    }
+
+    @Override
+    protected void cancel() {
+        canceled = true;
+        synchronized(this) {
+            if (client != null) {
+                client.cancel();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() { /* not used in this task */}
+
+    protected void alertRetrievingAccessTokenFailed(OsmOAuthAuthorisationException e) {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr(
+                        "<html>Retrieving an OAuth Access Token from ''{0}'' failed.</html>",
+                        parameters.getAccessTokenUrl()
+                ),
+                tr("Request Failed"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/OAuth#NotAuthorizedException")
+        );
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            synchronized(this) {
+                client = new OsmOAuthAuthorisationClient(parameters, requestToken);
+            }
+            accessToken = client.getAccessToken(getProgressMonitor().createSubTaskMonitor(0, false));
+        } catch(OsmTransferCancelledException e) {
+            return;
+        } catch (OsmOAuthAuthorisationException e) {
+            e.printStackTrace();
+            alertRetrievingAccessTokenFailed(e);
+            accessToken = null;
+        } finally {
+            synchronized(this) {
+                client = null;
+            }
+        }
+    }
+
+    /**
+     * Replies true if the task was canceled.
+     * 
+     * @return
+     */
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    /**
+     * Replies the retrieved Access Token. null, if something went wrong.
+     * 
+     * @return the retrieved Access Token
+     */
+    public OAuthToken getAccessToken() {
+        return accessToken;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveRequestTokenTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveRequestTokenTask.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/RetrieveRequestTokenTask.java	(revision 2746)
@@ -0,0 +1,110 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.io.IOException;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.io.OsmTransferCancelledException;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.xml.sax.SAXException;
+
+/**
+ * Asynchronous task for retrieving a request token
+ */
+public class RetrieveRequestTokenTask extends PleaseWaitRunnable {
+
+    private boolean canceled;
+    private OAuthToken requestToken;
+    private OAuthParameters parameters;
+    private OsmOAuthAuthorisationClient client;
+    private Component parent;
+
+    /**
+     * Creates the task
+     * 
+     * @param parent the parent component relative to which the {@see PleaseWaitRunnable}-Dialog
+     * is displayed
+     * @param parameters the OAuth parameters. Must not be null.
+     * @throws IllegalArgumentException thrown if parameters is null.
+     */
+    public RetrieveRequestTokenTask(Component parent, OAuthParameters parameters ) {
+        super(parent, tr("Retrieving OAuth Request Token..."), false /* don't ignore exceptions */);
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        this.parameters = parameters;
+        this.parent = parent;
+    }
+
+    @Override
+    protected void cancel() {
+        canceled = true;
+        synchronized(this) {
+            if (client != null) {
+                client.cancel();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() { /* not used in this task */}
+
+    protected void alertRetrievingRequestTokenFailed(OsmOAuthAuthorisationException e) {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr(
+                        "<html>Retrieving an OAuth Request Token from ''{0}'' failed.</html>",
+                        parameters.getRequestTokenUrl()
+                ),
+                tr("Request Failed"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/OAuth#NotAuthorizedException")
+        );
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            synchronized(this) {
+                client = new OsmOAuthAuthorisationClient(parameters);
+            }
+            requestToken = client.getRequestToken(getProgressMonitor().createSubTaskMonitor(0, false));
+        } catch(OsmTransferCancelledException e) {
+            return;
+        } catch (OsmOAuthAuthorisationException e) {
+            e.printStackTrace();
+            alertRetrievingRequestTokenFailed(e);
+            requestToken = null;
+        } finally {
+            synchronized(this) {
+                client = null;
+            }
+        }
+    }
+
+    /**
+     * Replies true if the task was canceled
+     * 
+     * @return true if the task was canceled
+     */
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    /**
+     * Replies the request token. null, if something went wrong.
+     * 
+     * @return the request token
+     */
+    public OAuthToken getRequestToken() {
+        return requestToken;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorisationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorisationUI.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorisationUI.java	(revision 2746)
@@ -0,0 +1,451 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.OpenBrowser;
+
+/**
+ * This is the UI for running a semic-automic authorisation procedure.
+ * 
+ * In contrast to the fully-automatic procedure the user is dispatched to an
+ * external browser for login and authorisation.
+ */
+public class SemiAutomaticAuthorisationUI extends AbstractAuthorisationUI {
+    private AccessTokenInfoPanel pnlAccessTokenInfo;
+    private OAuthToken requestToken;
+
+    private RetrieveRequestTokenPanel pnlRetrieveRequestToken;
+    private RetrieveAccessTokenPanel pnlRetrieveAccessToken;
+    private ShowAccessTokenPanel pnlShowAccessToken;
+
+    /**
+     * build the UI
+     */
+    protected void build() {
+        setLayout(new BorderLayout());
+        setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        pnlRetrieveRequestToken = new RetrieveRequestTokenPanel();
+        pnlRetrieveAccessToken = new RetrieveAccessTokenPanel();
+        pnlShowAccessToken = new ShowAccessTokenPanel();
+        add(pnlRetrieveRequestToken, BorderLayout.CENTER);
+    }
+
+    public SemiAutomaticAuthorisationUI() {
+        build();
+    }
+
+    @Override
+    public boolean isSaveAccessTokenToPreferences() {
+        return pnlAccessTokenInfo.isSaveToPreferences();
+    }
+
+    protected void transitionToRetrieveAccessToken() {
+        OsmOAuthAuthorisationClient client = new OsmOAuthAuthorisationClient(
+                getAdvancedPropertiesPanel().getAdvancedParameters()
+        );
+        String authoriseUrl = client.getAuthoriseUrl(requestToken);
+        OpenBrowser.displayUrl(authoriseUrl);
+
+        removeAll();
+        pnlRetrieveAccessToken.setAuthoriseUrl(authoriseUrl);
+        add(pnlRetrieveAccessToken, BorderLayout.CENTER);
+        pnlRetrieveAccessToken.invalidate();
+        validate();
+        repaint();
+    }
+
+    protected void transitionToRetrieveRequestToken() {
+        requestToken = null;
+        setAccessToken(null);
+        removeAll();
+        add(pnlRetrieveRequestToken, BorderLayout.CENTER);
+        pnlRetrieveRequestToken.invalidate();
+        validate();
+        repaint();
+    }
+
+    protected void transitionToShowAccessToken() {
+        removeAll();
+        add(pnlShowAccessToken, BorderLayout.CENTER);
+        pnlShowAccessToken.invalidate();
+        validate();
+        repaint();
+        pnlShowAccessToken.setAccessToken(getAccessToken());
+    }
+
+    /**
+     * This is the panel displayed in the first step of the semi-automatic authorisation
+     * process.
+     */
+    private class RetrieveRequestTokenPanel extends JPanel {
+        private JCheckBox cbShowAdvancedParameters;
+
+        protected JPanel buildAdvancedParametersPanel() {
+            JPanel pnl = new JPanel(new GridBagLayout());
+            GridBagConstraints gc= new GridBagConstraints();
+
+            gc.anchor = GridBagConstraints.NORTHWEST;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 0.0;
+            gc.insets = new Insets(0,0,0,3);
+            pnl.add(cbShowAdvancedParameters = new JCheckBox(), gc);
+            cbShowAdvancedParameters.setSelected(false);
+            cbShowAdvancedParameters.addItemListener(
+                    new ItemListener() {
+                        public void itemStateChanged(ItemEvent evt) {
+                            getAdvancedPropertiesPanel().setVisible(evt.getStateChange() == ItemEvent.SELECTED);
+                        }
+                    }
+            );
+
+            gc.gridx = 1;
+            gc.weightx = 1.0;
+            JMultilineLabel lbl = new JMultilineLabel(tr("Display Advanced OAuth Parameters"));
+            lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN));
+            pnl.add(lbl, gc);
+
+            gc.gridy = 1;
+            gc.gridx = 1;
+            gc.insets = new Insets(3,0,3,0);
+            gc.fill = GridBagConstraints.BOTH;
+            gc.weightx = 1.0;
+            gc.weighty = 1.0;
+            pnl.add(getAdvancedPropertiesPanel(), gc);
+            getAdvancedPropertiesPanel().setBorder(
+                    BorderFactory.createCompoundBorder(
+                            BorderFactory.createLineBorder(Color.GRAY, 1),
+                            BorderFactory.createEmptyBorder(3,3,3,3)
+                    )
+            );
+            getAdvancedPropertiesPanel().setVisible(false);
+            return pnl;
+        }
+
+        protected JPanel buildCommandPanel() {
+            JPanel pnl = new JPanel(new GridBagLayout());
+            GridBagConstraints gc= new GridBagConstraints();
+
+            gc.anchor = GridBagConstraints.NORTHWEST;
+            gc.fill = GridBagConstraints.BOTH;
+            gc.weightx = 1.0;
+            gc.weighty = 1.0;
+            gc.insets = new Insets(0,0,0,3);
+
+
+            HtmlPanel h = new HtmlPanel();
+            h.setText(tr("<html>"
+                    + "Please click on <strong>{0}</strong> to retrieve an OAuth Request Token from "
+                    + "''{1}''.</html>",
+                    tr("Retrieve Request Token"),
+                    getAdvancedPropertiesPanel().getAdvancedParameters().getRequestTokenUrl()
+            ));
+            pnl.add(h, gc);
+
+            JPanel pnl1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
+            pnl1.add(new SideButton(new RetrieveRequestTokenAction()));
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            gc.gridy = 1;
+            pnl.add(pnl1, gc);
+            return pnl;
+
+        }
+        protected void build() {
+            setLayout(new BorderLayout(0,5));
+            JLabel lbl = new JLabel(tr("<html>Step 1/3: Retrieve an OAuth Request Token</html>"));
+            lbl.setFont(lbl.getFont().deriveFont(16f));
+            add(lbl, BorderLayout.NORTH);
+            add(buildAdvancedParametersPanel(), BorderLayout.CENTER);
+            add(buildCommandPanel(), BorderLayout.SOUTH);
+        }
+
+        public RetrieveRequestTokenPanel() {
+            build();
+        }
+    }
+
+
+    /**
+     * This is the panel displayed in the second step of the semi-automatic authorization
+     * process.
+     */
+    private class RetrieveAccessTokenPanel extends JPanel {
+
+        private JTextField tfAuthoriseUrl;
+
+        protected JPanel buildTitlePanel() {
+            JPanel pnl = new JPanel(new BorderLayout());
+            JLabel lbl = new JLabel(tr("<html>Step 2/3: Authorise and retrieve an Access Token</html>"));
+            lbl.setFont(lbl.getFont().deriveFont(16f));
+            pnl.add(lbl, BorderLayout.CENTER);
+            return pnl;
+        }
+
+        protected JPanel buildContentPanel() {
+            JPanel pnl = new JPanel(new GridBagLayout());
+            GridBagConstraints gc = new GridBagConstraints();
+
+            gc.anchor= GridBagConstraints.NORTHWEST;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            gc.gridwidth = 2;
+            HtmlPanel html = new HtmlPanel();
+            html.setText(tr("<html><body>"
+                    + "JOSM successfully retrieved a Request Token. "
+                    + "JOSM is now launching an authorisation page in an external browser. "
+                    + "Please login with your OSM username and password and follow the instructions "
+                    + "to authorise the Request Token. Then switch back to this dialog and click on "
+                    + "<strong>{0}</strong><br><br>"
+                    + "If launching the external browser fails you can copy the following authorise URL "
+                    + "and paste it into the address field of your browser.",
+                    tr("Request Access Token")
+            ));
+            pnl.add(html, gc);
+
+            gc.gridx = 0;
+            gc.gridy = 1;
+            gc.weightx = 0.0;
+            gc.gridwidth = 1;
+            pnl.add(new JLabel(tr("Autorise URL:")), gc);
+
+            gc.gridx = 1;
+            gc.weightx = 1.0;
+            pnl.add(tfAuthoriseUrl = new JTextField(), gc);
+            tfAuthoriseUrl.setEditable(false);
+
+            return pnl;
+        }
+
+        protected JPanel buildActionPanel() {
+            JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+            pnl.add(new SideButton(new BackAction()));
+            pnl.add(new SideButton(new RetrieveAccessTokenAction()));
+            return pnl;
+        }
+
+        protected void build() {
+            setLayout(new BorderLayout());
+            add(buildTitlePanel(), BorderLayout.NORTH);
+            add(buildContentPanel(), BorderLayout.CENTER);
+            add(buildActionPanel(), BorderLayout.SOUTH);
+        }
+
+        public RetrieveAccessTokenPanel() {
+            build();
+        }
+
+        public void setAuthoriseUrl(String url) {
+            tfAuthoriseUrl.setText(url);
+        }
+
+        /**
+         * Action to go back to step 1 in the process
+         */
+        class BackAction extends AbstractAction {
+            public BackAction() {
+                putValue(NAME, tr("Back"));
+                putValue(SHORT_DESCRIPTION, tr("Go back to step 1/3"));
+                putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous"));
+            }
+
+            public void actionPerformed(ActionEvent arg0) {
+                transitionToRetrieveRequestToken();
+            }
+        }
+    }
+
+    /**
+     * Displays the retrieved Access Token in step 3.
+     */
+    class ShowAccessTokenPanel extends JPanel {
+
+        protected JPanel buildTitlePanel() {
+            JPanel pnl = new JPanel(new BorderLayout());
+            JLabel lbl = new JLabel(tr("<html>Step 3/3: Successfully retrieved an Access Token</html>"));
+            lbl.setFont(lbl.getFont().deriveFont(16f));
+            pnl.add(lbl, BorderLayout.CENTER);
+            return pnl;
+        }
+
+        protected JPanel buildContentPanel() {
+            JPanel pnl = new JPanel(new GridBagLayout());
+            GridBagConstraints gc = new GridBagConstraints();
+
+            gc.anchor= GridBagConstraints.NORTHWEST;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            HtmlPanel html = new HtmlPanel();
+            html.setText(tr("<html><body>"
+                    + "JOSM has successfully retrieved an Access Token. "
+                    + "You can now accept this token. JOSM will used it the future for authentication "
+                    + "and authorisation at the OSM server.<br><br>"
+                    + "The access token is: "
+            ));
+            pnl.add(html, gc);
+
+            gc.gridx = 0;
+            gc.gridy = 1;
+            gc.weightx = 1.0;
+            gc.gridwidth = 1;
+            pnl.add(pnlAccessTokenInfo = new AccessTokenInfoPanel(), gc);
+            pnlAccessTokenInfo.setSaveToPreferences(
+                    OAuthAccessTokenHolder.getInstance().isSaveToPreferences()
+            );
+            return pnl;
+        }
+
+        protected JPanel buildActionPanel() {
+            JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
+            pnl.add(new SideButton(new RestartAction()));
+            pnl.add(new SideButton(new TestAccessTokenAction()));
+            return pnl;
+        }
+
+        protected void build() {
+            setLayout(new BorderLayout());
+            add(buildTitlePanel(), BorderLayout.NORTH);
+            add(buildContentPanel(), BorderLayout.CENTER);
+            add(buildActionPanel(), BorderLayout.SOUTH);
+        }
+
+        public ShowAccessTokenPanel() {
+            build();
+        }
+
+        /**
+         * Action to go back to step 1 in the process
+         */
+        class RestartAction extends AbstractAction {
+            public RestartAction() {
+                putValue(NAME, tr("Restart"));
+                putValue(SHORT_DESCRIPTION, tr("Go back to step 1/3"));
+                putValue(SMALL_ICON, ImageProvider.get("dialogs", "previous"));
+            }
+
+            public void actionPerformed(ActionEvent arg0) {
+                transitionToRetrieveRequestToken();
+            }
+        }
+
+        public void setAccessToken(OAuthToken accessToken) {
+            pnlAccessTokenInfo.setAccessToken(accessToken);
+        }
+    }
+
+    /**
+     * Action for retrieving a request token
+     */
+    class RetrieveRequestTokenAction extends AbstractAction{
+
+        public RetrieveRequestTokenAction() {
+            putValue(NAME, tr("Retrieve Request Token"));
+            putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
+            putValue(SHORT_DESCRIPTION, tr("Click to retrieve a Request Token"));
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            final RetrieveRequestTokenTask task = new RetrieveRequestTokenTask(
+                    SemiAutomaticAuthorisationUI.this,
+                    getAdvancedPropertiesPanel().getAdvancedParameters()
+            );
+            Main.worker.submit(task);
+            Runnable r  = new Runnable() {
+                public void run() {
+                    if (task.isCanceled()) return;
+                    if (task.getRequestToken() == null) return;
+                    requestToken = task.getRequestToken();
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            transitionToRetrieveAccessToken();
+                        }
+                    });
+                }
+            };
+            Main.worker.submit(r);
+        }
+    }
+
+    /**
+     * Action for retrieving an Access Token
+     */
+    class RetrieveAccessTokenAction extends AbstractAction {
+
+        public RetrieveAccessTokenAction() {
+            putValue(NAME, tr("Retrieve Access Token"));
+            putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
+            putValue(SHORT_DESCRIPTION, tr("Click to retrieve an Access Token"));
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            final RetrieveAccessTokenTask task = new RetrieveAccessTokenTask(
+                    SemiAutomaticAuthorisationUI.this,
+                    getAdvancedPropertiesPanel().getAdvancedParameters(),
+                    requestToken
+            );
+            Main.worker.submit(task);
+            Runnable r  = new Runnable() {
+                public void run() {
+                    if (task.isCanceled()) return;
+                    if (task.getAccessToken() == null) return;
+                    setAccessToken(task.getAccessToken());
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            transitionToShowAccessToken();
+                        }
+                    });
+                }
+            };
+            Main.worker.submit(r);
+        }
+    }
+
+    /**
+     * Action for testing an Access Token
+     */
+    class TestAccessTokenAction extends AbstractAction {
+
+        public TestAccessTokenAction() {
+            putValue(NAME, tr("Test Access Token"));
+            putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
+            putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token"));
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            TestAccessTokenTask task = new TestAccessTokenTask(
+                    SemiAutomaticAuthorisationUI.this,
+                    getApiUrl(),
+                    getAdvancedPropertiesPanel().getAdvancedParameters(),
+                    getAccessToken()
+            );
+            Main.worker.submit(task);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/oauth/TestAccessTokenTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/TestAccessTokenTask.java	(revision 2746)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/TestAccessTokenTask.java	(revision 2746)
@@ -0,0 +1,279 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.oauth;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.JOptionPane;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import oauth.signpost.OAuthConsumer;
+import oauth.signpost.exception.OAuthException;
+
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.data.oauth.OAuthParameters;
+import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.data.osm.UserInfo;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.io.OsmApiException;
+import org.openstreetmap.josm.io.OsmDataParsingException;
+import org.openstreetmap.josm.io.OsmServerUserInfoReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * Checks whether an OSM API server can be accessed with a specific Access Token.
+ * 
+ * It retrieves the user details for the user which is authorized to access the server with
+ * this token.
+ * 
+ */
+public class TestAccessTokenTask extends PleaseWaitRunnable {
+    private OAuthToken token;
+    private OAuthParameters oauthParameters;
+    private boolean canceled;
+    private Component parent;
+    private String apiUrl;
+    private HttpURLConnection connection;
+
+    /**
+     * Create the task
+     * 
+     * @param parent the parent component relative to which the  {@see PleaseWaitRunnable}-Dialog is displayed
+     * @param apiUrl the API URL. Must not be null.
+     * @param parameters the OAuth parameters. Must not be null.
+     * @param accessToken the Access Token. Must not be null.
+     */
+    public TestAccessTokenTask(Component parent, String apiUrl, OAuthParameters parameters, OAuthToken accessToken) {
+        super(parent, tr("Testing OAuth Access Token"), false /* don't ignore exceptions */);
+        CheckParameterUtil.ensureParameterNotNull(apiUrl, "apiUrl");
+        CheckParameterUtil.ensureParameterNotNull(parameters, "parameters");
+        CheckParameterUtil.ensureParameterNotNull(accessToken, "accessToken");
+        this.token = accessToken;
+        this.oauthParameters = parameters;
+        this.parent = parent;
+        this.apiUrl = apiUrl;
+    }
+
+    @Override
+    protected void cancel() {
+        canceled = true;
+        synchronized(this) {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() {}
+
+    protected void sign(HttpURLConnection con) throws OAuthException{
+        OAuthConsumer consumer = oauthParameters.buildConsumer();
+        consumer.setTokenWithSecret(token.getKey(), token.getSecret());
+        consumer.sign(con);
+    }
+
+    protected String normalizeApiUrl(String url) {
+        // remove leading and trailing white space
+        url = url.trim();
+
+        // remove trailing slashes
+        while(url.endsWith("/")) {
+            url = url.substring(0, url.lastIndexOf("/"));
+        }
+        return url;
+    }
+
+    protected UserInfo getUserDetails() throws OsmOAuthAuthorisationException, OsmDataParsingException,OsmTransferException {
+        boolean authenticatorEnabled = true;
+        try {
+            URL url = new URL(normalizeApiUrl(apiUrl) + "/user/details");
+            authenticatorEnabled = DefaultAuthenticator.getInstance().isEnabled();
+            DefaultAuthenticator.getInstance().setEnabled(false);
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+            }
+
+            connection.setDoOutput(true);
+            connection.setRequestMethod("GET");
+            connection.setRequestProperty("User-Agent", Version.getInstance().getAgentString());
+            connection.setRequestProperty("Host", connection.getURL().getHost());
+            sign(connection);
+            connection.connect();
+
+            if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
+                throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED, tr("Retrieving user details with Access Token Key ''{0}'' was rejected.", token.getKey()), null);
+
+            if (connection.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN)
+                throw new OsmApiException(HttpURLConnection.HTTP_FORBIDDEN, tr("Retrieving user details with Access Token Key ''{0}'' was forbidded.", token.getKey()), null);
+
+            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
+                throw new OsmApiException(connection.getResponseCode(),connection.getHeaderField("Error"), null);
+            Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(connection.getInputStream());
+            return OsmServerUserInfoReader.buildFromXML(d);
+        } catch(SAXException e) {
+            throw new OsmDataParsingException(e);
+        } catch(ParserConfigurationException e){
+            throw new OsmDataParsingException(e);
+        } catch(MalformedURLException e) {
+            throw new OsmTransferException(e);
+        } catch(IOException e){
+            throw new OsmTransferException(e);
+        } catch(OAuthException e) {
+            throw new OsmOAuthAuthorisationException(e);
+        } finally {
+            DefaultAuthenticator.getInstance().setEnabled(authenticatorEnabled);
+        }
+    }
+
+    protected void notifySuccess(UserInfo userInfo) {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "Successfully used the the Access Token ''{0}'' to<br>"
+                        + "access the OSM server at ''{1}''.<br>"
+                        + "You''re accessing the OSM server as user ''{2}'' with id ''{3}''."
+                        +"</html>",
+                        token.getKey(),
+                        apiUrl,
+                        userInfo.getDisplayName(),
+                        userInfo.getId()
+                ),
+                tr("Success"),
+                JOptionPane.INFORMATION_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenOK")
+        );
+    }
+
+    protected void alertFailedAuthentication() {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "Failed to access the OSM server ''{0}''<br>"
+                        + "with the Access Token ''{0}''.<br>"
+                        + "The server rejected the Access Token as unauthorised. You will not<br>"
+                        + "able to access any protected resource on this server using this token."
+                        +"</html>",
+                        apiUrl,
+                        token.getKey()
+                ),
+                tr("Test failed"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenFailed")
+        );
+    }
+
+    protected void alertFailedAuthorisation() {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "The AccessToken ''{1}'' is known to the OSM server ''{0}''.<br>"
+                        + "The test to retrieve the user details for this token failed, though.<br>"
+                        + "Depending on what rights are granted to this token you may nevertheless use it<br>"
+                        + "to upload data, upload GPS traces, and/or access other protected resources."
+                        +"</html>",
+                        apiUrl,
+                        token.getKey()
+                ),
+                tr("Token allows restricted access"),
+                JOptionPane.WARNING_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenFailed")
+        );
+    }
+
+    protected void alertFailedConnection() {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "Failed to retrieve information about the current user"
+                        + "from the OSM server ''{0}''.<br>"
+                        + "This is probably not a problem caused by the tested Access Token, but<br>"
+                        + "rather a problem with the server configuration. Carefully check the server<br>"
+                        + "URL and your Internet connection."
+                        +"</html>",
+                        apiUrl,
+                        token.getKey()
+                ),
+                tr("Test failed"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenFailed")
+        );
+    }
+
+    protected void alertFailedSigning() {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "Failed to sign the request for the OSM server ''{0}'' with the "
+                        + "token ''{1}''.<br>"
+                        + "The token ist probably invalid."
+                        +"</html>",
+                        apiUrl,
+                        token.getKey()
+                ),
+                tr("Test failed"),
+                JOptionPane.ERROR_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenFailed")
+        );
+    }
+
+    protected void alertInternalError() {
+        HelpAwareOptionPane.showOptionDialog(
+                parent,
+                tr("<html>"
+                        + "The test failed because the server responded with an internal error.<br>"
+                        + "JOSM couldn''t decide whether the token is valid. Please try again later."
+                        +"</html>",
+                        apiUrl,
+                        token.getKey()
+                ),
+                tr("Test failed"),
+                JOptionPane.WARNING_MESSAGE,
+                HelpUtil.ht("/Dialog/OAuthAutorisationWizard#AccessTokenFailed")
+        );
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            getProgressMonitor().indeterminateSubTask(tr("Retrieving user info..."));
+            UserInfo userInfo = getUserDetails();
+            if (canceled) return;
+            notifySuccess(userInfo);
+        }catch(OsmOAuthAuthorisationException e) {
+            if (canceled) return;
+            e.printStackTrace();
+            alertFailedSigning();
+        } catch(OsmApiException e) {
+            if (canceled) return;
+            e.printStackTrace();
+            if (e.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) {
+                alertInternalError();
+                return;
+            } if (e.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+                alertFailedAuthentication();
+                return;
+            } else if (e.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
+                alertFailedAuthorisation();
+                return;
+            }
+            alertFailedConnection();
+        } catch(OsmTransferException e) {
+            if (canceled) return;
+            e.printStackTrace();
+            alertFailedConnection();
+        }
+    }
+}
