pax_global_header00006660000000000000000000000064117223564060014520gustar00rootroot0000000000000052 comment=2bf066adae095140d7d0661396c790f343bd396d kaeppler-signpost-653c361/000077500000000000000000000000001172235640600154435ustar00rootroot00000000000000kaeppler-signpost-653c361/.classpath000066400000000000000000000001541172235640600174260ustar00rootroot00000000000000 kaeppler-signpost-653c361/.gitignore000066400000000000000000000000211172235640600174240ustar00rootroot00000000000000target .DS_Store kaeppler-signpost-653c361/.project000066400000000000000000000012241172235640600171110ustar00rootroot00000000000000 oauth-signpost signpost-commonshttp4 signpost-core signpost-jetty6 org.eclipse.jdt.core.javabuilder org.maven.ide.eclipse.maven2Builder org.eclipse.jdt.core.javanature org.maven.ide.eclipse.maven2Nature kaeppler-signpost-653c361/.settings/000077500000000000000000000000001172235640600173615ustar00rootroot00000000000000kaeppler-signpost-653c361/.settings/org.eclipse.jdt.ui.prefs000066400000000000000000000001411172235640600240240ustar00rootroot00000000000000#Sun Jun 07 00:56:55 CEST 2009 eclipse.preferences.version=1 internal.default.compliance=default kaeppler-signpost-653c361/README.markdown000066400000000000000000000002611172235640600201430ustar00rootroot00000000000000# You will only find the soure code here. Looking for the project homepage? Try looking here: [http://code.google.com/p/oauth-signpost/](http://code.google.com/p/oauth-signpost)kaeppler-signpost-653c361/pom.xml000066400000000000000000000132571172235640600167700ustar00rootroot00000000000000 4.0.0 oauth.signpost oauth-signpost pom 1.2.1.2 oauth-signpost A simple, light-weight, and modular OAuth client library for the Java platform. http://code.google.com/p/oauth-signpost/ Google Code Issue Tracker http://code.google.com/p/oauth-signpost/issues/list mk Matthias Käppler matthias@qype.com http://matthiaskaeppler.de Qype GmbH http://www.qype.com +1 signpost-core signpost-commonshttp4 signpost-jetty6 The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo http://github.com/kaeppler/signpost scm:git:git://github.com/kaeppler/signpost.git scm:git:git@github.com:kaeppler/signpost.git UTF-8 junit junit 4.5 test org.mockito mockito-core 1.9.0 test package org.apache.maven.plugins maven-compiler-plugin 1.5 1.5 org.apache.maven.plugins maven-jar-plugin 2.2 test-jar org.apache.maven.plugins maven-javadoc-plugin 2.6.1 maven-release-plugin 2.0-beta-9 signpost-releases Signpost Release Repository http://oss.sonatype.org/service/local/staging/deploy/maven2 signpost-snapshots Signpost Snapshot Repository http://oss.sonatype.org/content/repositories/signpost-snapshots/ maven-javadoc-plugin copy-jar copyTo maven-antrun-plugin install run release-sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.0-alpha-4 sign-artifacts verify sign kaeppler-signpost-653c361/signpost-commonshttp4/000077500000000000000000000000001172235640600217465ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/.classpath000066400000000000000000000006651172235640600237400ustar00rootroot00000000000000 kaeppler-signpost-653c361/signpost-commonshttp4/.project000066400000000000000000000010501172235640600234110ustar00rootroot00000000000000 signpost-commonshttp4 org.eclipse.jdt.core.javabuilder org.maven.ide.eclipse.maven2Builder org.eclipse.jdt.core.javanature org.maven.ide.eclipse.maven2Nature kaeppler-signpost-653c361/signpost-commonshttp4/.settings/000077500000000000000000000000001172235640600236645ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/.settings/org.eclipse.jdt.ui.prefs000066400000000000000000000001411172235640600303270ustar00rootroot00000000000000#Sun Jun 07 00:56:45 CEST 2009 eclipse.preferences.version=1 internal.default.compliance=default kaeppler-signpost-653c361/signpost-commonshttp4/.settings/org.eclipse.ltk.core.refactoring.prefs000066400000000000000000000002071172235640600331600ustar00rootroot00000000000000#Sun Jun 07 00:58:01 CEST 2009 eclipse.preferences.version=1 org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false kaeppler-signpost-653c361/signpost-commonshttp4/.settings/org.maven.ide.eclipse.prefs000066400000000000000000000004071172235640600310050ustar00rootroot00000000000000#Sat Jun 06 17:58:11 CEST 2009 activeProfiles= eclipse.preferences.version=1 fullBuildGoals=process-test-resources includeModules=false resolveWorkspaceProjects=true resourceFilterGoals=process-resources resources\:testResources skipCompilerPlugin=true version=1 kaeppler-signpost-653c361/signpost-commonshttp4/pom.xml000066400000000000000000000026341172235640600232700ustar00rootroot00000000000000 oauth-signpost oauth.signpost 1.2.1.2 4.0.0 signpost-commonshttp4 signpost-commonshttp4 oauth.signpost signpost-core ${project.version} compile org.apache.httpcomponents httpcore 4.0.1 compile org.apache.httpcomponents httpclient 4.0.1 compile oauth.signpost signpost-core ${project.version} test-jar test maven-antrun-plugin kaeppler-signpost-653c361/signpost-commonshttp4/src/000077500000000000000000000000001172235640600225355ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/000077500000000000000000000000001172235640600234615ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/000077500000000000000000000000001172235640600244025ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/000077500000000000000000000000001172235640600255225ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/000077500000000000000000000000001172235640600273705ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/commonshttp/000077500000000000000000000000001172235640600317435ustar00rootroot00000000000000CommonsHttpOAuthConsumer.java000066400000000000000000000030051172235640600374550ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/commonshttp/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.commonshttp; import oauth.signpost.AbstractOAuthConsumer; import oauth.signpost.http.HttpRequest; /** * Supports signing HTTP requests of type {@link org.apache.http.HttpRequest}. * * @author Matthias Kaeppler */ public class CommonsHttpOAuthConsumer extends AbstractOAuthConsumer { private static final long serialVersionUID = 1L; public CommonsHttpOAuthConsumer(String consumerKey, String consumerSecret) { super(consumerKey, consumerSecret); } @Override protected HttpRequest wrap(Object request) { if (!(request instanceof org.apache.http.HttpRequest)) { throw new IllegalArgumentException( "This consumer expects requests of type " + org.apache.http.HttpRequest.class.getCanonicalName()); } return new HttpRequestAdapter((org.apache.http.client.methods.HttpUriRequest) request); } } CommonsHttpOAuthProvider.java000066400000000000000000000063361172235640600374660ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/commonshttp/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost.commonshttp; import java.io.IOException; import oauth.signpost.AbstractOAuthProvider; import oauth.signpost.http.HttpRequest; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.DefaultHttpClient; /** * This implementation uses the Apache Commons {@link HttpClient} 4.x HTTP * implementation to fetch OAuth tokens from a service provider. Android users * should use this provider implementation in favor of the default one, since * the latter is known to cause problems with Android's Apache Harmony * underpinnings. * * @author Matthias Kaeppler */ public class CommonsHttpOAuthProvider extends AbstractOAuthProvider { private static final long serialVersionUID = 1L; private transient HttpClient httpClient; public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl, String authorizationWebsiteUrl) { super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl); this.httpClient = new DefaultHttpClient(); } public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl, String authorizationWebsiteUrl, HttpClient httpClient) { super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl); this.httpClient = httpClient; } public void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } @Override protected HttpRequest createRequest(String endpointUrl) throws Exception { HttpPost request = new HttpPost(endpointUrl); return new HttpRequestAdapter(request); } @Override protected oauth.signpost.http.HttpResponse sendRequest(HttpRequest request) throws Exception { HttpResponse response = httpClient.execute((HttpUriRequest) request.unwrap()); return new HttpResponseAdapter(response); } @Override protected void closeConnection(HttpRequest request, oauth.signpost.http.HttpResponse response) throws Exception { if (response != null) { HttpEntity entity = ((HttpResponse) response.unwrap()).getEntity(); if (entity != null) { try { // free the connection entity.consumeContent(); } catch (IOException e) { // this means HTTP keep-alive is not possible e.printStackTrace(); } } } } } HttpRequestAdapter.java000066400000000000000000000041331172235640600363210ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/commonshttppackage oauth.signpost.commonshttp; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.client.methods.HttpUriRequest; public class HttpRequestAdapter implements oauth.signpost.http.HttpRequest { private HttpUriRequest request; private HttpEntity entity; public HttpRequestAdapter(org.apache.http.client.methods.HttpUriRequest request) { this.request = request; if (request instanceof HttpEntityEnclosingRequest) { entity = ((HttpEntityEnclosingRequest) request).getEntity(); } } public String getMethod() { return request.getRequestLine().getMethod(); } public String getRequestUrl() { return request.getURI().toString(); } public void setRequestUrl(String url) { throw new RuntimeException(new UnsupportedOperationException()); } public String getHeader(String name) { Header header = request.getFirstHeader(name); if (header == null) { return null; } return header.getValue(); } public void setHeader(String name, String value) { request.setHeader(name, value); } public Map getAllHeaders() { Header[] origHeaders = request.getAllHeaders(); HashMap headers = new HashMap(); for (Header h : origHeaders) { headers.put(h.getName(), h.getValue()); } return headers; } public String getContentType() { if (entity == null) { return null; } Header header = entity.getContentType(); if (header == null) { return null; } return header.getValue(); } public InputStream getMessagePayload() throws IOException { if (entity == null) { return null; } return entity.getContent(); } public Object unwrap() { return request; } } HttpResponseAdapter.java000066400000000000000000000013611172235640600364670ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/main/java/oauth/signpost/commonshttppackage oauth.signpost.commonshttp; import java.io.IOException; import java.io.InputStream; public class HttpResponseAdapter implements oauth.signpost.http.HttpResponse { private org.apache.http.HttpResponse response; public HttpResponseAdapter(org.apache.http.HttpResponse response) { this.response = response; } public InputStream getContent() throws IOException { return response.getEntity().getContent(); } public int getStatusCode() throws IOException { return response.getStatusLine().getStatusCode(); } public String getReasonPhrase() throws Exception { return response.getStatusLine().getReasonPhrase(); } public Object unwrap() { return response; } } kaeppler-signpost-653c361/signpost-commonshttp4/src/test/000077500000000000000000000000001172235640600235145ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/000077500000000000000000000000001172235640600244355ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/000077500000000000000000000000001172235640600255555ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/signpost/000077500000000000000000000000001172235640600274235ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/signpost/commonshttp/000077500000000000000000000000001172235640600317765ustar00rootroot00000000000000CommonHttpOAuthProviderMock.java000066400000000000000000000035431172235640600401450ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/signpost/commonshttppackage oauth.signpost.commonshttp; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.InputStream; import oauth.signpost.http.HttpRequest; import oauth.signpost.mocks.OAuthProviderMock; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.InputStreamEntity; import org.apache.http.message.BasicStatusLine; import org.mockito.Mockito; @SuppressWarnings("serial") public class CommonHttpOAuthProviderMock extends CommonsHttpOAuthProvider implements OAuthProviderMock { private HttpClient httpClientMock; public CommonHttpOAuthProviderMock(String requestTokenUrl, String accessTokenUrl, String websiteUrl) { super(requestTokenUrl, accessTokenUrl, websiteUrl); } @Override protected oauth.signpost.http.HttpResponse sendRequest(HttpRequest request) throws Exception { HttpResponse resp = httpClientMock.execute((HttpUriRequest) request.unwrap()); return new HttpResponseAdapter(resp); } public void mockConnection(String responseBody) throws Exception { HttpResponse response = mock(HttpResponse.class); this.httpClientMock = mock(HttpClient.class); InputStream is = new ByteArrayInputStream(responseBody.getBytes()); InputStreamEntity entity = new InputStreamEntity(is, responseBody.length()); StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"); when(response.getStatusLine()).thenReturn(statusLine); when(response.getEntity()).thenReturn(entity); when(httpClientMock.execute(Mockito.any(HttpUriRequest.class))).thenReturn(response); } } CommonsHttpOAuthProviderTest.java000066400000000000000000000017151172235640600403550ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/signpost/commonshttppackage oauth.signpost.commonshttp; import oauth.signpost.OAuth; import oauth.signpost.OAuthProvider; import oauth.signpost.OAuthProviderTest; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class CommonsHttpOAuthProviderTest extends OAuthProviderTest { @Override protected OAuthProvider buildProvider(String requestTokenUrl, String accessTokenUrl, String websiteUrl, boolean mockConnection) throws Exception { if (mockConnection) { CommonHttpOAuthProviderMock provider = new CommonHttpOAuthProviderMock(requestTokenUrl, accessTokenUrl, websiteUrl); provider.mockConnection(OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_TOKEN_SECRET + "=" + TOKEN_SECRET); return provider; } return new CommonsHttpOAuthProvider(requestTokenUrl, accessTokenUrl, websiteUrl); } } HttpRequestAdapterTest.java000066400000000000000000000013101172235640600372060ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-commonshttp4/src/test/java/oauth/signpost/commonshttppackage oauth.signpost.commonshttp; import oauth.signpost.basic.HttpRequestAdapterTestBase; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class HttpRequestAdapterTest extends HttpRequestAdapterTestBase { @Override public void prepareRequest() throws Exception { HttpPost r = new HttpPost(URL); r.setHeader(HEADER_NAME, HEADER_VALUE); StringEntity body = new StringEntity(PAYLOAD); body.setContentType(CONTENT_TYPE); r.setEntity(body); request = new HttpRequestAdapter(r); } } kaeppler-signpost-653c361/signpost-core/000077500000000000000000000000001172235640600202375ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/.classpath000066400000000000000000000006651172235640600222310ustar00rootroot00000000000000 kaeppler-signpost-653c361/signpost-core/.project000066400000000000000000000010401172235640600217010ustar00rootroot00000000000000 signpost-core org.eclipse.jdt.core.javabuilder org.maven.ide.eclipse.maven2Builder org.eclipse.jdt.core.javanature org.maven.ide.eclipse.maven2Nature kaeppler-signpost-653c361/signpost-core/.settings/000077500000000000000000000000001172235640600221555ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/.settings/org.eclipse.jdt.ui.prefs000066400000000000000000000001361172235640600266240ustar00rootroot00000000000000#Sat Jun 06 17:56:02 CEST 2009 eclipse.preferences.version=1 internal.default.compliance=user kaeppler-signpost-653c361/signpost-core/.settings/org.maven.ide.eclipse.prefs000066400000000000000000000004071172235640600272760ustar00rootroot00000000000000#Sat Jun 06 17:49:21 CEST 2009 activeProfiles= eclipse.preferences.version=1 fullBuildGoals=process-test-resources includeModules=false resolveWorkspaceProjects=true resourceFilterGoals=process-resources resources\:testResources skipCompilerPlugin=true version=1 kaeppler-signpost-653c361/signpost-core/pom.xml000066400000000000000000000015021172235640600215520ustar00rootroot00000000000000 oauth-signpost oauth.signpost 1.2.1.2 4.0.0 signpost-core signpost-core commons-codec commons-codec 1.3 compile maven-antrun-plugin kaeppler-signpost-653c361/signpost-core/src/000077500000000000000000000000001172235640600210265ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/000077500000000000000000000000001172235640600217525ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/000077500000000000000000000000001172235640600226735ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/000077500000000000000000000000001172235640600234515ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/000077500000000000000000000000001172235640600247255ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/000077500000000000000000000000001172235640600260055ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/000077500000000000000000000000001172235640600267625ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/000077500000000000000000000000001172235640600302525ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/base/000077500000000000000000000000001172235640600311645ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/base/Escaper.java000066400000000000000000000072751172235640600334240ustar00rootroot00000000000000/* Copyright (c) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gdata.util.common.base; /** * An object that converts literal text into a format safe for inclusion in a * particular context (such as an XML document). Typically (but not always), the * inverse process of "unescaping" the text is performed automatically by the * relevant parser. * *

For example, an XML escaper would convert the literal string {@code * "Foo"} into {@code "Foo<Bar>"} to prevent {@code ""} from * being confused with an XML tag. When the resulting XML document is parsed, * the parser API will return this text as the original literal string {@code * "Foo"}. * *

An {@code Escaper} instance is required to be stateless, and safe when * used concurrently by multiple threads. * *

