1 | /* |
2 | * Tomdroid |
3 | * Tomboy on Android |
4 | * http://www.launchpad.net/tomdroid |
5 | * |
6 | * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org> |
7 | * |
8 | * This file is part of Tomdroid. |
9 | * |
10 | * Tomdroid is free software: you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation, either version 3 of the License, or |
13 | * (at your option) any later version. |
14 | * |
15 | * Tomdroid is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>. |
22 | */ |
23 | package org.tomdroid.sync.web; |
24 | |
25 | import java.io.UnsupportedEncodingException; |
26 | import java.net.UnknownHostException; |
27 | |
28 | import oauth.signpost.OAuthConsumer; |
29 | import oauth.signpost.OAuthProvider; |
30 | import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer; |
31 | import oauth.signpost.commonshttp.CommonsHttpOAuthProvider; |
32 | import oauth.signpost.exception.OAuthCommunicationException; |
33 | import oauth.signpost.exception.OAuthExpectationFailedException; |
34 | import oauth.signpost.exception.OAuthMessageSignerException; |
35 | import oauth.signpost.exception.OAuthNotAuthorizedException; |
36 | |
37 | import org.apache.http.HttpRequest; |
38 | import org.apache.http.HttpResponse; |
39 | import org.apache.http.client.methods.HttpGet; |
40 | import org.apache.http.client.methods.HttpPut; |
41 | import org.apache.http.entity.StringEntity; |
42 | import org.json.JSONException; |
43 | import org.json.JSONObject; |
44 | import org.tomdroid.ui.Tomdroid; |
45 | import org.tomdroid.util.Preferences; |
46 | |
47 | import android.net.Uri; |
48 | import android.util.Log; |
49 | |
50 | public class OAuthConnection extends WebConnection { |
51 | |
52 | private static final String TAG = "OAuthConnection"; |
53 | private static final String CONSUMER_KEY = "anyone"; |
54 | private static final String CONSUMER_SECRET = "anyone"; |
55 | |
56 | private OAuthConsumer consumer = null; |
57 | |
58 | public String accessToken = ""; |
59 | public String accessTokenSecret = ""; |
60 | public String requestToken = ""; |
61 | public String requestTokenSecret = ""; |
62 | public boolean oauth10a = false; |
63 | public String authorizeUrl = ""; |
64 | public String requestTokenUrl = ""; |
65 | public String accessTokenUrl = ""; |
66 | public String rootApi = ""; |
67 | public String userApi = ""; |
68 | |
69 | public OAuthConnection() { |
70 | |
71 | consumer = new CommonsHttpOAuthConsumer( |
72 | CONSUMER_KEY, |
73 | CONSUMER_SECRET); |
74 | } |
75 | |
76 | public boolean isAuthenticated() { |
77 | |
78 | if (accessToken.equals("") || accessTokenSecret.equals("")) |
79 | return false; |
80 | else |
81 | return true; |
82 | } |
83 | |
84 | private OAuthProvider getProvider() { |
85 | |
86 | // Use the provider bundled with signpost, the android libs are buggy |
87 | // See: http://code.google.com/p/oauth-signpost/issues/detail?id=20 |
88 | OAuthProvider provider = new CommonsHttpOAuthProvider( |
89 | requestTokenUrl, |
90 | accessTokenUrl, |
91 | authorizeUrl); |
92 | provider.setOAuth10a(oauth10a); |
93 | |
94 | return provider; |
95 | } |
96 | |
97 | private void sign(HttpRequest request) { |
98 | |
99 | if (isAuthenticated()) |
100 | consumer.setTokenWithSecret(accessToken, accessTokenSecret); |
101 | else |
102 | return; |
103 | |
104 | // TODO: figure out if we should throw exceptions |
105 | try { |
106 | consumer.sign(request); |
107 | } catch (OAuthMessageSignerException e1) { |
108 | e1.printStackTrace(); |
109 | } catch (OAuthExpectationFailedException e1) { |
110 | e1.printStackTrace(); |
111 | } catch (OAuthCommunicationException e) { |
112 | // TODO Auto-generated catch block |
113 | e.printStackTrace(); |
114 | } |
115 | } |
116 | |
117 | public Uri getAuthorizationUrl(String server) throws UnknownHostException { |
118 | |
119 | String url = ""; |
120 | |
121 | // this method shouldn't have been called |
122 | if (isAuthenticated()) |
123 | return null; |
124 | |
125 | rootApi = server+"/api/1.0/"; |
126 | |
127 | AnonymousConnection connection = new AnonymousConnection(); |
128 | String response = connection.get(rootApi); |
129 | |
130 | JSONObject jsonResponse; |
131 | |
132 | try { |
133 | jsonResponse = new JSONObject(response); |
134 | |
135 | accessTokenUrl = jsonResponse.getString("oauth_access_token_url"); |
136 | requestTokenUrl = jsonResponse.getString("oauth_request_token_url"); |
137 | authorizeUrl = jsonResponse.getString("oauth_authorize_url"); |
138 | |
139 | } catch (JSONException e) { |
140 | e.printStackTrace(); |
141 | return null; |
142 | } |
143 | |
144 | OAuthProvider provider = getProvider(); |
145 | |
146 | try { |
147 | // the argument is the callback used when the remote authorization is complete |
148 | url = provider.retrieveRequestToken(consumer, "tomdroid://sync"); |
149 | |
150 | requestToken = consumer.getToken(); |
151 | requestTokenSecret = consumer.getTokenSecret(); |
152 | oauth10a = provider.isOAuth10a(); |
153 | accessToken = ""; |
154 | accessTokenSecret = ""; |
155 | saveConfiguration(); |
156 | |
157 | } catch (OAuthMessageSignerException e1) { |
158 | e1.printStackTrace(); |
159 | return null; |
160 | } catch (OAuthNotAuthorizedException e1) { |
161 | e1.printStackTrace(); |
162 | return null; |
163 | } catch (OAuthExpectationFailedException e1) { |
164 | e1.printStackTrace(); |
165 | return null; |
166 | } catch (OAuthCommunicationException e1) { |
167 | e1.printStackTrace(); |
168 | return null; |
169 | } |
170 | |
171 | if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Authorization URL : "+url); |
172 | |
173 | return Uri.parse(url); |
174 | } |
175 | |
176 | public boolean getAccess(String verifier) throws UnknownHostException { |
177 | |
178 | Log.i(TAG, "Verifier: "+verifier); |
179 | |
180 | // this method shouldn't have been called |
181 | if (isAuthenticated()) |
182 | return false; |
183 | |
184 | if (!requestToken.equals("") && !requestTokenSecret.equals("")) { |
185 | consumer.setTokenWithSecret(requestToken, requestTokenSecret); |
186 | if(Tomdroid.LOGGING_ENABLED) { |
187 | Log.d(TAG, "Added request token "+requestTokenSecret+" and request token secret "+requestTokenSecret); |
188 | } |
189 | } |
190 | else |
191 | return false; |
192 | |
193 | OAuthProvider provider = getProvider(); |
194 | |
195 | try { |
196 | provider.retrieveAccessToken(consumer, verifier); |
197 | } catch (OAuthMessageSignerException e1) { |
198 | e1.printStackTrace(); |
199 | return false; |
200 | } catch (OAuthNotAuthorizedException e1) { |
201 | e1.printStackTrace(); |
202 | return false; |
203 | } catch (OAuthExpectationFailedException e1) { |
204 | e1.printStackTrace(); |
205 | return false; |
206 | } catch (OAuthCommunicationException e1) { |
207 | e1.printStackTrace(); |
208 | return false; |
209 | } |
210 | |
211 | // access has been granted, store the access token |
212 | accessToken = consumer.getToken(); |
213 | accessTokenSecret = consumer.getTokenSecret(); |
214 | requestToken = ""; |
215 | requestTokenSecret = ""; |
216 | |
217 | try { |
218 | JSONObject response = new JSONObject(get(rootApi)); |
219 | // append a slash to the url, else the signature will fail |
220 | userApi = response.getJSONObject("user-ref").getString("api-ref"); |
221 | } catch (JSONException e) { |
222 | // TODO Auto-generated catch block |
223 | e.printStackTrace(); |
224 | } |
225 | |
226 | saveConfiguration(); |
227 | |
228 | if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Got access token "+consumer.getToken()+"."); |
229 | |
230 | return true; |
231 | } |
232 | |
233 | @Override |
234 | public String get(String uri) throws java.net.UnknownHostException { |
235 | |
236 | // Prepare a request object |
237 | HttpGet httpGet = new HttpGet(uri); |
238 | sign(httpGet); |
239 | HttpResponse response = execute(httpGet); |
240 | return parseResponse(response); |
241 | } |
242 | |
243 | @Override |
244 | public String put(String uri, String data) throws UnknownHostException { |
245 | |
246 | // Prepare a request object |
247 | HttpPut httpPut = new HttpPut(uri); |
248 | |
249 | try { |
250 | // The default http content charset is ISO-8859-1, JSON requires UTF-8 |
251 | httpPut.setEntity(new StringEntity(data, "UTF-8")); |
252 | } catch (UnsupportedEncodingException e1) { |
253 | e1.printStackTrace(); |
254 | return null; |
255 | } |
256 | |
257 | httpPut.setHeader("Content-Type", "application/json"); |
258 | sign(httpPut); |
259 | |
260 | // Do not handle redirects, we need to sign the request again as the old signature will be invalid |
261 | HttpResponse response = execute(httpPut); |
262 | return parseResponse(response); |
263 | } |
264 | |
265 | private void saveConfiguration() { |
266 | |
267 | Preferences.putString(Preferences.Key.ACCESS_TOKEN, accessToken); |
268 | Preferences.putString(Preferences.Key.ACCESS_TOKEN_SECRET, accessTokenSecret); |
269 | Preferences.putString(Preferences.Key.ACCESS_TOKEN_URL, accessTokenUrl); |
270 | Preferences.putString(Preferences.Key.REQUEST_TOKEN, requestToken); |
271 | Preferences.putString(Preferences.Key.REQUEST_TOKEN_SECRET, requestTokenSecret); |
272 | Preferences.putString(Preferences.Key.REQUEST_TOKEN_URL, requestTokenUrl); |
273 | Preferences.putBoolean(Preferences.Key.OAUTH_10A, oauth10a); |
274 | Preferences.putString(Preferences.Key.AUTHORIZE_URL, authorizeUrl); |
275 | Preferences.putString(Preferences.Key.SYNC_SERVER_ROOT_API, rootApi); |
276 | Preferences.putString(Preferences.Key.SYNC_SERVER_USER_API, userApi); |
277 | } |
278 | } |