001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.io; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.net.HttpURLConnection; 007 import java.net.Authenticator.RequestorType; 008 import java.nio.ByteBuffer; 009 import java.nio.CharBuffer; 010 import java.nio.charset.CharacterCodingException; 011 import java.nio.charset.Charset; 012 import java.nio.charset.CharsetEncoder; 013 014 import oauth.signpost.OAuthConsumer; 015 import oauth.signpost.exception.OAuthException; 016 017 import org.openstreetmap.josm.Main; 018 import org.openstreetmap.josm.data.oauth.OAuthParameters; 019 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 020 import org.openstreetmap.josm.io.auth.CredentialsAgentException; 021 import org.openstreetmap.josm.io.auth.CredentialsManager; 022 import org.openstreetmap.josm.io.auth.CredentialsAgentResponse; 023 import org.openstreetmap.josm.tools.Base64; 024 025 /** 026 * Base class that handles common things like authentication for the reader and writer 027 * to the osm server. 028 * 029 * @author imi 030 */ 031 public class OsmConnection { 032 protected boolean cancel = false; 033 protected HttpURLConnection activeConnection; 034 protected OAuthParameters oauthParameters; 035 036 /** 037 * Initialize the http defaults and the authenticator. 038 */ 039 static { 040 try { 041 HttpURLConnection.setFollowRedirects(true); 042 } catch (SecurityException e) { 043 e.printStackTrace(); 044 } 045 } 046 047 public void cancel() { 048 cancel = true; 049 synchronized (this) { 050 if (activeConnection != null) { 051 activeConnection.setConnectTimeout(100); 052 activeConnection.setReadTimeout(100); 053 } 054 } 055 try { 056 Thread.sleep(100); 057 } catch (InterruptedException ex) { 058 } 059 060 synchronized (this) { 061 if (activeConnection != null) { 062 activeConnection.disconnect(); 063 } 064 } 065 } 066 067 /** 068 * Adds an authentication header for basic authentication 069 * 070 * @param con the connection 071 * @throws OsmTransferException thrown if something went wrong. Check for nested exceptions 072 */ 073 protected void addBasicAuthorizationHeader(HttpURLConnection con) throws OsmTransferException { 074 CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); 075 CredentialsAgentResponse response; 076 String token; 077 try { 078 synchronized (CredentialsManager.getInstance()) { 079 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER, 080 con.getURL().getHost(), false /* don't know yet whether the credentials will succeed */); 081 } 082 } catch (CredentialsAgentException e) { 083 throw new OsmTransferException(e); 084 } 085 if (response == null) { 086 token = ":"; 087 } else if (response.isCanceled()) { 088 cancel = true; 089 return; 090 } else { 091 String username= response.getUsername() == null ? "" : response.getUsername(); 092 String password = response.getPassword() == null ? "" : String.valueOf(response.getPassword()); 093 token = username + ":" + password; 094 try { 095 ByteBuffer bytes = encoder.encode(CharBuffer.wrap(token)); 096 con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes)); 097 } catch(CharacterCodingException e) { 098 throw new OsmTransferException(e); 099 } 100 } 101 } 102 103 /** 104 * Signs the connection with an OAuth authentication header 105 * 106 * @param connection the connection 107 * 108 * @throws OsmTransferException thrown if there is currently no OAuth Access Token configured 109 * @throws OsmTransferException thrown if signing fails 110 */ 111 protected void addOAuthAuthorizationHeader(HttpURLConnection connection) throws OsmTransferException { 112 if (oauthParameters == null) { 113 oauthParameters = OAuthParameters.createFromPreferences(Main.pref); 114 } 115 OAuthConsumer consumer = oauthParameters.buildConsumer(); 116 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 117 if (! holder.containsAccessToken()) 118 throw new MissingOAuthAccessTokenException(); 119 consumer.setTokenWithSecret(holder.getAccessTokenKey(), holder.getAccessTokenSecret()); 120 try { 121 consumer.sign(connection); 122 } catch(OAuthException e) { 123 throw new OsmTransferException(tr("Failed to sign a HTTP connection with an OAuth Authentication header"), e); 124 } 125 } 126 127 protected void addAuth(HttpURLConnection connection) throws OsmTransferException { 128 String authMethod = Main.pref.get("osm-server.auth-method", "basic"); 129 if (authMethod.equals("basic")) { 130 addBasicAuthorizationHeader(connection); 131 } else if (authMethod.equals("oauth")) { 132 addOAuthAuthorizationHeader(connection); 133 } else { 134 String msg = tr("Warning: unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod); 135 System.err.println(msg); 136 throw new OsmTransferException(msg); 137 } 138 } 139 140 /** 141 * Replies true if this connection is canceled 142 * 143 * @return true if this connection is canceled 144 * @return 145 */ 146 public boolean isCanceled() { 147 return cancel; 148 } 149 }