Several popular escapers are defined as constants in the class {@link * CharEscapers}. To create your own escapers, use {@link * CharEscaperBuilder}, or extend {@link CharEscaper} or {@code UnicodeEscaper}. * * */ public interface Escaper { /** * Returns the escaped form of a given literal string. * *

Note that this method may treat input characters differently depending on * the specific escaper implementation. *

    *
  • {@link UnicodeEscaper} handles * UTF-16 correctly, * including surrogate character pairs. If the input is badly formed the * escaper should throw {@link IllegalArgumentException}. *
  • {@link CharEscaper} handles Java characters independently and does not * verify the input for well formed characters. A CharEscaper should not be * used in situations where input is not guaranteed to be restricted to the * Basic Multilingual Plane (BMP). *
* * @param string the literal string to be escaped * @return the escaped form of {@code string} * @throws NullPointerException if {@code string} is null * @throws IllegalArgumentException if {@code string} contains badly formed * UTF-16 or cannot be escaped for any other reason */ public String escape(String string); /** * Returns an {@code Appendable} instance which automatically escapes all * text appended to it before passing the resulting text to an underlying * {@code Appendable}. * *

Note that this method may treat input characters differently depending on * the specific escaper implementation. *

    *
  • {@link UnicodeEscaper} handles * UTF-16 correctly, * including surrogate character pairs. If the input is badly formed the * escaper should throw {@link IllegalArgumentException}. *
  • {@link CharEscaper} handles Java characters independently and does not * verify the input for well formed characters. A CharEscaper should not be * used in situations where input is not guaranteed to be restricted to the * Basic Multilingual Plane (BMP). *
* * @param out the underlying {@code Appendable} to append escaped output to * @return an {@code Appendable} which passes text to {@code out} after * escaping it. */ public Appendable escape(Appendable out); } PercentEscaper.java000066400000000000000000000232471172235640600346630ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/base/* Copyright (c) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gdata.util.common.base; /** * A {@code UnicodeEscaper} that escapes some set of Java characters using * the URI percent encoding scheme. The set of safe characters (those which * remain unescaped) can be specified on construction. * *

For details on escaping URIs for use in web pages, see section 2.4 of * RFC 3986. * *

In most cases this class should not need to be used directly. If you * have no special requirements for escaping your URIs, you should use either * {@link CharEscapers#uriEscaper()} or * {@link CharEscapers#uriEscaper(boolean)}. * *

When encoding a String, the following rules apply: *

    *
  • The alphanumeric characters "a" through "z", "A" through "Z" and "0" * through "9" remain the same. *
  • Any additionally specified safe characters remain the same. *
  • If {@code plusForSpace} was specified, the space character " " is * converted into a plus sign "+". *
  • All other characters are converted into one or more bytes using UTF-8 * encoding and each byte is then represented by the 3-character string * "%XY", where "XY" is the two-digit, uppercase, hexadecimal representation * of the byte value. *
* *

RFC 2396 specifies the set of unreserved characters as "-", "_", ".", "!", * "~", "*", "'", "(" and ")". It goes on to state: * *

Unreserved characters can be escaped without changing the semantics * of the URI, but this should not be done unless the URI is being used * in a context that does not allow the unescaped character to appear. * *

For performance reasons the only currently supported character encoding of * this class is UTF-8. * *

Note: This escaper produces uppercase hexidecimal sequences. From * RFC 3986:
* "URI producers and normalizers should use uppercase hexadecimal digits * for all percent-encodings." * * */ public class PercentEscaper extends UnicodeEscaper { /** * A string of safe characters that mimics the behavior of * {@link java.net.URLEncoder}. * */ public static final String SAFECHARS_URLENCODER = "-_.*"; /** * A string of characters that do not need to be encoded when used in URI * path segments, as specified in RFC 3986. Note that some of these * characters do need to be escaped when used in other parts of the URI. */ public static final String SAFEPATHCHARS_URLENCODER = "-_.!~*'()@:$&,;="; /** * A string of characters that do not need to be encoded when used in URI * query strings, as specified in RFC 3986. Note that some of these * characters do need to be escaped when used in other parts of the URI. */ public static final String SAFEQUERYSTRINGCHARS_URLENCODER = "-_.!~*'()@:$,;/?:"; // In some uri escapers spaces are escaped to '+' private static final char[] URI_ESCAPED_SPACE = { '+' }; private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); /** * If true we should convert space to the {@code +} character. */ private final boolean plusForSpace; /** * An array of flags where for any {@code char c} if {@code safeOctets[c]} is * true then {@code c} should remain unmodified in the output. If * {@code c > safeOctets.length} then it should be escaped. */ private final boolean[] safeOctets; /** * Constructs a URI escaper with the specified safe characters and optional * handling of the space character. * * @param safeChars a non null string specifying additional safe characters * for this escaper (the ranges 0..9, a..z and A..Z are always safe and * should not be specified here) * @param plusForSpace true if ASCII space should be escaped to {@code +} * rather than {@code %20} * @throws IllegalArgumentException if any of the parameters were invalid */ public PercentEscaper(String safeChars, boolean plusForSpace) { // Avoid any misunderstandings about the behavior of this escaper if (safeChars.matches(".*[0-9A-Za-z].*")) { throw new IllegalArgumentException( "Alphanumeric characters are always 'safe' and should not be " + "explicitly specified"); } // Avoid ambiguous parameters. Safe characters are never modified so if // space is a safe character then setting plusForSpace is meaningless. if (plusForSpace && safeChars.contains(" ")) { throw new IllegalArgumentException( "plusForSpace cannot be specified when space is a 'safe' character"); } if (safeChars.contains("%")) { throw new IllegalArgumentException( "The '%' character cannot be specified as 'safe'"); } this.plusForSpace = plusForSpace; this.safeOctets = createSafeOctets(safeChars); } /** * Creates a boolean[] with entries corresponding to the character values * for 0-9, A-Z, a-z and those specified in safeChars set to true. The array * is as small as is required to hold the given character information. */ private static boolean[] createSafeOctets(String safeChars) { int maxChar = 'z'; char[] safeCharArray = safeChars.toCharArray(); for (char c : safeCharArray) { maxChar = Math.max(c, maxChar); } boolean[] octets = new boolean[maxChar + 1]; for (int c = '0'; c <= '9'; c++) { octets[c] = true; } for (int c = 'A'; c <= 'Z'; c++) { octets[c] = true; } for (int c = 'a'; c <= 'z'; c++) { octets[c] = true; } for (char c : safeCharArray) { octets[c] = true; } return octets; } /* * Overridden for performance. For unescaped strings this improved the * performance of the uri escaper from ~760ns to ~400ns as measured by * {@link CharEscapersBenchmark}. */ @Override protected int nextEscapeIndex(CharSequence csq, int index, int end) { for (; index < end; index++) { char c = csq.charAt(index); if (c >= safeOctets.length || !safeOctets[c]) { break; } } return index; } /* * Overridden for performance. For unescaped strings this improved the * performance of the uri escaper from ~400ns to ~170ns as measured by * {@link CharEscapersBenchmark}. */ @Override public String escape(String s) { int slen = s.length(); for (int index = 0; index < slen; index++) { char c = s.charAt(index); if (c >= safeOctets.length || !safeOctets[c]) { return escapeSlow(s, index); } } return s; } /** * Escapes the given Unicode code point in UTF-8. */ @Override protected char[] escape(int cp) { // We should never get negative values here but if we do it will throw an // IndexOutOfBoundsException, so at least it will get spotted. if (cp < safeOctets.length && safeOctets[cp]) { return null; } else if (cp == ' ' && plusForSpace) { return URI_ESCAPED_SPACE; } else if (cp <= 0x7F) { // Single byte UTF-8 characters // Start with "%--" and fill in the blanks char[] dest = new char[3]; dest[0] = '%'; dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; dest[1] = UPPER_HEX_DIGITS[cp >>> 4]; return dest; } else if (cp <= 0x7ff) { // Two byte UTF-8 characters [cp >= 0x80 && cp <= 0x7ff] // Start with "%--%--" and fill in the blanks char[] dest = new char[6]; dest[0] = '%'; dest[3] = '%'; dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[1] = UPPER_HEX_DIGITS[0xC | cp]; return dest; } else if (cp <= 0xffff) { // Three byte UTF-8 characters [cp >= 0x800 && cp <= 0xffff] // Start with "%E-%--%--" and fill in the blanks char[] dest = new char[9]; dest[0] = '%'; dest[1] = 'E'; dest[3] = '%'; dest[6] = '%'; dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[2] = UPPER_HEX_DIGITS[cp]; return dest; } else if (cp <= 0x10ffff) { char[] dest = new char[12]; // Four byte UTF-8 characters [cp >= 0xffff && cp <= 0x10ffff] // Start with "%F-%--%--%--" and fill in the blanks dest[0] = '%'; dest[1] = 'F'; dest[3] = '%'; dest[6] = '%'; dest[9] = '%'; dest[11] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[10] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; cp >>>= 4; dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; cp >>>= 2; dest[2] = UPPER_HEX_DIGITS[cp & 0x7]; return dest; } else { // If this ever happens it is due to bug in UnicodeEscaper, not bad input. throw new IllegalArgumentException( "Invalid unicode character value " + cp); } } } Preconditions.java000066400000000000000000000446261172235640600346040ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/base/* * Copyright (C) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gdata.util.common.base; import java.util.Collection; import java.util.NoSuchElementException; /** * Simple static methods to be called at the start of your own methods to verify * correct arguments and state. This allows constructs such as *

 *     if (count <= 0) {
 *       throw new IllegalArgumentException("must be positive: " + count);
 *     }
* * to be replaced with the more compact *
 *     checkArgument(count > 0, "must be positive: %s", count);
* * Note that the sense of the expression is inverted; with {@code Preconditions} * you declare what you expect to be true, just as you do with an * * {@code assert} or a JUnit {@code assertTrue()} call. * *

Take care not to confuse precondition checking with other similar types * of checks! Precondition exceptions -- including those provided here, but also * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link * UnsupportedOperationException} and others -- are used to signal that the * calling method has made an error. This tells the caller that it should * not have invoked the method when it did, with the arguments it did, or * perhaps ever. Postcondition or other invariant failures should not * throw these types of exceptions. * *

Note: The methods of the {@code Preconditions} class are highly * unusual in one way: they are supposed to throw exceptions, and promise * in their specifications to do so even when given perfectly valid input. That * is, {@code null} is a valid parameter to the method {@link * #checkNotNull(Object)} -- and technically this parameter could be even marked * as {@link Nullable} -- yet the method will still throw an exception anyway, * because that's what its contract says to do. * *

This class may be used with the Google Web Toolkit (GWT). * * */ public final class Preconditions { private Preconditions() {} /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @throws IllegalArgumentException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let * this happen) */ public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException( format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @throws IllegalStateException if {@code expression} is false */ public static void checkState(boolean expression) { if (!expression) { throw new IllegalStateException(); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @throws IllegalStateException if {@code expression} is false */ public static void checkState(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalStateException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @throws IllegalStateException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let * this happen) */ public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException( format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference, Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway throw new NullPointerException( format(errorMessageTemplate, errorMessageArgs)); } return reference; } /** * Ensures that an {@code Iterable} object passed as a parameter to the * calling method is not null and contains no null elements. * * @param iterable the iterable to check the contents of * @return the non-null {@code iterable} reference just validated * @throws NullPointerException if {@code iterable} is null or contains at * least one null element */ public static > T checkContentsNotNull(T iterable) { if (containsOrIsNull(iterable)) { throw new NullPointerException(); } return iterable; } /** * Ensures that an {@code Iterable} object passed as a parameter to the * calling method is not null and contains no null elements. * * @param iterable the iterable to check the contents of * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @return the non-null {@code iterable} reference just validated * @throws NullPointerException if {@code iterable} is null or contains at * least one null element */ public static > T checkContentsNotNull( T iterable, Object errorMessage) { if (containsOrIsNull(iterable)) { throw new NullPointerException(String.valueOf(errorMessage)); } return iterable; } /** * Ensures that an {@code Iterable} object passed as a parameter to the * calling method is not null and contains no null elements. * * @param iterable the iterable to check the contents of * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @return the non-null {@code iterable} reference just validated * @throws NullPointerException if {@code iterable} is null or contains at * least one null element */ public static > T checkContentsNotNull(T iterable, String errorMessageTemplate, Object... errorMessageArgs) { if (containsOrIsNull(iterable)) { throw new NullPointerException( format(errorMessageTemplate, errorMessageArgs)); } return iterable; } private static boolean containsOrIsNull(Iterable iterable) { if (iterable == null) { return true; } if (iterable instanceof Collection) { Collection collection = (Collection) iterable; try { return collection.contains(null); } catch (NullPointerException e) { // A NPE implies that the collection doesn't contain null. return false; } } else { for (Object element : iterable) { if (element == null) { return true; } } return false; } } /** * Ensures that {@code index} specifies a valid element in an array, * list or string of size {@code size}. An element index may range from zero, * inclusive, to {@code size}, exclusive. * * @param index a user-supplied index identifying an element of an array, list * or string * @param size the size of that array, list or string * @throws IndexOutOfBoundsException if {@code index} is negative or is not * less than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkElementIndex(int index, int size) { checkElementIndex(index, size, "index"); } /** * Ensures that {@code index} specifies a valid element in an array, * list or string of size {@code size}. An element index may range from zero, * inclusive, to {@code size}, exclusive. * * @param index a user-supplied index identifying an element of an array, list * or string * @param size the size of that array, list or string * @param desc the text to use to describe this index in an error message * @throws IndexOutOfBoundsException if {@code index} is negative or is not * less than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkElementIndex(int index, int size, String desc) { checkArgument(size >= 0, "negative size: %s", size); if (index < 0) { throw new IndexOutOfBoundsException( format("%s (%s) must not be negative", desc, index)); } if (index >= size) { throw new IndexOutOfBoundsException( format("%s (%s) must be less than size (%s)", desc, index, size)); } } /** * Ensures that {@code index} specifies a valid position in an array, * list or string of size {@code size}. A position index may range from zero * to {@code size}, inclusive. * * @param index a user-supplied index identifying a position in an array, list * or string * @param size the size of that array, list or string * @throws IndexOutOfBoundsException if {@code index} is negative or is * greater than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkPositionIndex(int index, int size) { checkPositionIndex(index, size, "index"); } /** * Ensures that {@code index} specifies a valid position in an array, * list or string of size {@code size}. A position index may range from zero * to {@code size}, inclusive. * * @param index a user-supplied index identifying a position in an array, list * or string * @param size the size of that array, list or string * @param desc the text to use to describe this index in an error message * @throws IndexOutOfBoundsException if {@code index} is negative or is * greater than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkPositionIndex(int index, int size, String desc) { checkArgument(size >= 0, "negative size: %s", size); if (index < 0) { throw new IndexOutOfBoundsException(format( "%s (%s) must not be negative", desc, index)); } if (index > size) { throw new IndexOutOfBoundsException(format( "%s (%s) must not be greater than size (%s)", desc, index, size)); } } /** * Ensures that {@code start} and {@code end} specify a valid positions * in an array, list or string of size {@code size}, and are in order. A * position index may range from zero to {@code size}, inclusive. * * @param start a user-supplied index identifying a starting position in an * array, list or string * @param end a user-supplied index identifying a ending position in an array, * list or string * @param size the size of that array, list or string * @throws IndexOutOfBoundsException if either index is negative or is * greater than {@code size}, or if {@code end} is less than {@code start} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkPositionIndexes(int start, int end, int size) { checkPositionIndex(start, size, "start index"); checkPositionIndex(end, size, "end index"); if (end < start) { throw new IndexOutOfBoundsException(format( "end index (%s) must not be less than start index (%s)", end, start)); } } /** * Substitutes each {@code %s} in {@code template} with an argument. These * are matched by position - the first {@code %s} gets {@code args[0]}, etc. * If there are more arguments than placeholders, the unmatched arguments will * be appended to the end of the formatted message in square braces. * * @param template a non-null string containing 0 or more {@code %s} * placeholders. * @param args the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. */ // VisibleForTesting static String format(String template, Object... args) { // start substituting the arguments into the '%s' placeholders StringBuilder builder = new StringBuilder( template.length() + 16 * args.length); int templateStart = 0; int i = 0; while (i < args.length) { int placeholderStart = template.indexOf("%s", templateStart); if (placeholderStart == -1) { break; } builder.append(template.substring(templateStart, placeholderStart)); builder.append(args[i++]); templateStart = placeholderStart + 2; } builder.append(template.substring(templateStart)); // if we run out of placeholders, append the extra args in square braces if (i < args.length) { builder.append(" ["); builder.append(args[i++]); while (i < args.length) { builder.append(", "); builder.append(args[i++]); } builder.append("]"); } return builder.toString(); } } UnicodeEscaper.java000066400000000000000000000460761172235640600346560ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/com/google/gdata/util/common/base/* Copyright (c) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gdata.util.common.base; import static com.google.gdata.util.common.base.Preconditions.checkNotNull; import java.io.IOException; /** * An {@link Escaper} that converts literal text into a format safe for * inclusion in a particular context (such as an XML document). Typically (but * not always), the inverse process of "unescaping" the text is performed * automatically by the relevant parser. * *

For example, an XML escaper would convert the literal string {@code * "Foo"} into {@code "Foo<Bar>"} to prevent {@code ""} from * being confused with an XML tag. When the resulting XML document is parsed, * the parser API will return this text as the original literal string {@code * "Foo"}. * *

Note: This class is similar to {@link CharEscaper} but with one * very important difference. A CharEscaper can only process Java * UTF16 characters in * isolation and may not cope when it encounters surrogate pairs. This class * facilitates the correct escaping of all Unicode characters. * *

As there are important reasons, including potential security issues, to * handle Unicode correctly if you are considering implementing a new escaper * you should favor using UnicodeEscaper wherever possible. * *

A {@code UnicodeEscaper} instance is required to be stateless, and safe * when used concurrently by multiple threads. * *

Several popular escapers are defined as constants in the class {@link * CharEscapers}. To create your own escapers extend this class and implement * the {@link #escape(int)} method. * * */ public abstract class UnicodeEscaper implements Escaper { /** The amount of padding (chars) to use when growing the escape buffer. */ private static final int DEST_PAD = 32; /** * Returns the escaped form of the given Unicode code point, or {@code null} * if this code point does not need to be escaped. When called as part of an * escaping operation, the given code point is guaranteed to be in the range * {@code 0 <= cp <= Character#MAX_CODE_POINT}. * *

If an empty array is returned, this effectively strips the input * character from the resulting text. * *

If the character does not need to be escaped, this method should return * {@code null}, rather than an array containing the character representation * of the code point. This enables the escaping algorithm to perform more * efficiently. * *

If the implementation of this method cannot correctly handle a * particular code point then it should either throw an appropriate runtime * exception or return a suitable replacement character. It must never * silently discard invalid input as this may constitute a security risk. * * @param cp the Unicode code point to escape if necessary * @return the replacement characters, or {@code null} if no escaping was * needed */ protected abstract char[] escape(int cp); /** * Scans a sub-sequence of characters from a given {@link CharSequence}, * returning the index of the next character that requires escaping. * *

Note: When implementing an escaper, it is a good idea to override * this method for efficiency. The base class implementation determines * successive Unicode code points and invokes {@link #escape(int)} for each of * them. If the semantics of your escaper are such that code points in the * supplementary range are either all escaped or all unescaped, this method * can be implemented more efficiently using {@link CharSequence#charAt(int)}. * *

Note however that if your escaper does not escape characters in the * supplementary range, you should either continue to validate the correctness * of any surrogate characters encountered or provide a clear warning to users * that your escaper does not validate its input. * *

See {@link PercentEscaper} for an example. * * @param csq a sequence of characters * @param start the index of the first character to be scanned * @param end the index immediately after the last character to be scanned * @throws IllegalArgumentException if the scanned sub-sequence of {@code csq} * contains invalid surrogate pairs */ protected int nextEscapeIndex(CharSequence csq, int start, int end) { int index = start; while (index < end) { int cp = codePointAt(csq, index, end); if (cp < 0 || escape(cp) != null) { break; } index += Character.isSupplementaryCodePoint(cp) ? 2 : 1; } return index; } /** * Returns the escaped form of a given literal string. * *

If you are escaping input in arbitrary successive chunks, then it is not * generally safe to use this method. If an input string ends with an * unmatched high surrogate character, then this method will throw * {@link IllegalArgumentException}. You should either ensure your input is * valid UTF-16 before * calling this method or use an escaped {@link Appendable} (as returned by * {@link #escape(Appendable)}) which can cope with arbitrarily split input. * *

Note: When implementing an escaper it is a good idea to override * this method for efficiency by inlining the implementation of * {@link #nextEscapeIndex(CharSequence, int, int)} directly. Doing this for * {@link PercentEscaper} more than doubled the performance for unescaped * strings (as measured by {@link CharEscapersBenchmark}). * * @param string the literal string to be escaped * @return the escaped form of {@code string} * @throws NullPointerException if {@code string} is null * @throws IllegalArgumentException if invalid surrogate characters are * encountered */ public String escape(String string) { int end = string.length(); int index = nextEscapeIndex(string, 0, end); return index == end ? string : escapeSlow(string, index); } /** * Returns the escaped form of a given literal string, starting at the given * index. This method is called by the {@link #escape(String)} method when it * discovers that escaping is required. It is protected to allow subclasses * to override the fastpath escaping function to inline their escaping test. * See {@link CharEscaperBuilder} for an example usage. * *

This method is not reentrant and may only be invoked by the top level * {@link #escape(String)} method. * * @param s the literal string to be escaped * @param index the index to start escaping from * @return the escaped form of {@code string} * @throws NullPointerException if {@code string} is null * @throws IllegalArgumentException if invalid surrogate characters are * encountered */ protected final String escapeSlow(String s, int index) { int end = s.length(); // Get a destination buffer and setup some loop variables. char[] dest = DEST_TL.get(); int destIndex = 0; int unescapedChunkStart = 0; while (index < end) { int cp = codePointAt(s, index, end); if (cp < 0) { throw new IllegalArgumentException( "Trailing high surrogate at end of input"); } char[] escaped = escape(cp); if (escaped != null) { int charsSkipped = index - unescapedChunkStart; // This is the size needed to add the replacement, not the full // size needed by the string. We only regrow when we absolutely must. int sizeNeeded = destIndex + charsSkipped + escaped.length; if (dest.length < sizeNeeded) { int destLength = sizeNeeded + (end - index) + DEST_PAD; dest = growBuffer(dest, destIndex, destLength); } // If we have skipped any characters, we need to copy them now. if (charsSkipped > 0) { s.getChars(unescapedChunkStart, index, dest, destIndex); destIndex += charsSkipped; } if (escaped.length > 0) { System.arraycopy(escaped, 0, dest, destIndex, escaped.length); destIndex += escaped.length; } } unescapedChunkStart = index + (Character.isSupplementaryCodePoint(cp) ? 2 : 1); index = nextEscapeIndex(s, unescapedChunkStart, end); } // Process trailing unescaped characters - no need to account for escaped // length or padding the allocation. int charsSkipped = end - unescapedChunkStart; if (charsSkipped > 0) { int endIndex = destIndex + charsSkipped; if (dest.length < endIndex) { dest = growBuffer(dest, destIndex, endIndex); } s.getChars(unescapedChunkStart, end, dest, destIndex); destIndex = endIndex; } return new String(dest, 0, destIndex); } /** * Returns an {@code Appendable} instance which automatically escapes all * text appended to it before passing the resulting text to an underlying * {@code Appendable}. * *

Unlike {@link #escape(String)} it is permitted to append arbitrarily * split input to this Appendable, including input that is split over a * surrogate pair. In this case the pending high surrogate character will not * be processed until the corresponding low surrogate is appended. This means * that a trailing high surrogate character at the end of the input cannot be * detected and will be silently ignored. This is unavoidable since the * Appendable interface has no {@code close()} method, and it is impossible to * determine when the last characters have been appended. * *

The methods of the returned object will propagate any exceptions thrown * by the underlying {@code Appendable}. * *

For well formed UTF-16 * the escaping behavior is identical to that of {@link #escape(String)} and * the following code is equivalent to (but much slower than) * {@code escaper.escape(string)}:

{@code
   *
   *   StringBuilder sb = new StringBuilder();
   *   escaper.escape(sb).append(string);
   *   return sb.toString();}
* * @param out the underlying {@code Appendable} to append escaped output to * @return an {@code Appendable} which passes text to {@code out} after * escaping it * @throws NullPointerException if {@code out} is null * @throws IllegalArgumentException if invalid surrogate characters are * encountered * */ public Appendable escape(final Appendable out) { checkNotNull(out); return new Appendable() { int pendingHighSurrogate = -1; char[] decodedChars = new char[2]; public Appendable append(CharSequence csq) throws IOException { return append(csq, 0, csq.length()); } public Appendable append(CharSequence csq, int start, int end) throws IOException { int index = start; if (index < end) { // This is a little subtle: index must never reference the middle of a // surrogate pair but unescapedChunkStart can. The first time we enter // the loop below it is possible that index != unescapedChunkStart. int unescapedChunkStart = index; if (pendingHighSurrogate != -1) { // Our last append operation ended halfway through a surrogate pair // so we have to do some extra work first. char c = csq.charAt(index++); if (!Character.isLowSurrogate(c)) { throw new IllegalArgumentException( "Expected low surrogate character but got " + c); } char[] escaped = escape(Character.toCodePoint((char) pendingHighSurrogate, c)); if (escaped != null) { // Emit the escaped character and adjust unescapedChunkStart to // skip the low surrogate we have consumed. outputChars(escaped, escaped.length); unescapedChunkStart += 1; } else { // Emit pending high surrogate (unescaped) but do not modify // unescapedChunkStart as we must still emit the low surrogate. out.append((char) pendingHighSurrogate); } pendingHighSurrogate = -1; } while (true) { // Find and append the next subsequence of unescaped characters. index = nextEscapeIndex(csq, index, end); if (index > unescapedChunkStart) { out.append(csq, unescapedChunkStart, index); } if (index == end) { break; } // If we are not finished, calculate the next code point. int cp = codePointAt(csq, index, end); if (cp < 0) { // Our sequence ended half way through a surrogate pair so just // record the state and exit. pendingHighSurrogate = -cp; break; } // Escape the code point and output the characters. char[] escaped = escape(cp); if (escaped != null) { outputChars(escaped, escaped.length); } else { // This shouldn't really happen if nextEscapeIndex is correct but // we should cope with false positives. int len = Character.toChars(cp, decodedChars, 0); outputChars(decodedChars, len); } // Update our index past the escaped character and continue. index += (Character.isSupplementaryCodePoint(cp) ? 2 : 1); unescapedChunkStart = index; } } return this; } public Appendable append(char c) throws IOException { if (pendingHighSurrogate != -1) { // Our last append operation ended halfway through a surrogate pair // so we have to do some extra work first. if (!Character.isLowSurrogate(c)) { throw new IllegalArgumentException( "Expected low surrogate character but got '" + c + "' with value " + (int) c); } char[] escaped = escape(Character.toCodePoint((char) pendingHighSurrogate, c)); if (escaped != null) { outputChars(escaped, escaped.length); } else { out.append((char) pendingHighSurrogate); out.append(c); } pendingHighSurrogate = -1; } else if (Character.isHighSurrogate(c)) { // This is the start of a (split) surrogate pair. pendingHighSurrogate = c; } else { if (Character.isLowSurrogate(c)) { throw new IllegalArgumentException( "Unexpected low surrogate character '" + c + "' with value " + (int) c); } // This is a normal (non surrogate) char. char[] escaped = escape(c); if (escaped != null) { outputChars(escaped, escaped.length); } else { out.append(c); } } return this; } private void outputChars(char[] chars, int len) throws IOException { for (int n = 0; n < len; n++) { out.append(chars[n]); } } }; } /** * Returns the Unicode code point of the character at the given index. * *

Unlike {@link Character#codePointAt(CharSequence, int)} or * {@link String#codePointAt(int)} this method will never fail silently when * encountering an invalid surrogate pair. * *

The behaviour of this method is as follows: *

    *
  1. If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown. *
  2. If the character at the specified index is not a surrogate, it is * returned. *
  3. If the first character was a high surrogate value, then an attempt is * made to read the next character. *
      *
    1. If the end of the sequence was reached, the negated value of * the trailing high surrogate is returned. *
    2. If the next character was a valid low surrogate, the code point * value of the high/low surrogate pair is returned. *
    3. If the next character was not a low surrogate value, then * {@link IllegalArgumentException} is thrown. *
    *
  4. If the first character was a low surrogate value, * {@link IllegalArgumentException} is thrown. *
* * @param seq the sequence of characters from which to decode the code point * @param index the index of the first character to decode * @param end the index beyond the last valid character to decode * @return the Unicode code point for the given index or the negated value of * the trailing high surrogate character at the end of the sequence */ protected static final int codePointAt(CharSequence seq, int index, int end) { if (index < end) { char c1 = seq.charAt(index++); if (c1 < Character.MIN_HIGH_SURROGATE || c1 > Character.MAX_LOW_SURROGATE) { // Fast path (first test is probably all we need to do) return c1; } else if (c1 <= Character.MAX_HIGH_SURROGATE) { // If the high surrogate was the last character, return its inverse if (index == end) { return -c1; } // Otherwise look for the low surrogate following it char c2 = seq.charAt(index); if (Character.isLowSurrogate(c2)) { return Character.toCodePoint(c1, c2); } throw new IllegalArgumentException( "Expected low surrogate but got char '" + c2 + "' with value " + (int) c2 + " at index " + index); } else { throw new IllegalArgumentException( "Unexpected low surrogate character '" + c1 + "' with value " + (int) c1 + " at index " + (index - 1)); } } throw new IndexOutOfBoundsException("Index exceeds specified range"); } /** * Helper method to grow the character buffer as needed, this only happens * once in a while so it's ok if it's in a method call. If the index passed * in is 0 then no copying will be done. */ private static final char[] growBuffer(char[] dest, int index, int size) { char[] copy = new char[size]; if (index > 0) { System.arraycopy(dest, 0, copy, 0, index); } return copy; } /** * A thread-local destination buffer to keep us from creating new buffers. * The starting size is 1024 characters. If we grow past this we don't * put it back in the threadlocal, we just keep going and grow as needed. */ private static final ThreadLocal DEST_TL = new ThreadLocal() { @Override protected char[] initialValue() { return new char[1024]; } }; } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/000077500000000000000000000000001172235640600240135ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/000077500000000000000000000000001172235640600256615ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/AbstractOAuthConsumer.java000066400000000000000000000220771172235640600327540ustar00rootroot00000000000000/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost; import java.io.IOException; import java.io.InputStream; import java.util.Random; import oauth.signpost.basic.UrlStringRequestAdapter; import oauth.signpost.exception.OAuthCommunicationException; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; import oauth.signpost.signature.AuthorizationHeaderSigningStrategy; import oauth.signpost.signature.HmacSha1MessageSigner; import oauth.signpost.signature.OAuthMessageSigner; import oauth.signpost.signature.QueryStringSigningStrategy; import oauth.signpost.signature.SigningStrategy; /** * ABC for consumer implementations. If you're developing a custom consumer you * will probably inherit from this class to save you a lot of work. * * @author Matthias Kaeppler */ public abstract class AbstractOAuthConsumer implements OAuthConsumer { private static final long serialVersionUID = 1L; private String consumerKey, consumerSecret; private String token; private OAuthMessageSigner messageSigner; private SigningStrategy signingStrategy; // these are params that may be passed to the consumer directly (i.e. // without going through the request object) private HttpParameters additionalParameters; // these are the params which will be passed to the message signer private HttpParameters requestParameters; private boolean sendEmptyTokens; final private Random random = new Random(System.nanoTime()); public AbstractOAuthConsumer(String consumerKey, String consumerSecret) { this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; setMessageSigner(new HmacSha1MessageSigner()); setSigningStrategy(new AuthorizationHeaderSigningStrategy()); } public void setMessageSigner(OAuthMessageSigner messageSigner) { this.messageSigner = messageSigner; messageSigner.setConsumerSecret(consumerSecret); } public void setSigningStrategy(SigningStrategy signingStrategy) { this.signingStrategy = signingStrategy; } public void setAdditionalParameters(HttpParameters additionalParameters) { this.additionalParameters = additionalParameters; } public synchronized HttpRequest sign(HttpRequest request) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException { if (consumerKey == null) { throw new OAuthExpectationFailedException("consumer key not set"); } if (consumerSecret == null) { throw new OAuthExpectationFailedException("consumer secret not set"); } requestParameters = new HttpParameters(); try { if (additionalParameters != null) { requestParameters.putAll(additionalParameters, false); } collectHeaderParameters(request, requestParameters); collectQueryParameters(request, requestParameters); collectBodyParameters(request, requestParameters); // add any OAuth params that haven't already been set completeOAuthParameters(requestParameters); requestParameters.remove(OAuth.OAUTH_SIGNATURE); } catch (IOException e) { throw new OAuthCommunicationException(e); } String signature = messageSigner.sign(request, requestParameters); OAuth.debugOut("signature", signature); signingStrategy.writeSignature(signature, request, requestParameters); OAuth.debugOut("Request URL", request.getRequestUrl()); return request; } public synchronized HttpRequest sign(Object request) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException { return sign(wrap(request)); } public synchronized String sign(String url) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException { HttpRequest request = new UrlStringRequestAdapter(url); // switch to URL signing SigningStrategy oldStrategy = this.signingStrategy; this.signingStrategy = new QueryStringSigningStrategy(); sign(request); // revert to old strategy this.signingStrategy = oldStrategy; return request.getRequestUrl(); } /** * Adapts the given request object to a Signpost {@link HttpRequest}. How * this is done depends on the consumer implementation. * * @param request * the native HTTP request instance * @return the adapted request */ protected abstract HttpRequest wrap(Object request); public void setTokenWithSecret(String token, String tokenSecret) { this.token = token; messageSigner.setTokenSecret(tokenSecret); } public String getToken() { return token; } public String getTokenSecret() { return messageSigner.getTokenSecret(); } public String getConsumerKey() { return this.consumerKey; } public String getConsumerSecret() { return this.consumerSecret; } /** *

* Helper method that adds any OAuth parameters to the given request * parameters which are missing from the current request but required for * signing. A good example is the oauth_nonce parameter, which is typically * not provided by the client in advance. *

*

* It's probably not a very good idea to override this method. If you want * to generate different nonces or timestamps, override * {@link #generateNonce()} or {@link #generateTimestamp()} instead. *

* * @param out * the request parameter which should be completed */ protected void completeOAuthParameters(HttpParameters out) { if (!out.containsKey(OAuth.OAUTH_CONSUMER_KEY)) { out.put(OAuth.OAUTH_CONSUMER_KEY, consumerKey, true); } if (!out.containsKey(OAuth.OAUTH_SIGNATURE_METHOD)) { out.put(OAuth.OAUTH_SIGNATURE_METHOD, messageSigner.getSignatureMethod(), true); } if (!out.containsKey(OAuth.OAUTH_TIMESTAMP)) { out.put(OAuth.OAUTH_TIMESTAMP, generateTimestamp(), true); } if (!out.containsKey(OAuth.OAUTH_NONCE)) { out.put(OAuth.OAUTH_NONCE, generateNonce(), true); } if (!out.containsKey(OAuth.OAUTH_VERSION)) { out.put(OAuth.OAUTH_VERSION, OAuth.VERSION_1_0, true); } if (!out.containsKey(OAuth.OAUTH_TOKEN)) { if (token != null && !token.equals("") || sendEmptyTokens) { out.put(OAuth.OAUTH_TOKEN, token, true); } } } public HttpParameters getRequestParameters() { return requestParameters; } public void setSendEmptyTokens(boolean enable) { this.sendEmptyTokens = enable; } /** * Collects OAuth Authorization header parameters as per OAuth Core 1.0 spec * section 9.1.1 */ protected void collectHeaderParameters(HttpRequest request, HttpParameters out) { HttpParameters headerParams = OAuth.oauthHeaderToParamsMap(request.getHeader(OAuth.HTTP_AUTHORIZATION_HEADER)); out.putAll(headerParams, false); } /** * Collects x-www-form-urlencoded body parameters as per OAuth Core 1.0 spec * section 9.1.1 */ protected void collectBodyParameters(HttpRequest request, HttpParameters out) throws IOException { // collect x-www-form-urlencoded body params String contentType = request.getContentType(); if (contentType != null && contentType.startsWith(OAuth.FORM_ENCODED)) { InputStream payload = request.getMessagePayload(); out.putAll(OAuth.decodeForm(payload), true); } } /** * Collects HTTP GET query string parameters as per OAuth Core 1.0 spec * section 9.1.1 */ protected void collectQueryParameters(HttpRequest request, HttpParameters out) { String url = request.getRequestUrl(); int q = url.indexOf('?'); if (q >= 0) { // Combine the URL query string with the other parameters: out.putAll(OAuth.decodeForm(url.substring(q + 1)), true); } } protected String generateTimestamp() { return Long.toString(System.currentTimeMillis() / 1000L); } protected String generateNonce() { return Long.toString(random.nextLong()); } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/AbstractOAuthProvider.java000066400000000000000000000313001172235640600327400ustar00rootroot00000000000000/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import oauth.signpost.exception.OAuthCommunicationException; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.exception.OAuthNotAuthorizedException; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpResponse; /** * ABC for all provider implementations. If you're writing a custom provider, * you will probably inherit from this class, since it takes a lot of work from * you. * * @author Matthias Kaeppler */ public abstract class AbstractOAuthProvider implements OAuthProvider { private static final long serialVersionUID = 1L; private String requestTokenEndpointUrl; private String accessTokenEndpointUrl; private String authorizationWebsiteUrl; private HttpParameters responseParameters; private Map defaultHeaders; private boolean isOAuth10a; private transient OAuthProviderListener listener; public AbstractOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl, String authorizationWebsiteUrl) { this.requestTokenEndpointUrl = requestTokenEndpointUrl; this.accessTokenEndpointUrl = accessTokenEndpointUrl; this.authorizationWebsiteUrl = authorizationWebsiteUrl; this.responseParameters = new HttpParameters(); this.defaultHeaders = new HashMap(); } public synchronized String retrieveRequestToken(OAuthConsumer consumer, String callbackUrl, String... customOAuthParams) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException { // invalidate current credentials, if any consumer.setTokenWithSecret(null, null); // 1.0a expects the callback to be sent while getting the request token. // 1.0 service providers would simply ignore this parameter. HttpParameters params = new HttpParameters(); params.putAll(customOAuthParams, true); params.put(OAuth.OAUTH_CALLBACK, callbackUrl, true); retrieveToken(consumer, requestTokenEndpointUrl, params); String callbackConfirmed = responseParameters.getFirst(OAuth.OAUTH_CALLBACK_CONFIRMED); responseParameters.remove(OAuth.OAUTH_CALLBACK_CONFIRMED); isOAuth10a = Boolean.TRUE.toString().equals(callbackConfirmed); // 1.0 service providers expect the callback as part of the auth URL, // Do not send when 1.0a. if (isOAuth10a) { return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN, consumer.getToken()); } else { return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN, consumer.getToken(), OAuth.OAUTH_CALLBACK, callbackUrl); } } public synchronized void retrieveAccessToken(OAuthConsumer consumer, String oauthVerifier, String... customOAuthParams) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException { if (consumer.getToken() == null || consumer.getTokenSecret() == null) { throw new OAuthExpectationFailedException( "Authorized request token or token secret not set. " + "Did you retrieve an authorized request token before?"); } HttpParameters params = new HttpParameters(); params.putAll(customOAuthParams, true); if (isOAuth10a && oauthVerifier != null) { params.put(OAuth.OAUTH_VERIFIER, oauthVerifier, true); } retrieveToken(consumer, accessTokenEndpointUrl, params); } /** *

* Implemented by subclasses. The responsibility of this method is to * contact the service provider at the given endpoint URL and fetch a * request or access token. What kind of token is retrieved solely depends * on the URL being used. *

*

* Correct implementations of this method must guarantee the following * post-conditions: *

    *
  • the {@link OAuthConsumer} passed to this method must have a valid * {@link OAuth#OAUTH_TOKEN} and {@link OAuth#OAUTH_TOKEN_SECRET} set by * calling {@link OAuthConsumer#setTokenWithSecret(String, String)}
  • *
  • {@link #getResponseParameters()} must return the set of query * parameters served by the service provider in the token response, with all * OAuth specific parameters being removed
  • *
*

* * @param consumer * the {@link OAuthConsumer} that should be used to sign the request * @param endpointUrl * the URL at which the service provider serves the OAuth token that * is to be fetched * @param customOAuthParams * you can pass custom OAuth parameters here (such as oauth_callback * or oauth_verifier) which will go directly into the signer, i.e. * you don't have to put them into the request first. * @throws OAuthMessageSignerException * if signing the token request fails * @throws OAuthCommunicationException * if a network communication error occurs * @throws OAuthNotAuthorizedException * if the server replies 401 - Unauthorized * @throws OAuthExpectationFailedException * if an expectation has failed, e.g. because the server didn't * reply in the expected format */ protected void retrieveToken(OAuthConsumer consumer, String endpointUrl, HttpParameters customOAuthParams) throws OAuthMessageSignerException, OAuthCommunicationException, OAuthNotAuthorizedException, OAuthExpectationFailedException { Map defaultHeaders = getRequestHeaders(); if (consumer.getConsumerKey() == null || consumer.getConsumerSecret() == null) { throw new OAuthExpectationFailedException("Consumer key or secret not set"); } HttpRequest request = null; HttpResponse response = null; try { request = createRequest(endpointUrl); for (String header : defaultHeaders.keySet()) { request.setHeader(header, defaultHeaders.get(header)); } if (customOAuthParams != null && !customOAuthParams.isEmpty()) { consumer.setAdditionalParameters(customOAuthParams); } if (this.listener != null) { this.listener.prepareRequest(request); } consumer.sign(request); if (this.listener != null) { this.listener.prepareSubmission(request); } response = sendRequest(request); int statusCode = response.getStatusCode(); boolean requestHandled = false; if (this.listener != null) { requestHandled = this.listener.onResponseReceived(request, response); } if (requestHandled) { return; } if (statusCode >= 300) { handleUnexpectedResponse(statusCode, response); } HttpParameters responseParams = OAuth.decodeForm(response.getContent()); String token = responseParams.getFirst(OAuth.OAUTH_TOKEN); String secret = responseParams.getFirst(OAuth.OAUTH_TOKEN_SECRET); responseParams.remove(OAuth.OAUTH_TOKEN); responseParams.remove(OAuth.OAUTH_TOKEN_SECRET); setResponseParameters(responseParams); if (token == null || secret == null) { throw new OAuthExpectationFailedException( "Request token or token secret not set in server reply. " + "The service provider you use is probably buggy."); } consumer.setTokenWithSecret(token, secret); } catch (OAuthNotAuthorizedException e) { throw e; } catch (OAuthExpectationFailedException e) { throw e; } catch (Exception e) { throw new OAuthCommunicationException(e); } finally { try { closeConnection(request, response); } catch (Exception e) { throw new OAuthCommunicationException(e); } } } protected void handleUnexpectedResponse(int statusCode, HttpResponse response) throws Exception { if (response == null) { return; } BufferedReader reader = new BufferedReader(new InputStreamReader(response.getContent())); StringBuilder responseBody = new StringBuilder(); String line = reader.readLine(); while (line != null) { responseBody.append(line); line = reader.readLine(); } switch (statusCode) { case 401: throw new OAuthNotAuthorizedException(responseBody.toString()); default: throw new OAuthCommunicationException("Service provider responded in error: " + statusCode + " (" + response.getReasonPhrase() + ")", responseBody.toString()); } } /** * Overrride this method if you want to customize the logic for building a * request object for the given endpoint URL. * * @param endpointUrl * the URL to which the request will go * @return the request object * @throws Exception * if something breaks */ protected abstract HttpRequest createRequest(String endpointUrl) throws Exception; /** * Override this method if you want to customize the logic for how the given * request is sent to the server. * * @param request * the request to send * @return the response to the request * @throws Exception * if something breaks */ protected abstract HttpResponse sendRequest(HttpRequest request) throws Exception; /** * Called when the connection is being finalized after receiving the * response. Use this to do any cleanup / resource freeing. * * @param request * the request that has been sent * @param response * the response that has been received * @throws Exception * if something breaks */ protected void closeConnection(HttpRequest request, HttpResponse response) throws Exception { // NOP } public HttpParameters getResponseParameters() { return responseParameters; } /** * Returns a single query parameter as served by the service provider in a * token reply. You must call {@link #setResponseParameters} with the set of * parameters before using this method. * * @param key * the parameter name * @return the parameter value */ protected String getResponseParameter(String key) { return responseParameters.getFirst(key); } public void setResponseParameters(HttpParameters parameters) { this.responseParameters = parameters; } public void setOAuth10a(boolean isOAuth10aProvider) { this.isOAuth10a = isOAuth10aProvider; } public boolean isOAuth10a() { return isOAuth10a; } public String getRequestTokenEndpointUrl() { return this.requestTokenEndpointUrl; } public String getAccessTokenEndpointUrl() { return this.accessTokenEndpointUrl; } public String getAuthorizationWebsiteUrl() { return this.authorizationWebsiteUrl; } public void setRequestHeader(String header, String value) { defaultHeaders.put(header, value); } public Map getRequestHeaders() { return defaultHeaders; } public void setListener(OAuthProviderListener listener) { this.listener = listener; } public void removeListener(OAuthProviderListener listener) { this.listener = null; } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/OAuth.java000066400000000000000000000255301172235640600275510ustar00rootroot00000000000000/* Copyright (c) 2008, 2009 Netflix, Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URLDecoder; import java.util.Collection; import java.util.HashMap; import java.util.Map; import oauth.signpost.http.HttpParameters; import com.google.gdata.util.common.base.PercentEscaper; public class OAuth { public static final String VERSION_1_0 = "1.0"; public static final String ENCODING = "UTF-8"; public static final String FORM_ENCODED = "application/x-www-form-urlencoded"; public static final String HTTP_AUTHORIZATION_HEADER = "Authorization"; public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key"; public static final String OAUTH_TOKEN = "oauth_token"; public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret"; public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method"; public static final String OAUTH_SIGNATURE = "oauth_signature"; public static final String OAUTH_TIMESTAMP = "oauth_timestamp"; public static final String OAUTH_NONCE = "oauth_nonce"; public static final String OAUTH_VERSION = "oauth_version"; public static final String OAUTH_CALLBACK = "oauth_callback"; public static final String OAUTH_CALLBACK_CONFIRMED = "oauth_callback_confirmed"; public static final String OAUTH_VERIFIER = "oauth_verifier"; /** * Pass this value as the callback "url" upon retrieving a request token if * your application cannot receive callbacks (e.g. because it's a desktop * app). This will tell the service provider that verification happens * out-of-band, which basically means that it will generate a PIN code (the * OAuth verifier) and display that to your user. You must obtain this code * from your user and pass it to * {@link OAuthProvider#retrieveAccessToken(OAuthConsumer, String)} in order * to complete the token handshake. */ public static final String OUT_OF_BAND = "oob"; private static final PercentEscaper percentEncoder = new PercentEscaper( "-._~", false); public static String percentEncode(String s) { if (s == null) { return ""; } return percentEncoder.escape(s); } public static String percentDecode(String s) { try { if (s == null) { return ""; } return URLDecoder.decode(s, ENCODING); // This implements http://oauth.pbwiki.com/FlexibleDecoding } catch (java.io.UnsupportedEncodingException wow) { throw new RuntimeException(wow.getMessage(), wow); } } /** * Construct a x-www-form-urlencoded document containing the given sequence * of name/value pairs. Use OAuth percent encoding (not exactly the encoding * mandated by x-www-form-urlencoded). */ public static > void formEncode(Collection parameters, OutputStream into) throws IOException { if (parameters != null) { boolean first = true; for (Map.Entry entry : parameters) { if (first) { first = false; } else { into.write('&'); } into.write(percentEncode(safeToString(entry.getKey())).getBytes()); into.write('='); into.write(percentEncode(safeToString(entry.getValue())).getBytes()); } } } /** * Construct a x-www-form-urlencoded document containing the given sequence * of name/value pairs. Use OAuth percent encoding (not exactly the encoding * mandated by x-www-form-urlencoded). */ public static > String formEncode(Collection parameters) throws IOException { ByteArrayOutputStream b = new ByteArrayOutputStream(); formEncode(parameters, b); return new String(b.toByteArray()); } /** Parse a form-urlencoded document. */ public static HttpParameters decodeForm(String form) { HttpParameters params = new HttpParameters(); if (isEmpty(form)) { return params; } for (String nvp : form.split("\\&")) { int equals = nvp.indexOf('='); String name; String value; if (equals < 0) { name = percentDecode(nvp); value = null; } else { name = percentDecode(nvp.substring(0, equals)); value = percentDecode(nvp.substring(equals + 1)); } params.put(name, value); } return params; } public static HttpParameters decodeForm(InputStream content) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader( content)); StringBuilder sb = new StringBuilder(); String line = reader.readLine(); while (line != null) { sb.append(line); line = reader.readLine(); } return decodeForm(sb.toString()); } /** * Construct a Map containing a copy of the given parameters. If several * parameters have the same name, the Map will contain the first value, * only. */ public static > Map toMap(Collection from) { HashMap map = new HashMap(); if (from != null) { for (Map.Entry entry : from) { String key = entry.getKey(); if (!map.containsKey(key)) { map.put(key, entry.getValue()); } } } return map; } public static final String safeToString(Object from) { return (from == null) ? null : from.toString(); } public static boolean isEmpty(String str) { return (str == null) || (str.length() == 0); } /** * Appends a list of key/value pairs to the given URL, e.g.: * *
     * String url = OAuth.addQueryParameters("http://example.com?a=1", b, 2, c, 3);
     * 
* * which yields: * *
     * http://example.com?a=1&b=2&c=3
     * 
* * All parameters will be encoded according to OAuth's percent encoding * rules. * * @param url * the URL * @param kvPairs * the list of key/value pairs * @return */ public static String addQueryParameters(String url, String... kvPairs) { String queryDelim = url.contains("?") ? "&" : "?"; StringBuilder sb = new StringBuilder(url + queryDelim); for (int i = 0; i < kvPairs.length; i += 2) { if (i > 0) { sb.append("&"); } sb.append(OAuth.percentEncode(kvPairs[i]) + "=" + OAuth.percentEncode(kvPairs[i + 1])); } return sb.toString(); } public static String addQueryParameters(String url, Map params) { String[] kvPairs = new String[params.size() * 2]; int idx = 0; for (String key : params.keySet()) { kvPairs[idx] = key; kvPairs[idx + 1] = params.get(key); idx += 2; } return addQueryParameters(url, kvPairs); } public static String addQueryString(String url, String queryString) { String queryDelim = url.contains("?") ? "&" : "?"; StringBuilder sb = new StringBuilder(url + queryDelim); sb.append(queryString); return sb.toString(); } /** * Builds an OAuth header from the given list of header fields. All * parameters starting in 'oauth_*' will be percent encoded. * *
     * String authHeader = OAuth.prepareOAuthHeader("realm", "http://example.com", "oauth_token", "x%y");
     * 
* * which yields: * *
     * OAuth realm="http://example.com", oauth_token="x%25y"
     * 
* * @param kvPairs * the list of key/value pairs * @return a string eligible to be used as an OAuth HTTP Authorization * header. */ public static String prepareOAuthHeader(String... kvPairs) { StringBuilder sb = new StringBuilder("OAuth "); for (int i = 0; i < kvPairs.length; i += 2) { if (i > 0) { sb.append(", "); } boolean isOAuthElem = kvPairs[i].startsWith("oauth_") || kvPairs[i].startsWith("x_oauth_"); String value = isOAuthElem ? OAuth.percentEncode(kvPairs[i + 1]) : kvPairs[i + 1]; sb.append(OAuth.percentEncode(kvPairs[i]) + "=\"" + value + "\""); } return sb.toString(); } public static HttpParameters oauthHeaderToParamsMap(String oauthHeader) { HttpParameters params = new HttpParameters(); if (oauthHeader == null || !oauthHeader.startsWith("OAuth ")) { return params; } oauthHeader = oauthHeader.substring("OAuth ".length()); String[] elements = oauthHeader.split(","); for (String keyValuePair : elements) { String[] keyValue = keyValuePair.split("="); params.put(keyValue[0].trim(), keyValue[1].replace("\"", "").trim()); } return params; } /** * Helper method to concatenate a parameter and its value to a pair that can * be used in an HTTP header. This method percent encodes both parts before * joining them. * * @param name * the OAuth parameter name, e.g. oauth_token * @param value * the OAuth parameter value, e.g. 'hello oauth' * @return a name/value pair, e.g. oauth_token="hello%20oauth" */ public static String toHeaderElement(String name, String value) { return OAuth.percentEncode(name) + "=\"" + OAuth.percentEncode(value) + "\""; } public static void debugOut(String key, String value) { if (System.getProperty("debug") != null) { System.out.println("[SIGNPOST] " + key + ": " + value); } } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/OAuthConsumer.java000066400000000000000000000161431172235640600312650ustar00rootroot00000000000000/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost; import java.io.Serializable; import oauth.signpost.exception.OAuthCommunicationException; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; import oauth.signpost.signature.AuthorizationHeaderSigningStrategy; import oauth.signpost.signature.HmacSha1MessageSigner; import oauth.signpost.signature.OAuthMessageSigner; import oauth.signpost.signature.PlainTextMessageSigner; import oauth.signpost.signature.QueryStringSigningStrategy; import oauth.signpost.signature.SigningStrategy; /** *

* Exposes a simple interface to sign HTTP requests using a given OAuth token * and secret. Refer to {@link OAuthProvider} how to retrieve a valid token and * token secret. *

*

* HTTP messages are signed as follows: *

* *

 * // exchange the arguments with the actual token/secret pair
 * OAuthConsumer consumer = new DefaultOAuthConsumer("1234", "5678");
 * 
 * URL url = new URL("http://example.com/protected.xml");
 * HttpURLConnection request = (HttpURLConnection) url.openConnection();
 * 
 * consumer.sign(request);
 * 
 * request.connect();
 * 
* *

*

* * @author Matthias Kaeppler */ public interface OAuthConsumer extends Serializable { /** * Sets the message signer that should be used to generate the OAuth * signature. * * @param messageSigner * the signer * @see HmacSha1MessageSigner * @see PlainTextMessageSigner */ public void setMessageSigner(OAuthMessageSigner messageSigner); /** * Allows you to add parameters (typically OAuth parameters such as * oauth_callback or oauth_verifier) which will go directly into the signer, * i.e. you don't have to put them into the request first. The consumer's * {@link SigningStrategy} will then take care of writing them to the * correct part of the request before it is sent. This is useful if you want * to pre-set custom OAuth parameters. Note that these parameters are * expected to already be percent encoded -- they will be simply merged * as-is. BE CAREFUL WITH THIS METHOD! Your service provider may decide * to ignore any non-standard OAuth params when computing the signature. * * @param additionalParameters * the parameters */ public void setAdditionalParameters(HttpParameters additionalParameters); /** * Defines which strategy should be used to write a signature to an HTTP * request. * * @param signingStrategy * the strategy * @see AuthorizationHeaderSigningStrategy * @see QueryStringSigningStrategy */ public void setSigningStrategy(SigningStrategy signingStrategy); /** *

* Causes the consumer to always include the oauth_token parameter to be * sent, even if blank. If you're seeing 401s during calls to * {@link OAuthProvider#retrieveRequestToken}, try setting this to true. *

* * @param enable * true or false */ public void setSendEmptyTokens(boolean enable); /** * Signs the given HTTP request by writing an OAuth signature (and other * required OAuth parameters) to it. Where these parameters are written * depends on the current {@link SigningStrategy}. * * @param request * the request to sign * @return the request object passed as an argument * @throws OAuthMessageSignerException * @throws OAuthExpectationFailedException * @throws OAuthCommunicationException */ public HttpRequest sign(HttpRequest request) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException; /** *

* Signs the given HTTP request by writing an OAuth signature (and other * required OAuth parameters) to it. Where these parameters are written * depends on the current {@link SigningStrategy}. *

* This method accepts HTTP library specific request objects; the consumer * implementation must ensure that only those request types are passed which * it supports. * * @param request * the request to sign * @return the request object passed as an argument * @throws OAuthMessageSignerException * @throws OAuthExpectationFailedException * @throws OAuthCommunicationException */ public HttpRequest sign(Object request) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException; /** *

* "Signs" the given URL by appending all OAuth parameters to it which are * required for message signing. The assumed HTTP method is GET. * Essentially, this is equivalent to signing an HTTP GET request, but it * can be useful if your application requires clickable links to protected * resources, i.e. when your application does not have access to the actual * request that is being sent. *

* * @param url * the input URL. May have query parameters. * @return the input URL, with all necessary OAuth parameters attached as a * query string. Existing query parameters are preserved. * @throws OAuthMessageSignerException * @throws OAuthExpectationFailedException * @throws OAuthCommunicationException */ public String sign(String url) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException; /** * Sets the OAuth token and token secret used for message signing. * * @param token * the token * @param tokenSecret * the token secret */ public void setTokenWithSecret(String token, String tokenSecret); public String getToken(); public String getTokenSecret(); public String getConsumerKey(); public String getConsumerSecret(); /** * Returns all parameters collected from the HTTP request during message * signing (this means the return value may be NULL before a call to * {@link #sign}), plus all required OAuth parameters that were added * because the request didn't contain them beforehand. In other words, this * is the exact set of parameters that were used for creating the message * signature. * * @return the request parameters used for message signing */ public HttpParameters getRequestParameters(); } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/OAuthProvider.java000066400000000000000000000250061172235640600312620ustar00rootroot00000000000000/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost; import java.io.Serializable; import java.util.Map; import oauth.signpost.basic.DefaultOAuthConsumer; import oauth.signpost.basic.DefaultOAuthProvider; import oauth.signpost.exception.OAuthCommunicationException; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.exception.OAuthNotAuthorizedException; import oauth.signpost.http.HttpParameters; /** *

* Supplies an interface that can be used to retrieve request and access tokens * from an OAuth 1.0(a) service provider. A provider object requires an * {@link OAuthConsumer} to sign the token request message; after a token has * been retrieved, the consumer is automatically updated with the token and the * corresponding secret. *

*

* To initiate the token exchange, create a new provider instance and configure * it with the URLs the service provider exposes for requesting tokens and * resource authorization, e.g.: *

* *
 * OAuthProvider provider = new DefaultOAuthProvider("http://twitter.com/oauth/request_token",
 *         "http://twitter.com/oauth/access_token", "http://twitter.com/oauth/authorize");
 * 
*

* Depending on the HTTP library you use, you may need a different provider * type, refer to the website documentation for how to do that. *

*

* To receive a request token which the user must authorize, you invoke it using * a consumer instance and a callback URL: *

*

* *

 * String url = provider.retrieveRequestToken(consumer, "http://www.example.com/callback");
 * 
* *

*

* That url must be opened in a Web browser, where the user can grant access to * the resources in question. If that succeeds, the service provider will * redirect to the callback URL and append the blessed request token. *

*

* That token must now be exchanged for an access token, as such: *

*

* *

 * provider.retrieveAccessToken(consumer, nullOrVerifierCode);
 * 
* *

*

* where nullOrVerifierCode is either null if your provided a callback URL in * the previous step, or the pin code issued by the service provider to the user * if the request was out-of-band (cf. {@link OAuth#OUT_OF_BAND}. *

*

* The consumer used during token handshakes is now ready for signing. *

* * @see DefaultOAuthProvider * @see DefaultOAuthConsumer * @see OAuthProviderListener */ public interface OAuthProvider extends Serializable { /** * Queries the service provider for a request token. *

* Pre-conditions: the given {@link OAuthConsumer} must have a valid * consumer key and consumer secret already set. *

*

* Post-conditions: the given {@link OAuthConsumer} will have an * unauthorized request token and token secret set. *

* * @param consumer * the {@link OAuthConsumer} that should be used to sign the request * @param callbackUrl * Pass an actual URL if your app can receive callbacks and you want * to get informed about the result of the authorization process. * Pass {@link OAuth.OUT_OF_BAND} if the service provider implements * OAuth 1.0a and your app cannot receive callbacks. Pass null if the * service provider implements OAuth 1.0 and your app cannot receive * callbacks. Please note that some services (among them Twitter) * will fail authorization if you pass a callback URL but register * your application as a desktop app (which would only be able to * handle OOB requests). * @param customOAuthParams * you can pass custom OAuth parameters here which will go directly * into the signer, i.e. you don't have to put them into the request * first. This is useful for pre-setting OAuth params for signing. * Pass them sequentially in key/value order. * @return The URL to which the user must be sent in order to authorize the * consumer. It includes the unauthorized request token (and in the * case of OAuth 1.0, the callback URL -- 1.0a clients send along * with the token request). * @throws OAuthMessageSignerException * if signing the request failed * @throws OAuthNotAuthorizedException * if the service provider rejected the consumer * @throws OAuthExpectationFailedException * if required parameters were not correctly set by the consumer or * service provider * @throws OAuthCommunicationException * if server communication failed */ public String retrieveRequestToken(OAuthConsumer consumer, String callbackUrl, String... customOAuthParams) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException; /** * Queries the service provider for an access token. *

* Pre-conditions: the given {@link OAuthConsumer} must have a valid * consumer key, consumer secret, authorized request token and token secret * already set. *

*

* Post-conditions: the given {@link OAuthConsumer} will have an * access token and token secret set. *

* * @param consumer * the {@link OAuthConsumer} that should be used to sign the request * @param oauthVerifier * NOTE: Only applies to service providers implementing OAuth * 1.0a. Set to null if the service provider is still using OAuth * 1.0. The verification code issued by the service provider * after the the user has granted the consumer authorization. If the * callback method provided in the previous step was * {@link OAuth.OUT_OF_BAND}, then you must ask the user for this * value. If your app has received a callback, the verfication code * was passed as part of that request instead. * @param customOAuthParams * you can pass custom OAuth parameters here which will go directly * into the signer, i.e. you don't have to put them into the request * first. This is useful for pre-setting OAuth params for signing. * Pass them sequentially in key/value order. * @throws OAuthMessageSignerException * if signing the request failed * @throws OAuthNotAuthorizedException * if the service provider rejected the consumer * @throws OAuthExpectationFailedException * if required parameters were not correctly set by the consumer or * service provider * @throws OAuthCommunicationException * if server communication failed */ public void retrieveAccessToken(OAuthConsumer consumer, String oauthVerifier, String... customOAuthParams) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException; /** * Any additional non-OAuth parameters returned in the response body of a * token request can be obtained through this method. These parameters will * be preserved until the next token request is issued. The return value is * never null. */ public HttpParameters getResponseParameters(); /** * Subclasses must use this setter to preserve any non-OAuth query * parameters contained in the server response. It's the caller's * responsibility that any OAuth parameters be removed beforehand. * * @param parameters * the map of query parameters served by the service provider in the * token response */ public void setResponseParameters(HttpParameters parameters); /** * Use this method to set custom HTTP headers to be used for the requests * which are sent to retrieve tokens. @deprecated THIS METHOD HAS BEEN * DEPRECATED. Use {@link OAuthProviderListener} to customize requests. * * @param header * The header name (e.g. 'WWW-Authenticate') * @param value * The header value (e.g. 'realm=www.example.com') */ @Deprecated public void setRequestHeader(String header, String value); /** * @deprecated THIS METHOD HAS BEEN DEPRECATED. Use * {@link OAuthProviderListener} to customize requests. * @return all request headers set via {@link #setRequestHeader} */ @Deprecated public Map getRequestHeaders(); /** * @param isOAuth10aProvider * set to true if the service provider supports OAuth 1.0a. Note that * you need only call this method if you reconstruct a provider * object in between calls to retrieveRequestToken() and * retrieveAccessToken() (i.e. if the object state isn't preserved). * If instead those two methods are called on the same provider * instance, this flag will be deducted automatically based on the * server response during retrieveRequestToken(), so you can simply * ignore this method. */ public void setOAuth10a(boolean isOAuth10aProvider); /** * @return true if the service provider supports OAuth 1.0a. Note that the * value returned here is only meaningful after you have already * performed the token handshake, otherwise there is no way to * determine what version of the OAuth protocol the service provider * implements. */ public boolean isOAuth10a(); public String getRequestTokenEndpointUrl(); public String getAccessTokenEndpointUrl(); public String getAuthorizationWebsiteUrl(); public void setListener(OAuthProviderListener listener); public void removeListener(OAuthProviderListener listener); } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/OAuthProviderListener.java000066400000000000000000000027211172235640600327670ustar00rootroot00000000000000package oauth.signpost; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpResponse; /** * Provides hooks into the token request handling procedure executed by * {@link OAuthProvider}. * * @author Matthias Kaeppler */ public interface OAuthProviderListener { /** * Called after the request has been created and default headers added, but * before the request has been signed. * * @param request * the request to be sent * @throws Exception */ void prepareRequest(HttpRequest request) throws Exception; /** * Called after the request has been signed, but before it's being sent. * * @param request * the request to be sent * @throws Exception */ void prepareSubmission(HttpRequest request) throws Exception; /** * Called when the server response has been received. You can implement this * to manually handle the response data. * * @param request * the request that was sent * @param response * the response that was received * @return returning true means you have handled the response, and the * provider will return immediately. Return false to let the event * propagate and let the provider execute its default response * handling. * @throws Exception */ boolean onResponseReceived(HttpRequest request, HttpResponse response) throws Exception; } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basic/000077500000000000000000000000001172235640600267425ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basic/DefaultOAuthConsumer.java000066400000000000000000000030001172235640600336370ustar00rootroot00000000000000/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.basic; import java.net.HttpURLConnection; import oauth.signpost.AbstractOAuthConsumer; import oauth.signpost.http.HttpRequest; /** * The default implementation for an OAuth consumer. Only supports signing * {@link java.net.HttpURLConnection} type requests. * * @author Matthias Kaeppler */ public class DefaultOAuthConsumer extends AbstractOAuthConsumer { private static final long serialVersionUID = 1L; public DefaultOAuthConsumer(String consumerKey, String consumerSecret) { super(consumerKey, consumerSecret); } @Override protected HttpRequest wrap(Object request) { if (!(request instanceof HttpURLConnection)) { throw new IllegalArgumentException( "The default consumer expects requests of type java.net.HttpURLConnection"); } return new HttpURLConnectionRequestAdapter((HttpURLConnection) request); } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basic/DefaultOAuthProvider.java000066400000000000000000000044761172235640600336600ustar00rootroot00000000000000/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost.basic; import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import oauth.signpost.AbstractOAuthProvider; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpResponse; /** * This default implementation uses {@link java.net.HttpURLConnection} type GET * requests to receive tokens from a service provider. * * @author Matthias Kaeppler */ public class DefaultOAuthProvider extends AbstractOAuthProvider { private static final long serialVersionUID = 1L; public DefaultOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl, String authorizationWebsiteUrl) { super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl); } protected HttpRequest createRequest(String endpointUrl) throws MalformedURLException, IOException { HttpURLConnection connection = (HttpURLConnection) new URL(endpointUrl).openConnection(); connection.setRequestMethod("POST"); connection.setAllowUserInteraction(false); connection.setRequestProperty("Content-Length", "0"); return new HttpURLConnectionRequestAdapter(connection); } protected HttpResponse sendRequest(HttpRequest request) throws IOException { HttpURLConnection connection = (HttpURLConnection) request.unwrap(); connection.connect(); return new HttpURLConnectionResponseAdapter(connection); } @Override protected void closeConnection(HttpRequest request, HttpResponse response) { HttpURLConnection connection = (HttpURLConnection) request.unwrap(); if (connection != null) { connection.disconnect(); } } } HttpURLConnectionRequestAdapter.java000066400000000000000000000032211172235640600357200ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basicpackage oauth.signpost.basic; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.util.HashMap; import java.util.List; import java.util.Map; import oauth.signpost.http.HttpRequest; public class HttpURLConnectionRequestAdapter implements HttpRequest { protected HttpURLConnection connection; public HttpURLConnectionRequestAdapter(HttpURLConnection connection) { this.connection = connection; } public String getMethod() { return connection.getRequestMethod(); } public String getRequestUrl() { return connection.getURL().toExternalForm(); } public void setRequestUrl(String url) { // can't do } public void setHeader(String name, String value) { connection.setRequestProperty(name, value); } public String getHeader(String name) { return connection.getRequestProperty(name); } public Map getAllHeaders() { Map> origHeaders = connection.getRequestProperties(); Map headers = new HashMap(origHeaders.size()); for (String name : origHeaders.keySet()) { List values = origHeaders.get(name); if (!values.isEmpty()) { headers.put(name, values.get(0)); } } return headers; } public InputStream getMessagePayload() throws IOException { return null; } public String getContentType() { return connection.getRequestProperty("Content-Type"); } public HttpURLConnection unwrap() { return connection; } } HttpURLConnectionResponseAdapter.java000066400000000000000000000016021172235640600360670ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basicpackage oauth.signpost.basic; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import oauth.signpost.http.HttpResponse; public class HttpURLConnectionResponseAdapter implements HttpResponse { private HttpURLConnection connection; public HttpURLConnectionResponseAdapter(HttpURLConnection connection) { this.connection = connection; } public InputStream getContent() throws IOException { try { return connection.getInputStream(); } catch (IOException e) { return connection.getErrorStream(); } } public int getStatusCode() throws IOException { return connection.getResponseCode(); } public String getReasonPhrase() throws Exception { return connection.getResponseMessage(); } public Object unwrap() { return connection; } } UrlStringRequestAdapter.java000066400000000000000000000017361172235640600343400ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/basicpackage oauth.signpost.basic; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.Map; import oauth.signpost.http.HttpRequest; public class UrlStringRequestAdapter implements HttpRequest { private String url; public UrlStringRequestAdapter(String url) { this.url = url; } public String getMethod() { return "GET"; } public String getRequestUrl() { return url; } public void setRequestUrl(String url) { this.url = url; } public void setHeader(String name, String value) { } public String getHeader(String name) { return null; } public Map getAllHeaders() { return Collections.emptyMap(); } public InputStream getMessagePayload() throws IOException { return null; } public String getContentType() { return null; } public Object unwrap() { return url; } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/000077500000000000000000000000001172235640600276575ustar00rootroot00000000000000OAuthCommunicationException.java000066400000000000000000000022451172235640600360730ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.exception; @SuppressWarnings("serial") public class OAuthCommunicationException extends OAuthException { private String responseBody; public OAuthCommunicationException(Exception cause) { super("Communication with the service provider failed: " + cause.getLocalizedMessage(), cause); } public OAuthCommunicationException(String message, String responseBody) { super(message); this.responseBody = responseBody; } public String getResponseBody() { return responseBody; } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/OAuthException.java000066400000000000000000000005621172235640600334240ustar00rootroot00000000000000package oauth.signpost.exception; @SuppressWarnings("serial") public abstract class OAuthException extends Exception { public OAuthException(String message) { super(message); } public OAuthException(Throwable cause) { super(cause); } public OAuthException(String message, Throwable cause) { super(message, cause); } } OAuthExpectationFailedException.java000066400000000000000000000014751172235640600366620ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.exception; @SuppressWarnings("serial") public class OAuthExpectationFailedException extends OAuthException { public OAuthExpectationFailedException(String message) { super(message); } } OAuthMessageSignerException.java000066400000000000000000000016151172235640600360220ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.exception; @SuppressWarnings("serial") public class OAuthMessageSignerException extends OAuthException { public OAuthMessageSignerException(String message) { super(message); } public OAuthMessageSignerException(Exception cause) { super(cause); } } OAuthNotAuthorizedException.java000066400000000000000000000023631172235640600360660ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/exception/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.exception; @SuppressWarnings("serial") public class OAuthNotAuthorizedException extends OAuthException { private static final String ERROR = "Authorization failed (server replied with a 401). " + "This can happen if the consumer key was not correct or " + "the signatures did not match."; private String responseBody; public OAuthNotAuthorizedException() { super(ERROR); } public OAuthNotAuthorizedException(String responseBody) { super(ERROR); this.responseBody = responseBody; } public String getResponseBody() { return responseBody; } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/http/000077500000000000000000000000001172235640600266405ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/http/HttpParameters.java000066400000000000000000000224271172235640600324550ustar00rootroot00000000000000/* Copyright (c) 2008, 2009 Netflix, Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.http; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import oauth.signpost.OAuth; /** * A multi-map of HTTP request parameters. Each key references a * {@link SortedSet} of parameters collected from the request during message * signing. Parameter values are sorted as per {@linkplain http * ://oauth.net/core/1.0a/#anchor13}. Every key/value pair will be * percent-encoded upon insertion. This class has special semantics tailored to * being useful for message signing; it's not a general purpose collection class * to handle request parameters. * * @author Matthias Kaeppler */ @SuppressWarnings("serial") public class HttpParameters implements Map>, Serializable { private TreeMap> wrappedMap = new TreeMap>(); public SortedSet put(String key, SortedSet value) { return wrappedMap.put(key, value); } public SortedSet put(String key, SortedSet values, boolean percentEncode) { if (percentEncode) { remove(key); for (String v : values) { put(key, v, true); } return get(key); } else { return wrappedMap.put(key, values); } } /** * Convenience method to add a single value for the parameter specified by * 'key'. * * @param key * the parameter name * @param value * the parameter value * @return the value */ public String put(String key, String value) { return put(key, value, false); } /** * Convenience method to add a single value for the parameter specified by * 'key'. * * @param key * the parameter name * @param value * the parameter value * @param percentEncode * whether key and value should be percent encoded before being * inserted into the map * @return the value */ public String put(String key, String value, boolean percentEncode) { // fix contributed by Bjorn Roche - key should be encoded before wrappedMap.get key = percentEncode ? OAuth.percentEncode(key) : key; SortedSet values = wrappedMap.get(key); if (values == null) { values = new TreeSet(); wrappedMap.put( key, values); } if (value != null) { value = percentEncode ? OAuth.percentEncode(value) : value; values.add(value); } return value; } /** * Convenience method to allow for storing null values. {@link #put} doesn't * allow null values, because that would be ambiguous. * * @param key * the parameter name * @param nullString * can be anything, but probably... null? * @return null */ public String putNull(String key, String nullString) { return put(key, nullString); } public void putAll(Map> m) { wrappedMap.putAll(m); } public void putAll(Map> m, boolean percentEncode) { if (percentEncode) { for (String key : m.keySet()) { put(key, m.get(key), true); } } else { wrappedMap.putAll(m); } } public void putAll(String[] keyValuePairs, boolean percentEncode) { for (int i = 0; i < keyValuePairs.length - 1; i += 2) { this.put(keyValuePairs[i], keyValuePairs[i + 1], percentEncode); } } /** * Convenience method to merge a Map>. * * @param m * the map */ public void putMap(Map> m) { for (String key : m.keySet()) { SortedSet vals = get(key); if (vals == null) { vals = new TreeSet(); put(key, vals); } vals.addAll(m.get(key)); } } public SortedSet get(Object key) { return wrappedMap.get(key); } /** * Convenience method for {@link #getFirst(key, false)}. * * @param key * the parameter name (must be percent encoded if it contains unsafe * characters!) * @return the first value found for this parameter */ public String getFirst(Object key) { return getFirst(key, false); } /** * Returns the first value from the set of all values for the given * parameter name. If the key passed to this method contains special * characters, you MUST first percent encode it using * {@link OAuth#percentEncode(String)}, otherwise the lookup will fail * (that's because upon storing values in this map, keys get * percent-encoded). * * @param key * the parameter name (must be percent encoded if it contains unsafe * characters!) * @param percentDecode * whether the value being retrieved should be percent decoded * @return the first value found for this parameter */ public String getFirst(Object key, boolean percentDecode) { SortedSet values = wrappedMap.get(key); if (values == null || values.isEmpty()) { return null; } String value = values.first(); return percentDecode ? OAuth.percentDecode(value) : value; } /** * Concatenates all values for the given key to a list of key/value pairs * suitable for use in a URL query string. * * @param key * the parameter name * @return the query string */ public String getAsQueryString(Object key) { return getAsQueryString(key, true); } /** * Concatenates all values for the given key to a list of key/value pairs * suitable for use in a URL query string. * * @param key * the parameter name * @param percentEncode * whether key should be percent encoded before being * used with the map * @return the query string */ public String getAsQueryString(Object key, boolean percentEncode) { // fix contributed by Stjepan Rajko - we need the percentEncode parameter // because some places (like SignatureBaseString.normalizeRequestParameters) // need to supply the parameter percent encoded StringBuilder sb = new StringBuilder(); if(percentEncode) key = OAuth.percentEncode((String) key); Set values = wrappedMap.get(key); if (values == null) { return key + "="; } Iterator iter = values.iterator(); while (iter.hasNext()) { sb.append(key + "=" + iter.next()); if (iter.hasNext()) { sb.append("&"); } } return sb.toString(); } public String getAsHeaderElement(String key) { String value = getFirst(key); if (value == null) { return null; } return key + "=\"" + value + "\""; } public boolean containsKey(Object key) { return wrappedMap.containsKey(key); } public boolean containsValue(Object value) { for (Set values : wrappedMap.values()) { if (values.contains(value)) { return true; } } return false; } public int size() { int count = 0; for (String key : wrappedMap.keySet()) { count += wrappedMap.get(key).size(); } return count; } public boolean isEmpty() { return wrappedMap.isEmpty(); } public void clear() { wrappedMap.clear(); } public SortedSet remove(Object key) { return wrappedMap.remove(key); } public Set keySet() { return wrappedMap.keySet(); } public Collection> values() { return wrappedMap.values(); } public Set>> entrySet() { return wrappedMap.entrySet(); } public HttpParameters getOAuthParameters() { HttpParameters oauthParams = new HttpParameters(); for (Entry> param : this.entrySet()) { String key = param.getKey(); if (key.startsWith("oauth_") || key.startsWith("x_oauth_")) { oauthParams.put(key, param.getValue()); } } return oauthParams; } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/http/HttpRequest.java000066400000000000000000000022421172235640600317730ustar00rootroot00000000000000package oauth.signpost.http; import java.io.IOException; import java.io.InputStream; import java.util.Map; import oauth.signpost.OAuthConsumer; import oauth.signpost.basic.HttpURLConnectionRequestAdapter; /** * A concise description of an HTTP request. Contains methods to access all * those parts of an HTTP request which Signpost needs to sign a message. If you * want to extend Signpost to sign a different kind of HTTP request than those * currently supported, you'll have to write an adapter which implements this * interface and a custom {@link OAuthConsumer} which performs the wrapping. * * @see HttpURLConnectionRequestAdapter * @author Matthias Kaeppler */ public interface HttpRequest { String getMethod(); String getRequestUrl(); void setRequestUrl(String url); void setHeader(String name, String value); String getHeader(String name); Map getAllHeaders(); InputStream getMessagePayload() throws IOException; String getContentType(); /** * Returns the wrapped request object, in case you must work directly on it. * * @return the wrapped request object */ Object unwrap(); } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/http/HttpResponse.java000066400000000000000000000007001172235640600321360ustar00rootroot00000000000000package oauth.signpost.http; import java.io.IOException; import java.io.InputStream; public interface HttpResponse { int getStatusCode() throws IOException; String getReasonPhrase() throws Exception; InputStream getContent() throws IOException; /** * Returns the underlying response object, in case you need to work on it * directly. * * @return the wrapped response object */ Object unwrap(); } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/000077500000000000000000000000001172235640600276625ustar00rootroot00000000000000AuthorizationHeaderSigningStrategy.java000066400000000000000000000026671172235640600374740ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signaturepackage oauth.signpost.signature; import java.util.Iterator; import oauth.signpost.OAuth; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; /** * Writes to the HTTP Authorization header field. * * @author Matthias Kaeppler */ public class AuthorizationHeaderSigningStrategy implements SigningStrategy { private static final long serialVersionUID = 1L; public String writeSignature(String signature, HttpRequest request, HttpParameters requestParameters) { StringBuilder sb = new StringBuilder(); sb.append("OAuth "); // add the realm parameter, if any if (requestParameters.containsKey("realm")) { sb.append(requestParameters.getAsHeaderElement("realm")); sb.append(", "); } // add all (x_)oauth parameters HttpParameters oauthParams = requestParameters.getOAuthParameters(); oauthParams.put(OAuth.OAUTH_SIGNATURE, signature, true); Iterator iter = oauthParams.keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); sb.append(oauthParams.getAsHeaderElement(key)); if (iter.hasNext()) { sb.append(", "); } } String header = sb.toString(); OAuth.debugOut("Auth Header", header); request.setHeader(OAuth.HTTP_AUTHORIZATION_HEADER, header); return header; } } HmacSha1MessageSigner.java000066400000000000000000000042301172235640600345070ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.signature; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import oauth.signpost.OAuth; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; @SuppressWarnings("serial") public class HmacSha1MessageSigner extends OAuthMessageSigner { private static final String MAC_NAME = "HmacSHA1"; @Override public String getSignatureMethod() { return "HMAC-SHA1"; } @Override public String sign(HttpRequest request, HttpParameters requestParams) throws OAuthMessageSignerException { try { String keyString = OAuth.percentEncode(getConsumerSecret()) + '&' + OAuth.percentEncode(getTokenSecret()); byte[] keyBytes = keyString.getBytes(OAuth.ENCODING); SecretKey key = new SecretKeySpec(keyBytes, MAC_NAME); Mac mac = Mac.getInstance(MAC_NAME); mac.init(key); String sbs = new SignatureBaseString(request, requestParams).generate(); OAuth.debugOut("SBS", sbs); byte[] text = sbs.getBytes(OAuth.ENCODING); return base64Encode(mac.doFinal(text)).trim(); } catch (GeneralSecurityException e) { throw new OAuthMessageSignerException(e); } catch (UnsupportedEncodingException e) { throw new OAuthMessageSignerException(e); } } } OAuthMessageSigner.java000066400000000000000000000041521172235640600341450ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.signature; import java.io.IOException; import java.io.Serializable; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; import org.apache.commons.codec.binary.Base64; public abstract class OAuthMessageSigner implements Serializable { private static final long serialVersionUID = 4445779788786131202L; private transient Base64 base64; private String consumerSecret; private String tokenSecret; public OAuthMessageSigner() { this.base64 = new Base64(); } public abstract String sign(HttpRequest request, HttpParameters requestParameters) throws OAuthMessageSignerException; public abstract String getSignatureMethod(); public String getConsumerSecret() { return consumerSecret; } public String getTokenSecret() { return tokenSecret; } public void setConsumerSecret(String consumerSecret) { this.consumerSecret = consumerSecret; } public void setTokenSecret(String tokenSecret) { this.tokenSecret = tokenSecret; } protected byte[] decodeBase64(String s) { return base64.decode(s.getBytes()); } protected String base64Encode(byte[] b) { return new String(base64.encode(b)); } private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.base64 = new Base64(); } } PlainTextMessageSigner.java000066400000000000000000000023531172235640600350360ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.signature; import oauth.signpost.OAuth; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; @SuppressWarnings("serial") public class PlainTextMessageSigner extends OAuthMessageSigner { @Override public String getSignatureMethod() { return "PLAINTEXT"; } @Override public String sign(HttpRequest request, HttpParameters requestParams) throws OAuthMessageSignerException { return OAuth.percentEncode(getConsumerSecret()) + '&' + OAuth.percentEncode(getTokenSecret()); } } QueryStringSigningStrategy.java000066400000000000000000000031021172235640600360000ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signaturepackage oauth.signpost.signature; import java.util.Iterator; import oauth.signpost.OAuth; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; /** * Writes to a URL query string. Note that this currently ONLY works * when signing a URL directly, not with HTTP request objects. That's * because most HTTP request implementations do not allow the client to change * the URL once the request has been instantiated, so there is no way to append * parameters to it. * * @author Matthias Kaeppler */ public class QueryStringSigningStrategy implements SigningStrategy { private static final long serialVersionUID = 1L; public String writeSignature(String signature, HttpRequest request, HttpParameters requestParameters) { // add all (x_)oauth parameters HttpParameters oauthParams = requestParameters.getOAuthParameters(); oauthParams.put(OAuth.OAUTH_SIGNATURE, signature, true); Iterator iter = oauthParams.keySet().iterator(); // add the first query parameter (we always have at least the signature) String firstKey = iter.next(); StringBuilder sb = new StringBuilder(OAuth.addQueryString(request.getRequestUrl(), oauthParams.getAsQueryString(firstKey))); while (iter.hasNext()) { sb.append("&"); String key = iter.next(); sb.append(oauthParams.getAsQueryString(key)); } String signedUrl = sb.toString(); request.setRequestUrl(signedUrl); return signedUrl; } } SignatureBaseString.java000066400000000000000000000101371172235640600343730ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost.signature; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Iterator; import oauth.signpost.OAuth; import oauth.signpost.exception.OAuthMessageSignerException; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; public class SignatureBaseString { private HttpRequest request; private HttpParameters requestParameters; /** * Constructs a new SBS instance that will operate on the given request * object and parameter set. * * @param request * the HTTP request * @param requestParameters * the set of request parameters from the Authorization header, query * string and form body */ public SignatureBaseString(HttpRequest request, HttpParameters requestParameters) { this.request = request; this.requestParameters = requestParameters; } /** * Builds the signature base string from the data this instance was * configured with. * * @return the signature base string * @throws OAuthMessageSignerException */ public String generate() throws OAuthMessageSignerException { try { String normalizedUrl = normalizeRequestUrl(); String normalizedParams = normalizeRequestParameters(); return request.getMethod() + '&' + OAuth.percentEncode(normalizedUrl) + '&' + OAuth.percentEncode(normalizedParams); } catch (Exception e) { throw new OAuthMessageSignerException(e); } } public String normalizeRequestUrl() throws URISyntaxException { URI uri = new URI(request.getRequestUrl()); String scheme = uri.getScheme().toLowerCase(); String authority = uri.getAuthority().toLowerCase(); boolean dropPort = (scheme.equals("http") && uri.getPort() == 80) || (scheme.equals("https") && uri.getPort() == 443); if (dropPort) { // find the last : in the authority int index = authority.lastIndexOf(":"); if (index >= 0) { authority = authority.substring(0, index); } } String path = uri.getRawPath(); if (path == null || path.length() <= 0) { path = "/"; // conforms to RFC 2616 section 3.2.2 } // we know that there is no query and no fragment here. return scheme + "://" + authority + path; } /** * Normalizes the set of request parameters this instance was configured * with, as per OAuth spec section 9.1.1. * * @param parameters * the set of request parameters * @return the normalized params string * @throws IOException */ public String normalizeRequestParameters() throws IOException { if (requestParameters == null) { return ""; } StringBuilder sb = new StringBuilder(); Iterator iter = requestParameters.keySet().iterator(); for (int i = 0; iter.hasNext(); i++) { String param = iter.next(); if (OAuth.OAUTH_SIGNATURE.equals(param) || "realm".equals(param)) { continue; } if (i > 0) { sb.append("&"); } // fix contributed by Stjepan Rajko // since param should already be encoded, we supply false for percentEncode sb.append(requestParameters.getAsQueryString(param, false)); } return sb.toString(); } } kaeppler-signpost-653c361/signpost-core/src/main/java/oauth/signpost/signature/SigningStrategy.java000066400000000000000000000020421172235640600336440ustar00rootroot00000000000000package oauth.signpost.signature; import java.io.Serializable; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; /** *

* Defines how an OAuth signature string is written to a request. *

*

* Unlike {@link OAuthMessageSigner}, which is concerned with how to * generate a signature, this class is concered with where to write it * (e.g. HTTP header or query string). *

* * @author Matthias Kaeppler */ public interface SigningStrategy extends Serializable { /** * Writes an OAuth signature and all remaining required parameters to an * HTTP message. * * @param signature * the signature to write * @param request * the request to sign * @param requestParameters * the request parameters * @return whatever has been written to the request, e.g. an Authorization * header field */ String writeSignature(String signature, HttpRequest request, HttpParameters requestParameters); } kaeppler-signpost-653c361/signpost-core/src/test/000077500000000000000000000000001172235640600220055ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/000077500000000000000000000000001172235640600227265ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/000077500000000000000000000000001172235640600240465ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/000077500000000000000000000000001172235640600257145ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/OAuthConsumerTest.java000066400000000000000000000246321172235640600321620ustar00rootroot00000000000000package oauth.signpost; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; import oauth.signpost.signature.HmacSha1MessageSigner; import oauth.signpost.signature.OAuthMessageSigner; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentCaptor; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public abstract class OAuthConsumerTest extends SignpostTestBase { protected OAuthConsumer consumer; protected abstract OAuthConsumer buildConsumer(String consumerKey, String consumerSecret, OAuthMessageSigner messageSigner); @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowIfConsumerKeyNotSet() throws Exception { OAuthConsumer consumer = buildConsumer(null, CONSUMER_SECRET, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); consumer.sign(httpGetMock); } @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowIfConsumerSecretNotSet() throws Exception { OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, null, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); consumer.sign(httpGetMock); } @Test public void shouldSignHttpRequestMessage() throws Exception { OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); consumer.sign(httpGetMock); verify(httpGetMock).setHeader(eq("Authorization"), argThat(new IsCompleteListOfOAuthParameters())); } @Test public void shouldSignUrl() throws Exception { OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); String result = consumer.sign("http://www.example.com?q=1"); assertNotNull(result); String[] parts = result.split("\\?"); assertEquals("parameters are missing", 2, parts.length); assertEquals("http://www.example.com", parts[0]); HttpParameters params = OAuth.decodeForm(parts[1]); assertAllOAuthParametersExist(params); assertEquals("1", params.getFirst("q")); } @Test public void shouldIncludeOAuthAndQueryAndBodyParams() throws Exception { // mock a request that has custom query, body, and header params set HttpRequest request = mock(HttpRequest.class); when(request.getRequestUrl()).thenReturn("http://example.com?a=1+1"); ByteArrayInputStream body = new ByteArrayInputStream("b=2+2".getBytes()); when(request.getMessagePayload()).thenReturn(body); when(request.getContentType()).thenReturn( "application/x-www-form-urlencoded; charset=ISO-8859-1"); when(request.getHeader("Authorization")).thenReturn( "OAuth realm=\"http%3A%2F%2Fexample.com\", oauth_token=\"12%25345\", oauth_signature=\"1234\""); OAuthMessageSigner signer = mock(HmacSha1MessageSigner.class); consumer.setMessageSigner(signer); consumer.sign(request); // verify that all custom params are properly read and passed to the // message signer ArgumentMatcher hasAllParameters = new ArgumentMatcher() { public boolean matches(Object argument) { HttpParameters params = (HttpParameters) argument; assertEquals("1 1", params.getFirst("a", true)); assertEquals("2 2", params.getFirst("b", true)); assertEquals("http://example.com", params.getFirst("realm", true)); assertEquals("12%345", params.getFirst("oauth_token", true)); // signature should be dropped, not valid to pre-set assertNull(params.getFirst("oauth_signature")); return true; } }; verify(signer).sign(same(request), argThat(hasAllParameters)); } @Test public void shouldHonorManuallySetSigningParameters() throws Exception { // mock a request that has custom query, body, and header params set HttpRequest request = mock(HttpRequest.class); when(request.getRequestUrl()).thenReturn("http://example.com?a=1"); OAuthMessageSigner signer = mock(HmacSha1MessageSigner.class); consumer.setMessageSigner(signer); HttpParameters params = new HttpParameters(); params.put("oauth_callback", "http://mycallback"); consumer.setAdditionalParameters(params); consumer.sign(request); // verify that all custom params are properly read and passed to the // message signer ArgumentMatcher hasParameters = new ArgumentMatcher() { public boolean matches(Object argument) { HttpParameters params = (HttpParameters) argument; assertEquals("http://mycallback", params.getFirst("oauth_callback")); assertEquals("1", params.getFirst("a")); return true; } }; verify(signer).sign(same(request), argThat(hasParameters)); } @Test public void shouldPercentEncodeOAuthParameters() throws Exception { OAuthConsumer consumer = buildConsumer("1%2", CONSUMER_SECRET, null); consumer.setTokenWithSecret("3 4", TOKEN_SECRET); consumer.sign(httpGetMock); verify(httpGetMock).setHeader(eq("Authorization"), argThat(new HasValuesPercentEncoded())); } @Test public void shouldBeSerializable() throws Exception { OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream ostream = new ObjectOutputStream(baos); ostream.writeObject(consumer); ObjectInputStream istream = new ObjectInputStream(new ByteArrayInputStream( baos.toByteArray())); consumer = (OAuthConsumer) istream.readObject(); assertEquals(CONSUMER_KEY, consumer.getConsumerKey()); assertEquals(CONSUMER_SECRET, consumer.getConsumerSecret()); assertEquals(TOKEN, consumer.getToken()); assertEquals(TOKEN_SECRET, consumer.getTokenSecret()); // signing messages should still work consumer.sign(httpGetMock); } @Test public void shoudNotCllideOnMultiThread() throws Exception { for (int i = 0; i < 10; i++) { initRequestMocks(); final OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); Thread t1 = new Thread() { @Override public void run() { try { consumer.sign(httpGetMock); } catch (Exception e) { throw new RuntimeException("sign error", e); } } }; Thread t2 = new Thread() { @Override public void run() { try { consumer.sign(httpGetMockWithQueryString); } catch (Exception e) { throw new RuntimeException("sign error", e); } } }; t1.start(); t2.start(); t1.join(); t2.join(); ArgumentCaptor arg1 = ArgumentCaptor.forClass(String.class); ArgumentCaptor arg2 = ArgumentCaptor.forClass(String.class); verify(httpGetMock).setHeader(eq("Authorization"), arg1.capture()); verify(httpGetMockWithQueryString).setHeader(eq("Authorization"), arg2.capture()); HttpParameters headerMap1 = OAuth.oauthHeaderToParamsMap(arg1.getValue()); HttpParameters headerMap2 = OAuth.oauthHeaderToParamsMap(arg2.getValue()); assertThat(headerMap1.getFirst(OAuth.OAUTH_NONCE), not(equalTo(headerMap2.getFirst(OAuth.OAUTH_NONCE)))); } } // @Test // public void shouldSupport2LeggedOAuth() throws Exception { // OAuthConsumer consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, // null); // // // note how we do not set a token and secret; should still include the // // oauth_token parameter // // consumer.sign(httpGetMock); // // verify(httpGetMock).setHeader(eq("Authorization"), // argThat(new IsCompleteListOfOAuthParameters())); // } private class IsCompleteListOfOAuthParameters extends ArgumentMatcher { @Override public boolean matches(Object argument) { String oauthHeader = (String) argument; assertTrue(oauthHeader.startsWith("OAuth ")); assertAllOAuthParametersExist(OAuth.oauthHeaderToParamsMap(oauthHeader)); return true; } } private void assertAllOAuthParametersExist(HttpParameters params) { assertNotNull(params.getFirst("oauth_consumer_key")); assertNotNull(params.getFirst("oauth_token")); assertNotNull(params.getFirst("oauth_signature_method")); assertNotNull(params.getFirst("oauth_signature")); assertNotNull(params.getFirst("oauth_timestamp")); assertNotNull(params.getFirst("oauth_nonce")); assertNotNull(params.getFirst("oauth_version")); } private class HasValuesPercentEncoded extends ArgumentMatcher { @Override public boolean matches(Object argument) { String oauthHeader = (String) argument; HttpParameters params = OAuth.oauthHeaderToParamsMap(oauthHeader); assertEquals("1%252", params.getFirst("oauth_consumer_key")); assertEquals("3%204", params.getFirst("oauth_token")); return true; } } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/OAuthProviderTest.java000066400000000000000000000160361172235640600321600ustar00rootroot00000000000000package oauth.signpost; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URL; import oauth.signpost.basic.DefaultOAuthConsumer; import oauth.signpost.exception.OAuthExpectationFailedException; import oauth.signpost.http.HttpParameters; import oauth.signpost.http.HttpRequest; import oauth.signpost.mocks.OAuthProviderMock; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public abstract class OAuthProviderTest extends SignpostTestBase { protected OAuthProvider provider; @Mock OAuthConsumer consumerMock; @Before public void prepare() throws Exception { MockitoAnnotations.initMocks(this); // init consumer mock when(consumerMock.getConsumerKey()).thenReturn(CONSUMER_KEY); when(consumerMock.getConsumerSecret()).thenReturn(CONSUMER_SECRET); when(consumerMock.getToken()).thenReturn(TOKEN); when(consumerMock.getTokenSecret()).thenReturn(TOKEN_SECRET); provider = buildProvider(REQUEST_TOKEN_ENDPOINT_URL, ACCESS_TOKEN_ENDPOINT_URL, AUTHORIZE_WEBSITE_URL, true); } protected abstract OAuthProvider buildProvider(String requestTokenUrl, String accessTokenUrl, String websiteUrl, boolean mockConnection) throws Exception; @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowExpectationFailedIfConsumerKeyNotSet() throws Exception { provider = buildProvider(REQUEST_TOKEN_ENDPOINT_URL, ACCESS_TOKEN_ENDPOINT_URL, AUTHORIZE_WEBSITE_URL, true); provider.retrieveRequestToken(new DefaultOAuthConsumer(null, CONSUMER_SECRET), REQUEST_TOKEN_ENDPOINT_URL); } @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowExpectationFailedIfConsumerSecretNotSet() throws Exception { provider = buildProvider(REQUEST_TOKEN_ENDPOINT_URL, ACCESS_TOKEN_ENDPOINT_URL, AUTHORIZE_WEBSITE_URL, true); provider.retrieveRequestToken(new DefaultOAuthConsumer(CONSUMER_KEY, null), REQUEST_TOKEN_ENDPOINT_URL); } @Test public void shouldRetrieveRequestTokenAndUpdateConsumer() throws Exception { String callbackUrl = "http://www.example.com"; String result = provider.retrieveRequestToken(consumerMock, callbackUrl); verify(consumerMock).sign((HttpRequest) anyObject()); verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET); assertEquals(AUTHORIZE_WEBSITE_URL + "?" + OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_CALLBACK + "=" + "http%3A%2F%2Fwww.example.com", result); } @Test public void shouldRespectCustomQueryParametersInAuthWebsiteUrl() throws Exception { provider = buildProvider(REQUEST_TOKEN_ENDPOINT_URL, ACCESS_TOKEN_ENDPOINT_URL, "http://provider.com/authorize?q=1", true); String callbackUrl = "http://www.example.com"; // the URL ctor checks for URL validity URL url = new URL(provider.retrieveRequestToken(consumerMock, callbackUrl)); assertTrue(url.getQuery().startsWith("q=1&oauth_token=")); } @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowWhenGettingAccessTokenAndRequestTokenNotSet() throws Exception { when(consumerMock.getToken()).thenReturn(null); provider.retrieveAccessToken(consumerMock, null); } @Test(expected = OAuthExpectationFailedException.class) public void shouldThrowWhenGettingAccessTokenAndRequestTokenSecretNotSet() throws Exception { when(consumerMock.getTokenSecret()).thenReturn(null); provider.retrieveAccessToken(consumerMock, null); } @Test public void shouldRetrieveAccessTokenAndUpdateConsumer() throws Exception { provider.retrieveAccessToken(consumerMock, null); verify(consumerMock).sign((HttpRequest) anyObject()); verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET); } @Test public void shouldMakeSpecialResponseParametersAvailableToConsumer() throws Exception { assertTrue(provider.getResponseParameters().isEmpty()); ((OAuthProviderMock) provider).mockConnection(OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_TOKEN_SECRET + "=" + TOKEN_SECRET + "&a=1"); provider.retrieveRequestToken(consumerMock, null); assertEquals(1, provider.getResponseParameters().size()); assertTrue(provider.getResponseParameters().containsKey("a")); assertEquals("1", provider.getResponseParameters().getFirst("a")); ((OAuthProviderMock) provider).mockConnection(OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_TOKEN_SECRET + "=" + TOKEN_SECRET + "&b=2&c=3"); provider.retrieveAccessToken(consumerMock, null); assertEquals(2, provider.getResponseParameters().size()); assertTrue(provider.getResponseParameters().containsKey("b")); assertTrue(provider.getResponseParameters().containsKey("c")); assertEquals("2", provider.getResponseParameters().getFirst("b")); assertEquals("3", provider.getResponseParameters().getFirst("c")); } @Test public void shouldBeSerializable() throws Exception { // the mock consumer isn't serializable, thus set a normal one OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); consumer.setTokenWithSecret(TOKEN, TOKEN_SECRET); provider = buildProvider(REQUEST_TOKEN_ENDPOINT_URL, ACCESS_TOKEN_ENDPOINT_URL, AUTHORIZE_WEBSITE_URL, false); provider.setOAuth10a(true); // prepare a provider that has response params set HttpParameters params = new HttpParameters(); params.put("a", "1"); ((AbstractOAuthProvider) provider).setResponseParameters(params); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream ostream = new ObjectOutputStream(baos); ostream.writeObject(provider); ObjectInputStream istream = new ObjectInputStream(new ByteArrayInputStream( baos.toByteArray())); provider = (OAuthProvider) istream.readObject(); assertEquals(REQUEST_TOKEN_ENDPOINT_URL, provider.getRequestTokenEndpointUrl()); assertEquals(ACCESS_TOKEN_ENDPOINT_URL, provider.getAccessTokenEndpointUrl()); assertEquals(AUTHORIZE_WEBSITE_URL, provider.getAuthorizationWebsiteUrl()); assertEquals(true, provider.isOAuth10a()); assertNotNull(provider.getResponseParameters()); assertEquals("1", provider.getResponseParameters().getFirst("a")); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/OAuthTest.java000066400000000000000000000103561172235640600304440ustar00rootroot00000000000000package oauth.signpost; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import oauth.signpost.http.HttpParameters; import org.junit.Test; public class OAuthTest { private String rfc3986ReservedCharacters = ":/?#[]@!$&'()*+,;="; private String rfc3986UnreservedCharacters = "-._~"; // alpha-numeric chars ignored private String reservedCharactersEncoded = "%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D"; /** * OAuth percent encoding demands characters from the URI reserved set to be * percent encoded, and characters from the unreserved to NOT be percent * encoded. All characters must be UTF-8 encoded first. */ @Test public void shouldHonorOAuthPercentEncodingRules() { // ALWAYS percent encode all characters from the reserved set as per RFC3986 assertEquals(reservedCharactersEncoded, OAuth.percentEncode(rfc3986ReservedCharacters)); // NEVER percent encode any characters from the unreserved set as per RFC3986 assertEquals(rfc3986UnreservedCharacters, OAuth.percentEncode(rfc3986UnreservedCharacters)); // percent encode spaces, do not use + assertEquals("%20", OAuth.percentEncode(" ")); // percent encode % assertEquals("%25", OAuth.percentEncode("%")); } @Test public void shouldCorrectlyPercentDecodeReservedCharacters() { assertEquals(rfc3986ReservedCharacters, OAuth.percentDecode(reservedCharactersEncoded)); assertEquals("%", OAuth.percentDecode("%25")); assertEquals(" ", OAuth.percentDecode("%20")); } @Test public void shouldCorrectlyFormEncodeParameters() throws Exception { HashMap params = new HashMap(); params.put("one", rfc3986ReservedCharacters); params.put(rfc3986ReservedCharacters, rfc3986UnreservedCharacters); // both keys and values must be percent encoded assertEquals("one=" + reservedCharactersEncoded + "&" + reservedCharactersEncoded + "=" + rfc3986UnreservedCharacters, OAuth.formEncode(params.entrySet())); } @Test public void shouldCorrectlyFormDecodeParameters() { HttpParameters params = OAuth.decodeForm("one=" + reservedCharactersEncoded + "&" + "one=another&" + reservedCharactersEncoded + "=" + rfc3986UnreservedCharacters); assertTrue(params.size() == 3); Iterator iter1 = params.get("one").iterator(); assertEquals(rfc3986ReservedCharacters, iter1.next()); assertEquals("another", iter1.next()); Iterator iter2 = params.get(rfc3986ReservedCharacters).iterator(); assertEquals(rfc3986UnreservedCharacters, iter2.next()); } @Test public void shouldCorrectlyAppendQueryParameters() { String url1 = "http://www.example.com"; assertEquals("http://www.example.com?a=1&b=2", OAuth.addQueryParameters(url1, "a", "1", "b", "2")); String url2 = "http://www.example.com?x=1"; assertEquals("http://www.example.com?x=1&a=1&b=2", OAuth.addQueryParameters(url2, "a", "1", "b", "2")); Map params = new LinkedHashMap(); params.put("a", "1"); params.put("b", "2"); assertEquals("http://www.example.com?x=1&a=1&b=2", OAuth.addQueryParameters(url2, params)); } @Test public void shouldCorrectlyParseOAuthAuthorizationHeader() { String header = "OAuth realm=\"http://xyz.com\", oauth_callback=\"oob\""; HttpParameters params = OAuth.oauthHeaderToParamsMap(header); assertEquals("http://xyz.com", params.getFirst("realm")); assertEquals("oob", params.getFirst("oauth_callback")); } @Test public void shouldCorrectlyPrepareOAuthHeader() { assertEquals("OAuth realm=\"http://x.com\"", OAuth.prepareOAuthHeader("realm", "http://x.com")); assertEquals("OAuth realm=\"http://x.com\", oauth_token=\"x%25y\"", OAuth .prepareOAuthHeader("realm", "http://x.com", "oauth_token", "x%y")); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/SignpostTestBase.java000066400000000000000000000045131172235640600320230ustar00rootroot00000000000000package oauth.signpost; import static org.mockito.Mockito.when; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; import org.junit.Before; import org.junit.BeforeClass; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public abstract class SignpostTestBase { public static final String OAUTH_VERSION = "1.0"; public static final String CONSUMER_KEY = "dpf43f3p2l4k3l03"; public static final String CONSUMER_SECRET = "kd94hf93k423kf44"; public static final String TOKEN = "nnch734d00sl2jdk"; public static final String TOKEN_SECRET = "pfkkdhi9sl3r4s00"; public static final String NONCE = "kllo9940pd9333jh"; public static final String TIMESTAMP = "1191242096"; public static final String SIGNATURE_METHOD = "HMAC-SHA1"; public static final String REQUEST_TOKEN_ENDPOINT_URL = "http://api.test.com/request_token"; public static final String ACCESS_TOKEN_ENDPOINT_URL = "http://api.test.com/access_token"; public static final String AUTHORIZE_WEBSITE_URL = "http://www.test.com/authorize"; public static final HttpParameters OAUTH_PARAMS = new HttpParameters(); public static final HttpParameters EMPTY_PARAMS = new HttpParameters(); @Mock protected HttpRequest httpGetMock; @Mock protected HttpRequest httpGetMockWithQueryString; @Mock protected HttpRequest httpPostMock; @BeforeClass public static void initOAuthParams() { OAUTH_PARAMS.put("oauth_consumer_key", CONSUMER_KEY); OAUTH_PARAMS.put("oauth_signature_method", SIGNATURE_METHOD); OAUTH_PARAMS.put("oauth_timestamp", TIMESTAMP); OAUTH_PARAMS.put("oauth_nonce", NONCE); OAUTH_PARAMS.put("oauth_version", OAUTH_VERSION); OAUTH_PARAMS.put("oauth_token", TOKEN); } @Before public void initRequestMocks() { MockitoAnnotations.initMocks(this); when(httpGetMock.getMethod()).thenReturn("GET"); when(httpGetMock.getRequestUrl()).thenReturn("http://www.example.com"); when(httpGetMockWithQueryString.getMethod()).thenReturn("GET"); when(httpGetMockWithQueryString.getRequestUrl()).thenReturn("http://www.example.com?foo=bar"); when(httpPostMock.getMethod()).thenReturn("POST"); when(httpPostMock.getRequestUrl()).thenReturn("http://www.example.com"); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/basic/000077500000000000000000000000001172235640600267755ustar00rootroot00000000000000DefaultOAuthConsumerTest.java000066400000000000000000000013161172235640600344630ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/basicpackage oauth.signpost.basic; import oauth.signpost.OAuthConsumer; import oauth.signpost.OAuthConsumerTest; import oauth.signpost.signature.OAuthMessageSigner; import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class DefaultOAuthConsumerTest extends OAuthConsumerTest { @Before public void prepare() { consumer = buildConsumer(CONSUMER_KEY, CONSUMER_SECRET, null); } @Override protected OAuthConsumer buildConsumer(String consumerKey, String consumerSecret, OAuthMessageSigner messageSigner) { return new DefaultOAuthConsumer(consumerKey, consumerSecret); } } DefaultOAuthProviderTest.java000066400000000000000000000017411172235640600344640ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/basicpackage oauth.signpost.basic; import oauth.signpost.OAuth; import oauth.signpost.OAuthProvider; import oauth.signpost.OAuthProviderTest; import oauth.signpost.mocks.DefaultOAuthProviderMock; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class DefaultOAuthProviderTest extends OAuthProviderTest { protected OAuthProvider buildProvider(String requestTokenUrl, String accessTokenUrl, String websiteUrl, boolean mockConnection) throws Exception { if (mockConnection) { DefaultOAuthProviderMock provider = new DefaultOAuthProviderMock(requestTokenUrl, accessTokenUrl, websiteUrl); provider.mockConnection(OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_TOKEN_SECRET + "=" + TOKEN_SECRET); return provider; } return new DefaultOAuthProvider(requestTokenUrl, accessTokenUrl, websiteUrl); } } HttpRequestAdapterTest.java000066400000000000000000000016241172235640600342150ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/basicpackage oauth.signpost.basic; import static org.junit.Assert.assertTrue; import java.net.HttpURLConnection; import java.net.URL; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class HttpRequestAdapterTest extends HttpRequestAdapterTestBase { @Override public void prepareRequest() throws Exception { HttpURLConnection conn = (HttpURLConnection) new URL(URL).openConnection(); conn.setRequestMethod(HTTP_POST_METHOD); conn.setRequestProperty(HEADER_NAME, HEADER_VALUE); conn.setRequestProperty("Content-Type", CONTENT_TYPE); request = new HttpURLConnectionRequestAdapter(conn); } @Override public void shouldReturnCorrectMessagePayload() throws Exception { // can't test this here, because we would have to establish a connection assertTrue(true); } } HttpRequestAdapterTestBase.java000066400000000000000000000034531172235640600350120ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/basicpackage oauth.signpost.basic; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import java.io.BufferedReader; import java.io.InputStreamReader; import oauth.signpost.http.HttpRequest; import org.junit.Before; import org.junit.Test; public abstract class HttpRequestAdapterTestBase { protected static final String URL = "http://www.example.com/protected"; protected static final String HTTP_POST_METHOD = "POST"; protected static final String CONTENT_TYPE = "text/plain"; protected static final String HEADER_NAME = "test-header"; protected static final String HEADER_VALUE = "test-header-value"; protected static final String PAYLOAD = "message-body"; protected HttpRequest request; @Before public abstract void prepareRequest() throws Exception; @Test public void shouldReturnCorrectRequestUrl() { assertEquals(URL, request.getRequestUrl()); } @Test public void shouldReturnCorrectRequestMethod() { assertEquals(HTTP_POST_METHOD, request.getMethod()); } @Test public void shouldGetAndSetRequestHeaders() { assertEquals(HEADER_VALUE, request.getHeader(HEADER_NAME)); request.setHeader("a", "b"); assertEquals("b", request.getHeader("a")); assertTrue(request.getAllHeaders().containsKey(HEADER_NAME)); assertTrue(request.getAllHeaders().containsKey("a")); } @Test public void shouldReturnCorrectContentType() { assertEquals(CONTENT_TYPE, request.getContentType()); } @Test public void shouldReturnCorrectMessagePayload() throws Exception { String actual = new BufferedReader(new InputStreamReader( request.getMessagePayload())).readLine(); assertEquals(PAYLOAD, actual); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/http/000077500000000000000000000000001172235640600266735ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/http/HttpParametersTest.java000066400000000000000000000045061172235640600333460ustar00rootroot00000000000000package oauth.signpost.http; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class HttpParametersTest { @Test public void testBasicBehavior() { HttpParameters params = new HttpParameters(); assertTrue(params.isEmpty()); params.put("a", "5"); assertFalse(params.isEmpty()); assertEquals("5", params.get("a").first()); params.put("a", "1"); assertEquals("a=1&a=5", params.getAsQueryString("a")); params.put("b", "drei"); params.put("b", "vier"); HashMap> other = new HashMap>(); LinkedList values = new LinkedList(); values.add("eins"); other.put("b", values); params.putMap(other); assertEquals(2, params.keySet().size()); assertEquals(5, params.size()); assertEquals("b=drei&b=eins&b=vier", params.getAsQueryString("b")); params.put("a b", "c d", true); assertEquals("a%20b=c%20d", params.getAsQueryString("a b")); assertEquals("c%20d", params.getFirst("a%20b")); assertEquals("c d", params.getFirst("a%20b", true)); assertEquals("x=", params.getAsQueryString("x")); params.clear(); assertTrue(params.isEmpty()); assertEquals(0, params.size()); assertEquals(null, params.get("a")); String[] kvPairs = new String[] { "a", "1", "b", "2" }; params.putAll(kvPairs, false); assertEquals("1", params.getFirst("a")); assertEquals("2", params.getFirst("b")); } @Test public void testGetOAuthParameters() { HttpParameters params = new HttpParameters(); params.put("a", "5"); params.put("oauth_token", "1"); params.put("x_oauth_token", "1"); HttpParameters oauthParams = params.getOAuthParameters(); assertFalse(oauthParams.containsKey("a")); assertTrue(oauthParams.containsKey("oauth_token")); assertTrue(oauthParams.containsKey("x_oauth_token")); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/mocks/000077500000000000000000000000001172235640600270305ustar00rootroot00000000000000DefaultOAuthProviderMock.java000066400000000000000000000024541172235640600344730ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/mockspackage oauth.signpost.mocks; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import oauth.signpost.basic.DefaultOAuthProvider; import oauth.signpost.basic.HttpURLConnectionRequestAdapter; import oauth.signpost.http.HttpRequest; @SuppressWarnings("serial") public class DefaultOAuthProviderMock extends DefaultOAuthProvider implements OAuthProviderMock { private HttpURLConnection connectionMock; public DefaultOAuthProviderMock(String requestTokenUrl, String accessTokenUrl, String websiteUrl) { super(requestTokenUrl, accessTokenUrl, websiteUrl); } public void mockConnection(String responseBody) throws Exception { this.connectionMock = mock(HttpURLConnection.class); InputStream is = new ByteArrayInputStream(responseBody.getBytes()); when(connectionMock.getResponseCode()).thenReturn(200); when(connectionMock.getInputStream()).thenReturn(is); } @Override protected HttpRequest createRequest(String endpointUrl) throws MalformedURLException, IOException { return new HttpURLConnectionRequestAdapter(connectionMock); } } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/mocks/OAuthProviderMock.java000066400000000000000000000002071172235640600332370ustar00rootroot00000000000000package oauth.signpost.mocks; public interface OAuthProviderMock { void mockConnection(String responseBody) throws Exception; } kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/signature/000077500000000000000000000000001172235640600277155ustar00rootroot00000000000000OAuthMessageSignerTest.java000066400000000000000000000032021172235640600350330ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/signaturepackage oauth.signpost.signature; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import oauth.signpost.SignpostTestBase; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class OAuthMessageSignerTest extends SignpostTestBase { @Test public void shouldCreateCorrectPlaintextSignature() throws Exception { OAuthMessageSigner signer = new PlainTextMessageSigner(); signer.setConsumerSecret(CONSUMER_SECRET); signer.setTokenSecret(TOKEN_SECRET); assertEquals(CONSUMER_SECRET + "&" + TOKEN_SECRET, signer.sign(httpGetMock, OAUTH_PARAMS)); } @Test public void shouldComputeCorrectHmacSha1Signature() throws Exception { // based on the reference test case from // http://oauth.pbwiki.com/TestCases OAuthMessageSigner signer = new HmacSha1MessageSigner(); signer.setConsumerSecret(CONSUMER_SECRET); signer.setTokenSecret(TOKEN_SECRET); HttpRequest request = mock(HttpRequest.class); when(request.getRequestUrl()).thenReturn("http://photos.example.net/photos"); when(request.getMethod()).thenReturn("GET"); HttpParameters params = new HttpParameters(); params.putAll(OAUTH_PARAMS); params.put("file", "vacation.jpg"); params.put("size", "original"); assertEquals("tR3+Ty81lMeYAr/Fid0kMTYa/WM=", signer.sign(request, params)); } } SignatureBaseStringTest.java000066400000000000000000000134401172235640600352660ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/signaturepackage oauth.signpost.signature; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import oauth.signpost.SignpostTestBase; import oauth.signpost.http.HttpRequest; import oauth.signpost.http.HttpParameters; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class SignatureBaseStringTest extends SignpostTestBase { @Test public void shouldConsistOf3NonEmptyPartsConcatenatedWithAmpersand() throws Exception { SignatureBaseString sbs = new SignatureBaseString(httpPostMock, OAUTH_PARAMS); String result = sbs.generate(); String[] parts = result.split("&"); assertEquals(3, parts.length); assertNotNull(parts[0]); assertNotNull(parts[1]); assertNotNull(parts[2]); } @Test public void shouldStartWithUppercaseHttpMethod() throws Exception { assertTrue(new SignatureBaseString(httpPostMock, EMPTY_PARAMS).generate().split("&")[0] .equals("POST")); assertTrue(new SignatureBaseString(httpGetMock, EMPTY_PARAMS).generate().split("&")[0] .equals("GET")); } @Test public void shouldNormalizeRequestUrl() throws Exception { // must include scheme and authority in lowercase letters, // plus non HTTP(S) port, plus path, // but must ignore query params and fragment when(httpGetMock.getRequestUrl()) .thenReturn("HTTP://www.Example.Com:123/test?q=1#fragment"); assertEquals("http://www.example.com:123/test", new SignatureBaseString(httpGetMock, OAUTH_PARAMS).normalizeRequestUrl()); // must exclude HTTP(S) default ports when(httpGetMock.getRequestUrl()).thenReturn("http://example.com:80"); assertEquals("http://example.com/", new SignatureBaseString(httpGetMock, EMPTY_PARAMS) .normalizeRequestUrl()); when(httpGetMock.getRequestUrl()).thenReturn("https://example.com:443"); assertEquals("https://example.com/", new SignatureBaseString(httpGetMock, EMPTY_PARAMS) .normalizeRequestUrl()); } @Test public void shouldNormalizeParameters() throws Exception { // should ignore signature, callback, and realm params HttpParameters params = new HttpParameters(); params.put("a", "1", true); params.put("realm", "www.example.com", true); params.put("oauth_signature", "12345", true); String result = new SignatureBaseString(httpGetMock, params).normalizeRequestParameters(); assertEquals("a=1", result); // example from OAuth spec params = new HttpParameters(); params.put("a", "1", true); params.put("c", "hi there", true); params.put("f", "25", true); params.put("f", "50", true); params.put("f", "a", true); params.put("z", "p", true); params.put("z", "t", true); String expected = "a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t"; result = new SignatureBaseString(httpGetMock, params).normalizeRequestParameters(); assertEquals(expected, result); // examples from the official test cases on // http://oauth.pbwiki.com/TestCases params = new HttpParameters(); params.put("a", "x!y", true); params.put("a", "x y", true); expected = "a=x%20y&a=x%21y"; result = new SignatureBaseString(httpGetMock, params).normalizeRequestParameters(); assertEquals(expected, result); params = new HttpParameters(); params.put("name", "", true); assertEquals("name=", new SignatureBaseString(httpGetMock, params) .normalizeRequestParameters()); params.putNull("name", null); assertEquals("name=", new SignatureBaseString(httpGetMock, params) .normalizeRequestParameters()); } @Test public void shouldEncodeAndConcatenateAllSignatureParts() throws Exception { HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getRequestUrl()).thenReturn("http://example.com"); HttpParameters params = new HttpParameters(); params.put("a", "1"); SignatureBaseString sbs = new SignatureBaseString(request, params); //TODO: Is it correct that a trailing slash is always added to the //request URL authority if the path is empty? assertEquals("GET&http%3A%2F%2Fexample.com%2F&a%3D1", sbs.generate()); } @Test public void shouldWorkWithBracketsInParameterName() throws Exception { HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getRequestUrl()).thenReturn("http://examplebrackets.com"); HttpParameters params = new HttpParameters(); params.put("a[]", "1", true); SignatureBaseString sbs = new SignatureBaseString(request, params); assertEquals("GET&http%3A%2F%2Fexamplebrackets.com%2F&a%255B%255D%3D1", sbs.generate()); } @Test public void shouldWorkWithMultipleParametersWithBracketsOfSameName() throws Exception { HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getRequestUrl()).thenReturn("http://examplemultiple.com"); HttpParameters params = new HttpParameters(); params.put("a[]", "1", true); params.put("a[]", "2", true); SignatureBaseString sbs = new SignatureBaseString(request, params); assertEquals("GET&http%3A%2F%2Fexamplemultiple.com%2F&a%255B%255D%3D1%26a%255B%255D%3D2", sbs.generate()); } } SigningStrategyTest.java000066400000000000000000000033441172235640600344660ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-core/src/test/java/oauth/signpost/signaturepackage oauth.signpost.signature; import static org.junit.Assert.assertEquals; import oauth.signpost.SignpostTestBase; import oauth.signpost.http.HttpParameters; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; @RunWith(MockitoJUnit44Runner.class) public class SigningStrategyTest extends SignpostTestBase { @Test public void testDifferentSigningStrategies() throws Exception { SigningStrategy strategy = null; String signature = "123"; HttpParameters params = new HttpParameters(); params.put("realm", "http://x.com"); params.put("oauth_token", "abc"); params.put("x_oauth_custom_param", "cde"); params.put("should_not_appear", "nono"); strategy = new AuthorizationHeaderSigningStrategy(); assertEquals( "OAuth realm=\"http://x.com\", oauth_signature=\"123\", oauth_token=\"abc\", x_oauth_custom_param=\"cde\"", strategy.writeSignature(signature, httpGetMock, params)); assertEquals( "OAuth realm=\"http://x.com\", oauth_signature=\"123\", oauth_token=\"abc\", x_oauth_custom_param=\"cde\"", strategy.writeSignature(signature, httpGetMockWithQueryString, params)); strategy = new QueryStringSigningStrategy(); assertEquals( "http://www.example.com?oauth_signature=123&oauth_token=abc&x_oauth_custom_param=cde", strategy.writeSignature(signature, httpGetMock, params)); assertEquals( "http://www.example.com?foo=bar&oauth_signature=123&oauth_token=abc&x_oauth_custom_param=cde", strategy.writeSignature(signature, httpGetMockWithQueryString, params)); } } kaeppler-signpost-653c361/signpost-jetty6/000077500000000000000000000000001172235640600205345ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/.classpath000066400000000000000000000006651172235640600225260ustar00rootroot00000000000000 kaeppler-signpost-653c361/signpost-jetty6/.project000066400000000000000000000010421172235640600222000ustar00rootroot00000000000000 signpost-jetty6 org.eclipse.jdt.core.javabuilder org.maven.ide.eclipse.maven2Builder org.eclipse.jdt.core.javanature org.maven.ide.eclipse.maven2Nature kaeppler-signpost-653c361/signpost-jetty6/.settings/000077500000000000000000000000001172235640600224525ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/.settings/org.eclipse.jdt.ui.prefs000066400000000000000000000001411172235640600271150ustar00rootroot00000000000000#Sun Jun 07 00:56:25 CEST 2009 eclipse.preferences.version=1 internal.default.compliance=default kaeppler-signpost-653c361/signpost-jetty6/.settings/org.maven.ide.eclipse.prefs000066400000000000000000000004071172235640600275730ustar00rootroot00000000000000#Sun Jun 07 00:01:04 CEST 2009 activeProfiles= eclipse.preferences.version=1 fullBuildGoals=process-test-resources includeModules=false resolveWorkspaceProjects=true resourceFilterGoals=process-resources resources\:testResources skipCompilerPlugin=true version=1 kaeppler-signpost-653c361/signpost-jetty6/pom.xml000066400000000000000000000023311172235640600220500ustar00rootroot00000000000000 oauth-signpost oauth.signpost 1.2.1.2 4.0.0 signpost-jetty6 signpost-jetty6 oauth.signpost signpost-core ${project.version} compile org.mortbay.jetty jetty-client 6.1.18 compile oauth.signpost signpost-core ${project.version} test-jar test maven-antrun-plugin kaeppler-signpost-653c361/signpost-jetty6/src/000077500000000000000000000000001172235640600213235ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/000077500000000000000000000000001172235640600222475ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/java/000077500000000000000000000000001172235640600231705ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/java/oauth/000077500000000000000000000000001172235640600243105ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/java/oauth/signpost/000077500000000000000000000000001172235640600261565ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/java/oauth/signpost/jetty/000077500000000000000000000000001172235640600273155ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/main/java/oauth/signpost/jetty/HttpRequestAdapter.java000066400000000000000000000057451172235640600337640ustar00rootroot00000000000000/* * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package oauth.signpost.jetty; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import oauth.signpost.http.HttpRequest; import org.mortbay.jetty.HttpFields; import org.mortbay.jetty.HttpFields.Field; import org.mortbay.jetty.client.HttpExchange; public class HttpRequestAdapter implements HttpRequest { private HttpExchange request; private String requestUrl; public HttpRequestAdapter(HttpExchange request) { this.request = request; buildRequestUrl(); } public String getContentType() { HttpFields fields = request.getRequestFields(); return fields.getStringField("Content-Type"); } public InputStream getMessagePayload() throws IOException { return new ByteArrayInputStream(request.getRequestContent().array()); } public String getMethod() { return request.getMethod(); } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String url) { throw new RuntimeException(new UnsupportedOperationException()); } public void setHeader(String name, String value) { request.setRequestHeader(name, value); } public String getHeader(String name) { HttpFields fields = request.getRequestFields(); return fields.getStringField(name); } @SuppressWarnings("unchecked") public Map getAllHeaders() { HttpFields fields = request.getRequestFields(); Iterator iter = fields.getFields(); HashMap headers = new HashMap(); while (iter.hasNext()) { Field field = (Field) iter.next(); headers.put(field.getName(), field.getValue()); } return headers; } // Jetty has some very weird mechanism for handling URLs... we have to // reconstruct it here. private void buildRequestUrl() { StringBuilder sb = new StringBuilder(); sb.append(request.getScheme() + "://"); sb.append(request.getAddress().toString().replaceAll(":\\d+", "")); if (request.getURI() != null) { // the "URI" in Jetty is actually the path... WTF?! sb.append(request.getURI()); } this.requestUrl = sb.toString(); } public Object unwrap() { return request; } } kaeppler-signpost-653c361/signpost-jetty6/src/main/java/oauth/signpost/jetty/JettyOAuthConsumer.java000066400000000000000000000021721172235640600337360ustar00rootroot00000000000000/* Copyright (c) 2009 Matthias Kaeppler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package oauth.signpost.jetty; import oauth.signpost.AbstractOAuthConsumer; import oauth.signpost.http.HttpRequest; import org.mortbay.jetty.client.HttpExchange; public class JettyOAuthConsumer extends AbstractOAuthConsumer { private static final long serialVersionUID = 1L; public JettyOAuthConsumer(String consumerKey, String consumerSecret) { super(consumerKey, consumerSecret); } @Override protected HttpRequest wrap(Object request) { return new HttpRequestAdapter((HttpExchange) request); } } kaeppler-signpost-653c361/signpost-jetty6/src/test/000077500000000000000000000000001172235640600223025ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/test/java/000077500000000000000000000000001172235640600232235ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/test/java/oauth/000077500000000000000000000000001172235640600243435ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/test/java/oauth/signpost/000077500000000000000000000000001172235640600262115ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/test/java/oauth/signpost/jetty/000077500000000000000000000000001172235640600273505ustar00rootroot00000000000000HttpRequestAdapterTest.java000066400000000000000000000014101172235640600345610ustar00rootroot00000000000000kaeppler-signpost-653c361/signpost-jetty6/src/test/java/oauth/signpost/jettypackage oauth.signpost.jetty; import oauth.signpost.basic.HttpRequestAdapterTestBase; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnit44Runner; import org.mortbay.io.ByteArrayBuffer; import org.mortbay.jetty.client.HttpExchange; @RunWith(MockitoJUnit44Runner.class) public class HttpRequestAdapterTest extends HttpRequestAdapterTestBase { @Override public void prepareRequest() throws Exception { HttpExchange r = new HttpExchange(); r.setMethod(HTTP_POST_METHOD); r.setURL(URL); r.addRequestHeader(HEADER_NAME, HEADER_VALUE); r.addRequestHeader("Content-Type", CONTENT_TYPE); r.setRequestContent(new ByteArrayBuffer(PAYLOAD.getBytes())); request = new HttpRequestAdapter(r); } }