├── lib ├── .gitignore ├── httpclient-4.0.jar ├── httpcore-4.0.1.jar └── xmlpull_1_1_3_4c.jar ├── .gitignore ├── local.properties.example ├── src ├── custom │ ├── META-INF │ │ └── services │ │ │ └── com.kenai.jbosh.HTTPSender │ ├── de │ │ └── measite │ │ │ └── smack │ │ │ ├── SaslClientFactory.java │ │ │ ├── Sasl.java │ │ │ └── AndroidDebugger.java │ ├── org │ │ └── jivesoftware │ │ │ └── smack │ │ │ └── util │ │ │ └── DNSUtil.java │ └── com │ │ └── kenai │ │ └── jbosh │ │ └── QName.java ├── .gitignore └── novell-openldap-jldap │ └── com │ └── novell │ └── sasl │ └── client │ ├── ParsedDirective.java │ ├── ResponseAuth.java │ ├── TokenParser.java │ ├── DirectiveList.java │ ├── DigestChallenge.java │ └── DigestMD5SaslClient.java ├── patch ├── 32-remove-jbosh-xlightweb.sh ├── 31-remove-dns-wrapper.sh ├── 40-enable-custom-sasl.sh ├── 21-remove-unused-smack.sh ├── 11-remove-nls-harmony.sh ├── 10-remove-unused-harmony.sh ├── 20-remove-mxparser.sh ├── buddycloud │ ├── README │ ├── 10-PubSubManager-non-final.patch │ ├── 30-PubSubNode-protected-constructor.patch │ └── 20-PubSubNode-getAllSubscriptions.patch ├── 45-protected-xmpp-socket.patch ├── 34-pin-jbosh-http-sender.patch ├── 00-relocate-javax.security.sh ├── 35-pin-jbosh-boddy-parser.patch ├── 41-fix-digest-md5.patch ├── 24-disable-sasl-methods.patch ├── 30-swtich-debugging-implementations.patch ├── 23-strip-unused-xml-transform.patch ├── 13-mock-configuration.patch ├── 22-remove-beans.Property-deps.patch ├── 12-harmony-nls-msg.patch └── 33-jbosh-android.patch ├── README └── LICENCE /lib/.gitignore: -------------------------------------------------------------------------------- 1 | android-*.jar 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | local.properties 3 | -------------------------------------------------------------------------------- /local.properties.example: -------------------------------------------------------------------------------- 1 | sdk-location=${user.home}/android/ 2 | -------------------------------------------------------------------------------- /src/custom/META-INF/services/com.kenai.jbosh.HTTPSender: -------------------------------------------------------------------------------- 1 | com.kenai.jbosh.ApacheHTTPSender 2 | -------------------------------------------------------------------------------- /lib/httpclient-4.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtreffer/asmack/HEAD/lib/httpclient-4.0.jar -------------------------------------------------------------------------------- /lib/httpcore-4.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtreffer/asmack/HEAD/lib/httpcore-4.0.1.jar -------------------------------------------------------------------------------- /lib/xmlpull_1_1_3_4c.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtreffer/asmack/HEAD/lib/xmlpull_1_1_3_4c.jar -------------------------------------------------------------------------------- /patch/32-remove-jbosh-xlightweb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm com/kenai/jbosh/XLightWeb*.java 4 | 5 | -------------------------------------------------------------------------------- /patch/31-remove-dns-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm org/xbill/DNS/spi/DNSJavaNameServiceDescriptor.java 4 | 5 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | dnsjava 2 | harmony-trunk 3 | qpid-trunk 4 | smack-trunk 5 | smack 6 | qpid 7 | harmony 8 | jbosh 9 | -------------------------------------------------------------------------------- /patch/40-enable-custom-sasl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find -name '*.java' -exec sed -i 's:import org.apache.harmony.javax.security.sasl.Sasl;:import de.measite.smack.Sasl;:g' '{}' ';' 4 | 5 | -------------------------------------------------------------------------------- /patch/21-remove-unused-smack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm org/jivesoftware/smack/debugger/LiteDebugger.java 4 | rm org/jivesoftware/smackx/debugger/EnhancedDebugger.java 5 | rm org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java 6 | 7 | -------------------------------------------------------------------------------- /patch/11-remove-nls-harmony.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find org/apache/harmony -name '*.java' -exec sed -i 's:import org.apache.harmony.auth.internal.nls.Messages;::' '{}' ';' 4 | find org/apache/harmony -name '*.java' -exec sed -i 's:Messages.getString(\("[^"]*"\)):\1:g' '{}' ';' 5 | 6 | -------------------------------------------------------------------------------- /patch/10-remove-unused-harmony.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf org/ietf/ 4 | 5 | rm -rf org/apache/harmony/auth 6 | rm -rf org/apache/harmony/javax/security/auth/kerberos/ 7 | rm -rf org/apache/harmony/javax/security/auth/x500/ 8 | 9 | rm org/apache/harmony/javax/security/auth/Policy.java 10 | 11 | -------------------------------------------------------------------------------- /patch/20-remove-mxparser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find org/jivesoftware -name '*.java' -exec sed -i 's:import org.xmlpull.mxp1.MXParser:import org.xmlpull.v1.XmlPullParserFactory:' '{}' ';' 4 | find org/jivesoftware -name '*.java' -exec sed -i 's:new MXParser():XmlPullParserFactory.newInstance().newPullParser():g' '{}' ';' 5 | 6 | -------------------------------------------------------------------------------- /patch/buddycloud/README: -------------------------------------------------------------------------------- 1 | This is an example of custom jar building. 2 | 3 | This sample addes a few patches to the pubsub engine. 4 | This leads to the -buddycloud jar used by http://code.google.com/p/buddycloud/ 5 | 6 | Feel free to create your custom spinoffs, 7 | http://www.igniterealtime.org/issues/browse/SMACK 8 | and the community pages are great places to look for patches :-) 9 | 10 | -------------------------------------------------------------------------------- /patch/45-protected-xmpp-socket.patch: -------------------------------------------------------------------------------- 1 | --- org/jivesoftware/smack/XMPPConnection.java 2010-02-13 11:13:16.478541616 +0100 2 | +++ org/jivesoftware/smack/XMPPConnection.java 2010-02-13 11:58:49.798590947 +0100 3 | @@ -55,7 +55,7 @@ 4 | /** 5 | * The socket which is used for this connection. 6 | */ 7 | - Socket socket; 8 | + protected Socket socket; 9 | 10 | String connectionID = null; 11 | private String user = null; 12 | -------------------------------------------------------------------------------- /patch/34-pin-jbosh-http-sender.patch: -------------------------------------------------------------------------------- 1 | --- com/kenai/jbosh/BOSHClient.java 2010-01-13 20:57:36.000000000 +0100 2 | +++ com/kenai/jbosh/BOSHClient.java 2010-02-04 17:40:45.678169320 +0100 3 | @@ -232,7 +232,7 @@ 4 | * HTTPSender instance. 5 | */ 6 | private final HTTPSender httpSender = 7 | - ServiceLib.loadService(HTTPSender.class); 8 | + new ApacheHTTPSender(); 9 | 10 | /** 11 | * Storage for test hook implementation. 12 | -------------------------------------------------------------------------------- /patch/00-relocate-javax.security.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p org/apache/harmony/ 4 | mv javax org/apache/harmony/ 5 | find org/apache/harmony/ -name '*.java' -exec sed -i 's:package javax:package org.apache.harmony.javax:g' '{}' ';' 6 | find -name '*.java' -exec sed -i 's:import javax.security.sasl:import org.apache.harmony.javax.security.sasl:g' '{}' ';' 7 | find -name '*.java' -exec sed -i 's:import javax.security.auth:import org.apache.harmony.javax.security.auth:g' '{}' ';' 8 | 9 | -------------------------------------------------------------------------------- /patch/35-pin-jbosh-boddy-parser.patch: -------------------------------------------------------------------------------- 1 | --- com/kenai/jbosh/StaticBody.java 2010-01-13 20:57:36.000000000 +0100 2 | +++ com/kenai/jbosh/StaticBody.java 2010-02-04 17:59:06.037770746 +0100 3 | @@ -42,7 +42,7 @@ 4 | * Selected parser to be used to process raw XML messages. 5 | */ 6 | private static final BodyParser PARSER = 7 | - ServiceLib.loadService(BodyParser.class); 8 | + new BodyParserXmlPull(); 9 | 10 | /** 11 | * Size of the internal buffer when copying from a stream. 12 | -------------------------------------------------------------------------------- /patch/buddycloud/10-PubSubManager-non-final.patch: -------------------------------------------------------------------------------- 1 | Index: org/jivesoftware/smackx/pubsub/PubSubManager.java 2 | =================================================================== 3 | --- org/jivesoftware/smackx/pubsub/PubSubManager.java (revision 11464) 4 | +++ org/jivesoftware/smackx/pubsub/PubSubManager.java (working copy) 5 | @@ -41,7 +41,7 @@ 6 | * 7 | * @author Robin Collier 8 | */ 9 | -final public class PubSubManager 10 | +public class PubSubManager 11 | { 12 | private XMPPConnection con; 13 | private String to; 14 | -------------------------------------------------------------------------------- /patch/buddycloud/30-PubSubNode-protected-constructor.patch: -------------------------------------------------------------------------------- 1 | Index: org/jivesoftware/smackx/pubsub/Node.java 2 | =================================================================== 3 | --- org/jivesoftware/smackx/pubsub/Node.java (revision 11464) 4 | +++ org/jivesoftware/smackx/pubsub/Node.java (working copy) 5 | @@ -48,7 +48,7 @@ 6 | * @param connection The connection the node is associated with 7 | * @param nodeName The node id 8 | */ 9 | - Node(Connection connection, String nodeName) 10 | + protected Node(Connection connection, String nodeName) 11 | { 12 | con = connection; 13 | id = nodeName; 14 | -------------------------------------------------------------------------------- /patch/41-fix-digest-md5.patch: -------------------------------------------------------------------------------- 1 | --- ../../src-unpatched/trunk/com/novell/sasl/client/DigestMD5SaslClient.java 2009-12-07 19:14:10.000000000 +0100 2 | +++ com/novell/sasl/client/DigestMD5SaslClient.java 2009-12-07 19:19:07.000000000 +0100 3 | @@ -673,8 +673,8 @@ 4 | digestResponse.append("00000001"); //nounce count 5 | digestResponse.append(",qop="); 6 | digestResponse.append(m_qopValue); 7 | - digestResponse.append(",digest-uri=\"ldap/"); 8 | - digestResponse.append(m_serverName); 9 | + digestResponse.append(",digest-uri=\""); 10 | + digestResponse.append(m_digestURI); 11 | digestResponse.append("\",response="); 12 | digestResponse.append(response); 13 | digestResponse.append(",charset=utf-8,nonce=\""); 14 | -------------------------------------------------------------------------------- /patch/24-disable-sasl-methods.patch: -------------------------------------------------------------------------------- 1 | --- ../../src-unpatched/trunk/org/jivesoftware/smack/SASLAuthentication.java 2009-12-07 00:03:46.000000000 +0100 2 | +++ org/jivesoftware/smack/SASLAuthentication.java 2009-12-07 00:05:46.000000000 +0100 3 | @@ -91,11 +91,11 @@ 4 | registerSASLMechanism("PLAIN", SASLPlainMechanism.class); 5 | registerSASLMechanism("ANONYMOUS", SASLAnonymous.class); 6 | 7 | - supportSASLMechanism("GSSAPI",0); 8 | - supportSASLMechanism("DIGEST-MD5",1); 9 | - supportSASLMechanism("CRAM-MD5",2); 10 | - supportSASLMechanism("PLAIN",3); 11 | - supportSASLMechanism("ANONYMOUS",4); 12 | +// supportSASLMechanism("GSSAPI",0); 13 | + supportSASLMechanism("DIGEST-MD5",0); 14 | +// supportSASLMechanism("CRAM-MD5",2); 15 | + supportSASLMechanism("PLAIN",1); 16 | + supportSASLMechanism("ANONYMOUS",2); 17 | 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /patch/30-swtich-debugging-implementations.patch: -------------------------------------------------------------------------------- 1 | --- ../../../src/smack/org/jivesoftware/smack/Connection.java 2010-02-03 23:29:21.000000000 +0100 2 | +++ org/jivesoftware/smack/Connection.java 2010-02-03 23:56:56.000000000 +0100 3 | @@ -743,12 +743,12 @@ 4 | if (debuggerClass == null) { 5 | try { 6 | debuggerClass = 7 | - Class.forName("org.jivesoftware.smackx.debugger.EnhancedDebugger"); 8 | + Class.forName("de.measite.smack.AndroidDebugger"); 9 | } 10 | catch (Exception ex) { 11 | try { 12 | debuggerClass = 13 | - Class.forName("org.jivesoftware.smack.debugger.LiteDebugger"); 14 | + Class.forName("org.jivesoftware.smack.debugger.ConsoleDebugger"); 15 | } 16 | catch (Exception ex2) { 17 | ex2.printStackTrace(); 18 | -------------------------------------------------------------------------------- /patch/23-strip-unused-xml-transform.patch: -------------------------------------------------------------------------------- 1 | --- org/jivesoftware/smackx/pubsub/util/XmlUtils.java 2 | +++ org/jivesoftware/smackx/pubsub/util/XmlUtils.java 3 | @@ -28,32 +28,6 @@ import javax.xml.transform.stream.StreamSource; 4 | */ 5 | public class XmlUtils 6 | { 7 | - /** 8 | - * 9 | - * @param header Just a title for the stanza for readability. Single word no spaces since 10 | - * it is inserted as the root element in the output. 11 | - * @param xml The string to pretty print 12 | - */ 13 | - static public void prettyPrint(String header, String xml) 14 | - { 15 | - try 16 | - { 17 | - Transformer transformer = TransformerFactory.newInstance().newTransformer(); 18 | - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 19 | - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3"); 20 | - 21 | - if (header != null) 22 | - { 23 | - xml = "\n<" + header + ">" + xml + "'; 24 | - } 25 | - transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out)); 26 | - } 27 | - catch (Exception e) 28 | - { 29 | - System.out.println("Something wrong with xml in \n---------------\n" + xml + "\n---------------"); 30 | - e.printStackTrace(); 31 | - } 32 | - } 33 | 34 | static public void appendAttribute(StringBuilder builder, String att, String value) 35 | { 36 | -------------------------------------------------------------------------------- /patch/13-mock-configuration.patch: -------------------------------------------------------------------------------- 1 | diff -ur ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/login/Configuration.java ./org/apache/harmony/javax/security/auth/login/Configuration.java 2 | --- ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/login/Configuration.java 2009-12-06 18:40:25.000000000 +0100 3 | +++ ./org/apache/harmony/javax/security/auth/login/Configuration.java 2009-12-06 18:44:23.000000000 +0100 4 | @@ -20,8 +20,6 @@ 5 | import java.security.AccessController; 6 | import org.apache.harmony.javax.security.auth.AuthPermission; 7 | 8 | -import org.apache.harmony.security.fortress.PolicyUtils; 9 | - 10 | public abstract class Configuration { 11 | 12 | // the current configuration 13 | @@ -56,8 +54,18 @@ 14 | * exception, wraps it with SecurityException and throws further. 15 | */ 16 | private static final Configuration getDefaultProvider() { 17 | - return AccessController.doPrivileged(new PolicyUtils.ProviderLoader( 18 | - LOGIN_CONFIGURATION_PROVIDER, Configuration.class)); 19 | + return new Configuration() { 20 | + 21 | + @Override 22 | + public void refresh() { 23 | + } 24 | + 25 | + @Override 26 | + public AppConfigurationEntry[] getAppConfigurationEntry( 27 | + String applicationName) { 28 | + return new AppConfigurationEntry[0]; 29 | + } 30 | + }; 31 | } 32 | 33 | /** 34 | -------------------------------------------------------------------------------- /patch/22-remove-beans.Property-deps.patch: -------------------------------------------------------------------------------- 1 | --- ../../src-unpatched/trunk/org/jivesoftware/smack/util/PacketParserUtils.java 2009-12-06 19:45:45.000000000 +0100 2 | +++ org/jivesoftware/smack/util/PacketParserUtils.java 2009-12-06 19:48:13.000000000 +0100 3 | @@ -25,7 +25,6 @@ 4 | import org.jivesoftware.smack.provider.ProviderManager; 5 | import org.xmlpull.v1.XmlPullParser; 6 | 7 | -import java.beans.PropertyDescriptor; 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ObjectInputStream; 10 | import java.util.ArrayList; 11 | @@ -433,14 +432,14 @@ 12 | if (eventType == XmlPullParser.START_TAG) { 13 | String name = parser.getName(); 14 | String stringValue = parser.nextText(); 15 | - PropertyDescriptor descriptor = new PropertyDescriptor(name, objectClass); 16 | - // Load the class type of the property. 17 | - Class propertyType = descriptor.getPropertyType(); 18 | + Class propertyType = object.getClass().getMethod( 19 | + "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType(); 20 | // Get the value of the property by converting it from a 21 | // String to the correct object type. 22 | Object value = decode(propertyType, stringValue); 23 | // Set the value of the bean. 24 | - descriptor.getWriteMethod().invoke(object, value); 25 | + object.getClass().getMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), propertyType) 26 | + .invoke(object, value); 27 | } 28 | else if (eventType == XmlPullParser.END_TAG) { 29 | if (parser.getName().equals(elementName)) { 30 | 31 | -------------------------------------------------------------------------------- /patch/buddycloud/20-PubSubNode-getAllSubscriptions.patch: -------------------------------------------------------------------------------- 1 | Index: org/jivesoftware/smackx/pubsub/PubSubElementType.java 2 | =================================================================== 3 | --- org/jivesoftware/smackx/pubsub/PubSubElementType.java (revision 11464) 4 | +++ org/jivesoftware/smackx/pubsub/PubSubElementType.java (working copy) 5 | @@ -41,6 +41,7 @@ 6 | SUBSCRIBE("subscribe", PubSubNamespace.BASIC), 7 | SUBSCRIPTION("subscription", PubSubNamespace.BASIC), 8 | SUBSCRIPTIONS("subscriptions", PubSubNamespace.BASIC), 9 | + SUBSCRIPTIONS_OWNER("subscriptions", PubSubNamespace.OWNER), 10 | UNSUBSCRIBE("unsubscribe", PubSubNamespace.BASIC); 11 | 12 | private String eName; 13 | Index: org/jivesoftware/smackx/pubsub/Node.java 14 | =================================================================== 15 | --- org/jivesoftware/smackx/pubsub/Node.java (revision 11464) 16 | +++ org/jivesoftware/smackx/pubsub/Node.java (working copy) 17 | @@ -131,6 +131,22 @@ 18 | } 19 | 20 | /** 21 | + * Get all subscriptions currently associated with this node. 22 | + * This includes foreign jids and uses the #owner namespace. 23 | + * 24 | + * @return List of {@link Subscription} 25 | + * 26 | + * @trhows XMPPException 27 | + */ 28 | + public List getAllSubscriptions() 29 | + throws XMPPException 30 | + { 31 | + PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS_OWNER, getId()), PubSubNamespace.OWNER); 32 | + SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS_OWNER); 33 | + return subElem.getSubscriptions(); 34 | + } 35 | + 36 | + /** 37 | * The user subscribes to the node using the supplied jid. The 38 | * bare jid portion of this one must match the jid for the connection. 39 | * 40 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/ParsedDirective.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/ParsedDirective.java,v 1.1 2003/08/21 10:06:26 kkanil Exp $ 3 | * 4 | * Copyright (C) 2002 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | /** 18 | * Implements the ParsedDirective class which will be used in the 19 | * DigestMD5SaslClient mechanism. 20 | */ 21 | class ParsedDirective 22 | { 23 | public static final int QUOTED_STRING_VALUE = 1; 24 | public static final int TOKEN_VALUE = 2; 25 | 26 | private int m_valueType; 27 | private String m_name; 28 | private String m_value; 29 | 30 | ParsedDirective( 31 | String name, 32 | String value, 33 | int type) 34 | { 35 | m_name = name; 36 | m_value = value; 37 | m_valueType = type; 38 | } 39 | 40 | String getValue() 41 | { 42 | return m_value; 43 | } 44 | 45 | String getName() 46 | { 47 | return m_name; 48 | } 49 | 50 | int getValueType() 51 | { 52 | return m_valueType; 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/custom/de/measite/smack/SaslClientFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 Rene Treffer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package de.measite.smack; 18 | 19 | import java.util.Map; 20 | 21 | import com.novell.sasl.client.DigestMD5SaslClient; 22 | 23 | import org.apache.harmony.javax.security.auth.callback.CallbackHandler; 24 | import org.apache.harmony.javax.security.sasl.SaslClient; 25 | import org.apache.harmony.javax.security.sasl.SaslException; 26 | import org.apache.qpid.management.common.sasl.PlainSaslClient; 27 | 28 | public class SaslClientFactory implements 29 | org.apache.harmony.javax.security.sasl.SaslClientFactory { 30 | 31 | @Override 32 | public SaslClient createSaslClient(String[] mechanisms, 33 | String authorizationId, String protocol, String serverName, 34 | Map props, CallbackHandler cbh) throws SaslException { 35 | for (String mech: mechanisms) { 36 | if ("PLAIN".equals(mech)) { 37 | return new PlainSaslClient(authorizationId, cbh); 38 | } else 39 | if ("DIGEST-MD5".equals(mech)) { 40 | return DigestMD5SaslClient.getClient( 41 | authorizationId, 42 | protocol, 43 | serverName, 44 | props, 45 | cbh 46 | ); 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | @Override 53 | public String[] getMechanismNames(Map props) { 54 | return new String[]{ 55 | "PLAIN", 56 | "DIGEST-MD5" 57 | }; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | aSmack - buildsystem for Smack on Android 2 | ========================================= 3 | 4 | *This repository doesn't contain much code, it's a build environment!* 5 | 6 | Tracking trunk can be hard. Doing massive changes on top of trunk can be 7 | near impossible. We are mixing 6 open source projects to provide a working 8 | xmpp library for Android. All trunk-based. 9 | 10 | This repository contains a source fetching, patching and building script. 11 | As well as all the minor changes to make an Android version fly. 12 | See the patches/ folder for a detailed list of changes and scripts. 13 | 14 | Contribution 15 | ============ 16 | 17 | The easiest way to contribute is fork & pull request. You may also ask about 18 | direct project access. Mind that minor changes can be applied to the smack 19 | repository. You'll most likly want to help out on smack, not on aSmack. 20 | 21 | Contributors 22 | ============ 23 | 24 | We do not keep a seperate CONTRIBUTORS file, and we discourage @author tags. 25 | However you're free to add your full name to every git commit, and we will 26 | preserver this. Let us know if you've helped on non-technical stuff and we'll 27 | find a way to give you the deserved credit. 28 | 29 | Reporting Problems / Debugging 30 | ============================== 31 | 32 | We always provide source zips. Attach them to the jar in your favorite IDE. 33 | Enable debugging mode (BOSH/XMPPConnection.DEBUG and config.setDebug) 34 | Record a logcat 35 | Remove your credentials (usually a base64 block inside ) 36 | 37 | Your issue should contain 38 | 1. a logcat 39 | 2. a server to reproduce 40 | 3. the code you are using (for fl/oss project we'll accept reposituroy URLs) 41 | 42 | There is no guarantee that we will reply immediatly. But we will try to 43 | investigate the problem. 44 | 45 | Problems that would disclose projects can be to treffer(?)measite.de 46 | Please add asmack and confidential in the subject, and only use this if the 47 | tracker isn't an option for you. 48 | 49 | Releases 50 | ======== 51 | 52 | Releases are usually build every 1-2 weeks, depending on changes and 53 | volunteers time. 54 | 55 | Licences / Used libraries 56 | ========================= 57 | 58 | We only accept Apache and BSD-like licences. 59 | We are currently using code from 60 | 61 | * Apache Harmony (sasl/xml) (Apache Licence) 62 | * smack (xmpp) (Apache Licence) 63 | * novell-openldap-jldap (sasl) ( [OpenLDAP Licence] [1] ) 64 | * Apache qpid (sasl) (Apache Licence) 65 | * jbosh (BOSH) (Apache Licence) 66 | * dnsjava (dns srv lookups) (BSD) 67 | * custom code (various glue stuff) (WTFPL | BSD | Apache) 68 | 69 | This should work for just about every project. Contact us if you have problems 70 | with the licence. 71 | 72 | [1]: http://www.openldap.org/devel/cvsweb.cgi/~checkout~/LICENSE?rev=1.23.2.1&hideattic=1&sortbydate=0 "OpenLDAP Licence" 73 | 74 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/ResponseAuth.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/ResponseAuth.java,v 1.3 2005/01/17 15:00:54 sunilk Exp $ 3 | * 4 | * Copyright (C) 2002 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | import java.util.*; 18 | import org.apache.harmony.javax.security.sasl.*; 19 | 20 | /** 21 | * Implements the ResponseAuth class used by the DigestMD5SaslClient mechanism 22 | */ 23 | class ResponseAuth 24 | { 25 | 26 | private String m_responseValue; 27 | 28 | ResponseAuth( 29 | byte[] responseAuth) 30 | throws SaslException 31 | { 32 | m_responseValue = null; 33 | 34 | DirectiveList dirList = new DirectiveList(responseAuth); 35 | try 36 | { 37 | dirList.parseDirectives(); 38 | checkSemantics(dirList); 39 | } 40 | catch (SaslException e) 41 | { 42 | } 43 | } 44 | 45 | /** 46 | * Checks the semantics of the directives in the directive list as parsed 47 | * from the digest challenge byte array. 48 | * 49 | * @param dirList the list of directives parsed from the digest challenge 50 | * 51 | * @exception SaslException If a semantic error occurs 52 | */ 53 | void checkSemantics( 54 | DirectiveList dirList) throws SaslException 55 | { 56 | Iterator directives = dirList.getIterator(); 57 | ParsedDirective directive; 58 | String name; 59 | 60 | while (directives.hasNext()) 61 | { 62 | directive = (ParsedDirective)directives.next(); 63 | name = directive.getName(); 64 | if (name.equals("rspauth")) 65 | m_responseValue = directive.getValue(); 66 | } 67 | 68 | /* post semantic check */ 69 | if (m_responseValue == null) 70 | throw new SaslException("Missing response-auth directive."); 71 | } 72 | 73 | /** 74 | * returns the ResponseValue 75 | * 76 | * @return the ResponseValue as a String. 77 | */ 78 | public String getResponseValue() 79 | { 80 | return m_responseValue; 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /patch/12-harmony-nls-msg.patch: -------------------------------------------------------------------------------- 1 | diff -ur ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/login/LoginContext.java ./org/apache/harmony/javax/security/auth/login/LoginContext.java 2 | --- ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/login/LoginContext.java 2009-12-06 15:31:24.000000000 +0100 3 | +++ ./org/apache/harmony/javax/security/auth/login/LoginContext.java 2009-12-06 15:36:20.000000000 +0100 4 | @@ -161,7 +161,7 @@ 5 | } 6 | entries = config.getAppConfigurationEntry("other"); //$NON-NLS-1$ 7 | if (entries == null) { 8 | - throw new LoginException(Messages.getString("auth.35", name)); //$NON-NLS-1$ 9 | + throw new LoginException("auth.35 " + name); //$NON-NLS-1$ 10 | } 11 | } 12 | 13 | @@ -524,8 +524,8 @@ 14 | try { 15 | klass = Class.forName(klassName, false, contextClassLoader); 16 | } catch (ClassNotFoundException ex) { 17 | - throw (LoginException) new LoginException(Messages.getString( 18 | - "auth.39", klassName)).initCause(ex); //$NON-NLS-1$ 19 | + throw (LoginException) new LoginException( 20 | + "auth.39 " + klassName).initCause(ex); //$NON-NLS-1$ 21 | } 22 | } 23 | 24 | @@ -533,12 +533,12 @@ 25 | try { 26 | module = (LoginModule) klass.newInstance(); 27 | } catch (IllegalAccessException ex) { 28 | - throw (LoginException) new LoginException(Messages.getString( 29 | - "auth.3A", klassName)) //$NON-NLS-1$ 30 | + throw (LoginException) new LoginException( 31 | + "auth.3A " + klassName) //$NON-NLS-1$ 32 | .initCause(ex); 33 | } catch (InstantiationException ex) { 34 | - throw (LoginException) new LoginException(Messages.getString( 35 | - "auth.3A", klassName)) //$NON-NLS-1$ 36 | + throw (LoginException) new LoginException( 37 | + "auth.3A" + klassName) //$NON-NLS-1$ 38 | .initCause(ex); 39 | } 40 | module.initialize(subject, callbackHandler, sharedState, entry.getOptions()); 41 | diff -ur ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/Subject.java ./org/apache/harmony/javax/security/auth/Subject.java 42 | --- ../../src-unpatched/trunk/org/apache/harmony/javax/security/auth/Subject.java 2009-12-06 15:31:24.000000000 +0100 43 | +++ ./org/apache/harmony/javax/security/auth/Subject.java 2009-12-06 15:32:10.000000000 +0100 44 | @@ -669,7 +669,7 @@ 45 | 46 | if (!c.isAssignableFrom(o.getClass())) { 47 | throw new IllegalArgumentException( 48 | - Messages.getString("auth.0C", c.getName())); //$NON-NLS-1$ 49 | + "auth.0C " + c.getName()); //$NON-NLS-1$ 50 | } 51 | 52 | if (elements.contains(o)) { 53 | @@ -779,4 +779,4 @@ 54 | } 55 | } 56 | } 57 | -} 58 | \ No newline at end of file 59 | +} 60 | -------------------------------------------------------------------------------- /src/custom/de/measite/smack/Sasl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 Rene Treffer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package de.measite.smack; 18 | 19 | import java.util.Enumeration; 20 | import java.util.Hashtable; 21 | import java.util.Map; 22 | 23 | import org.apache.harmony.javax.security.auth.callback.CallbackHandler; 24 | import org.apache.harmony.javax.security.sasl.SaslClient; 25 | import org.apache.harmony.javax.security.sasl.SaslException; 26 | import org.apache.harmony.javax.security.sasl.SaslServer; 27 | import org.apache.harmony.javax.security.sasl.SaslServerFactory; 28 | 29 | public class Sasl { 30 | 31 | // SaslClientFactory service name 32 | private static final String CLIENTFACTORYSRV = "SaslClientFactory"; //$NON-NLS-1$ 33 | 34 | // SaslServerFactory service name 35 | private static final String SERVERFACTORYSRV = "SaslServerFactory"; //$NON-NLS-1$ 36 | 37 | public static final String POLICY_NOPLAINTEXT = "javax.security.sasl.policy.noplaintext"; //$NON-NLS-1$ 38 | 39 | public static final String POLICY_NOACTIVE = "javax.security.sasl.policy.noactive"; //$NON-NLS-1$ 40 | 41 | public static final String POLICY_NODICTIONARY = "javax.security.sasl.policy.nodictionary"; //$NON-NLS-1$ 42 | 43 | public static final String POLICY_NOANONYMOUS = "javax.security.sasl.policy.noanonymous"; //$NON-NLS-1$ 44 | 45 | public static final String POLICY_FORWARD_SECRECY = "javax.security.sasl.policy.forward"; //$NON-NLS-1$ 46 | 47 | public static final String POLICY_PASS_CREDENTIALS = "javax.security.sasl.policy.credentials"; //$NON-NLS-1$ 48 | 49 | public static final String MAX_BUFFER = "javax.security.sasl.maxbuffer"; //$NON-NLS-1$ 50 | 51 | public static final String RAW_SEND_SIZE = "javax.security.sasl.rawsendsize"; //$NON-NLS-1$ 52 | 53 | public static final String REUSE = "javax.security.sasl.reuse"; //$NON-NLS-1$ 54 | 55 | public static final String QOP = "javax.security.sasl.qop"; //$NON-NLS-1$ 56 | 57 | public static final String STRENGTH = "javax.security.sasl.strength"; //$NON-NLS-1$ 58 | 59 | public static final String SERVER_AUTH = "javax.security.sasl.server.authentication"; //$NON-NLS-1$ 60 | 61 | public static Enumeration getSaslClientFactories() { 62 | Hashtable factories = new Hashtable(); 63 | factories.put(new SaslClientFactory(), new Object()); 64 | return factories.keys(); 65 | } 66 | 67 | public static Enumeration getSaslServerFactories() { 68 | return org.apache.harmony.javax.security.sasl.Sasl.getSaslServerFactories(); 69 | } 70 | 71 | public static SaslServer createSaslServer(String mechanism, String protocol, 72 | String serverName, Map prop, CallbackHandler cbh) throws SaslException { 73 | return org.apache.harmony.javax.security.sasl.Sasl.createSaslServer(mechanism, protocol, serverName, prop, cbh); 74 | } 75 | 76 | public static SaslClient createSaslClient(String[] mechanisms, String authanticationID, 77 | String protocol, String serverName, Map prop, CallbackHandler cbh) 78 | throws SaslException { 79 | if (mechanisms == null) { 80 | throw new NullPointerException("auth.33"); //$NON-NLS-1$ 81 | } 82 | SaslClientFactory fact = getSaslClientFactories().nextElement(); 83 | String[] mech = fact.getMechanismNames(null); 84 | boolean is = false; 85 | if (mech != null) { 86 | for (int j = 0; j < mech.length; j++) { 87 | for (int n = 0; n < mechanisms.length; n++) { 88 | if (mech[j].equals(mechanisms[n])) { 89 | is = true; 90 | break; 91 | } 92 | } 93 | } 94 | } 95 | if (is) { 96 | return fact.createSaslClient( 97 | mechanisms, 98 | authanticationID, 99 | protocol, 100 | serverName, 101 | prop, 102 | cbh 103 | ); 104 | } 105 | return null; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/custom/de/measite/smack/AndroidDebugger.java: -------------------------------------------------------------------------------- 1 | package de.measite.smack; 2 | 3 | import org.jivesoftware.smack.debugger.SmackDebugger; 4 | import org.jivesoftware.smack.ConnectionListener; 5 | import org.jivesoftware.smack.PacketListener; 6 | import org.jivesoftware.smack.Connection; 7 | import org.jivesoftware.smack.packet.Packet; 8 | import org.jivesoftware.smack.util.*; 9 | 10 | import android.util.Log; 11 | 12 | import java.io.Reader; 13 | import java.io.Writer; 14 | import java.text.SimpleDateFormat; 15 | import java.util.Date; 16 | 17 | /** 18 | * Very simple debugger that prints to the android log the sent and received stanzas. Use 19 | * this debugger with caution since printing to the console is an expensive operation that may 20 | * even block the thread since only one thread may print at a time.

21 | *

22 | * It is possible to not only print the raw sent and received stanzas but also the interpreted 23 | * packets by Smack. By default interpreted packets won't be printed. To enable this feature 24 | * just change the printInterpreted static variable to true. 25 | * 26 | * @author Gaston Dombiak 27 | */ 28 | public class AndroidDebugger implements SmackDebugger { 29 | 30 | public static boolean printInterpreted = false; 31 | private SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss aaa"); 32 | 33 | private Connection connection = null; 34 | 35 | private PacketListener listener = null; 36 | private ConnectionListener connListener = null; 37 | 38 | private Writer writer; 39 | private Reader reader; 40 | private ReaderListener readerListener; 41 | private WriterListener writerListener; 42 | 43 | public AndroidDebugger(Connection connection, Writer writer, Reader reader) { 44 | this.connection = connection; 45 | this.writer = writer; 46 | this.reader = reader; 47 | createDebug(); 48 | } 49 | 50 | /** 51 | * Creates the listeners that will print in the console when new activity is detected. 52 | */ 53 | private void createDebug() { 54 | // Create a special Reader that wraps the main Reader and logs data to the GUI. 55 | ObservableReader debugReader = new ObservableReader(reader); 56 | readerListener = new ReaderListener() { 57 | public void read(String str) { 58 | Log.d("SMACK", 59 | dateFormatter.format(new Date()) + " RCV (" + connection.hashCode() + 60 | "): " + 61 | str); 62 | } 63 | }; 64 | debugReader.addReaderListener(readerListener); 65 | 66 | // Create a special Writer that wraps the main Writer and logs data to the GUI. 67 | ObservableWriter debugWriter = new ObservableWriter(writer); 68 | writerListener = new WriterListener() { 69 | public void write(String str) { 70 | Log.d("SMACK", 71 | dateFormatter.format(new Date()) + " SENT (" + connection.hashCode() + 72 | "): " + 73 | str); 74 | } 75 | }; 76 | debugWriter.addWriterListener(writerListener); 77 | 78 | // Assign the reader/writer objects to use the debug versions. The packet reader 79 | // and writer will use the debug versions when they are created. 80 | reader = debugReader; 81 | writer = debugWriter; 82 | 83 | // Create a thread that will listen for all incoming packets and write them to 84 | // the GUI. This is what we call "interpreted" packet data, since it's the packet 85 | // data as Smack sees it and not as it's coming in as raw XML. 86 | listener = new PacketListener() { 87 | public void processPacket(Packet packet) { 88 | if (printInterpreted) { 89 | Log.d("SMACK", 90 | dateFormatter.format(new Date()) + " RCV PKT (" + 91 | connection.hashCode() + 92 | "): " + 93 | packet.toXML()); 94 | } 95 | } 96 | }; 97 | 98 | connListener = new ConnectionListener() { 99 | public void connectionClosed() { 100 | Log.d("SMACK", 101 | dateFormatter.format(new Date()) + " Connection closed (" + 102 | connection.hashCode() + 103 | ")"); 104 | } 105 | 106 | public void connectionClosedOnError(Exception e) { 107 | Log.d("SMACK", 108 | dateFormatter.format(new Date()) + 109 | " Connection closed due to an exception (" + 110 | connection.hashCode() + 111 | ")"); 112 | e.printStackTrace(); 113 | } 114 | public void reconnectionFailed(Exception e) { 115 | Log.d("SMACK", 116 | dateFormatter.format(new Date()) + 117 | " Reconnection failed due to an exception (" + 118 | connection.hashCode() + 119 | ")"); 120 | e.printStackTrace(); 121 | } 122 | public void reconnectionSuccessful() { 123 | Log.d("SMACK", 124 | dateFormatter.format(new Date()) + " Connection reconnected (" + 125 | connection.hashCode() + 126 | ")"); 127 | } 128 | public void reconnectingIn(int seconds) { 129 | Log.d("SMACK", 130 | dateFormatter.format(new Date()) + " Connection (" + 131 | connection.hashCode() + 132 | ") will reconnect in " + seconds); 133 | } 134 | }; 135 | } 136 | 137 | public Reader newConnectionReader(Reader newReader) { 138 | ((ObservableReader)reader).removeReaderListener(readerListener); 139 | ObservableReader debugReader = new ObservableReader(newReader); 140 | debugReader.addReaderListener(readerListener); 141 | reader = debugReader; 142 | return reader; 143 | } 144 | 145 | public Writer newConnectionWriter(Writer newWriter) { 146 | ((ObservableWriter)writer).removeWriterListener(writerListener); 147 | ObservableWriter debugWriter = new ObservableWriter(newWriter); 148 | debugWriter.addWriterListener(writerListener); 149 | writer = debugWriter; 150 | return writer; 151 | } 152 | 153 | public void userHasLogged(String user) { 154 | boolean isAnonymous = "".equals(StringUtils.parseName(user)); 155 | String title = 156 | "User logged (" + connection.hashCode() + "): " 157 | + (isAnonymous ? "" : StringUtils.parseBareAddress(user)) 158 | + "@" 159 | + connection.getServiceName() 160 | + ":" 161 | + connection.getPort(); 162 | title += "/" + StringUtils.parseResource(user); 163 | Log.d("SMACK", title); 164 | // Add the connection listener to the connection so that the debugger can be notified 165 | // whenever the connection is closed. 166 | connection.addConnectionListener(connListener); 167 | } 168 | 169 | public Reader getReader() { 170 | return reader; 171 | } 172 | 173 | public Writer getWriter() { 174 | return writer; 175 | } 176 | 177 | public PacketListener getReaderListener() { 178 | return listener; 179 | } 180 | 181 | public PacketListener getWriterListener() { 182 | return null; 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/TokenParser.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/TokenParser.java,v 1.3 2005/01/17 15:00:54 sunilk Exp $ 3 | * 4 | * Copyright (C) 2002 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | import org.apache.harmony.javax.security.sasl.*; 18 | /** 19 | * The TokenParser class will parse individual tokens from a list of tokens that 20 | * are a directive value for a DigestMD5 authentication.The tokens are separated 21 | * commas. 22 | */ 23 | class TokenParser extends Object 24 | { 25 | private static final int STATE_LOOKING_FOR_FIRST_TOKEN = 1; 26 | private static final int STATE_LOOKING_FOR_TOKEN = 2; 27 | private static final int STATE_SCANNING_TOKEN = 3; 28 | private static final int STATE_LOOKING_FOR_COMMA = 4; 29 | private static final int STATE_PARSING_ERROR = 5; 30 | private static final int STATE_DONE = 6; 31 | 32 | private int m_curPos; 33 | private int m_scanStart; 34 | private int m_state; 35 | private String m_tokens; 36 | 37 | 38 | TokenParser( 39 | String tokens) 40 | { 41 | m_tokens = tokens; 42 | m_curPos = 0; 43 | m_scanStart = 0; 44 | m_state = STATE_LOOKING_FOR_FIRST_TOKEN; 45 | } 46 | 47 | /** 48 | * This function parses the next token from the tokens string and returns 49 | * it as a string. If there are no more tokens a null reference is returned. 50 | * 51 | * @return the parsed token or a null reference if there are no more 52 | * tokens 53 | * 54 | * @exception SASLException if an error occurs while parsing 55 | */ 56 | String parseToken() throws SaslException 57 | { 58 | char currChar; 59 | String token = null; 60 | 61 | 62 | if (m_state == STATE_DONE) 63 | return null; 64 | 65 | while (m_curPos < m_tokens.length() && (token == null)) 66 | { 67 | currChar = m_tokens.charAt(m_curPos); 68 | switch (m_state) 69 | { 70 | case STATE_LOOKING_FOR_FIRST_TOKEN: 71 | case STATE_LOOKING_FOR_TOKEN: 72 | if (isWhiteSpace(currChar)) 73 | { 74 | break; 75 | } 76 | else if (isValidTokenChar(currChar)) 77 | { 78 | m_scanStart = m_curPos; 79 | m_state = STATE_SCANNING_TOKEN; 80 | } 81 | else 82 | { 83 | m_state = STATE_PARSING_ERROR; 84 | throw new SaslException("Invalid token character at position " + m_curPos); 85 | } 86 | break; 87 | 88 | case STATE_SCANNING_TOKEN: 89 | if (isValidTokenChar(currChar)) 90 | { 91 | break; 92 | } 93 | else if (isWhiteSpace(currChar)) 94 | { 95 | token = m_tokens.substring(m_scanStart, m_curPos); 96 | m_state = STATE_LOOKING_FOR_COMMA; 97 | } 98 | else if (',' == currChar) 99 | { 100 | token = m_tokens.substring(m_scanStart, m_curPos); 101 | m_state = STATE_LOOKING_FOR_TOKEN; 102 | } 103 | else 104 | { 105 | m_state = STATE_PARSING_ERROR; 106 | throw new SaslException("Invalid token character at position " + m_curPos); 107 | } 108 | break; 109 | 110 | 111 | case STATE_LOOKING_FOR_COMMA: 112 | if (isWhiteSpace(currChar)) 113 | break; 114 | else if (currChar == ',') 115 | m_state = STATE_LOOKING_FOR_TOKEN; 116 | else 117 | { 118 | m_state = STATE_PARSING_ERROR; 119 | throw new SaslException("Expected a comma, found '" + 120 | currChar + "' at postion " + 121 | m_curPos); 122 | } 123 | break; 124 | } 125 | m_curPos++; 126 | } /* end while loop */ 127 | 128 | if (token == null) 129 | { /* check the ending state */ 130 | switch (m_state) 131 | { 132 | case STATE_SCANNING_TOKEN: 133 | token = m_tokens.substring(m_scanStart); 134 | m_state = STATE_DONE; 135 | break; 136 | 137 | case STATE_LOOKING_FOR_FIRST_TOKEN: 138 | case STATE_LOOKING_FOR_COMMA: 139 | break; 140 | 141 | case STATE_LOOKING_FOR_TOKEN: 142 | throw new SaslException("Trialing comma"); 143 | } 144 | } 145 | 146 | return token; 147 | } 148 | 149 | /** 150 | * This function returns TRUE if the character is a valid token character. 151 | * 152 | * token = 1* 153 | * 154 | * separators = "(" | ")" | "<" | ">" | "@" 155 | * | "," | ";" | ":" | "\" | <"> 156 | * | "/" | "[" | "]" | "?" | "=" 157 | * | "{" | "}" | SP | HT 158 | * 159 | * CTL = 161 | * 162 | * CHAR = 163 | * 164 | * @param c character to be validated 165 | * 166 | * @return True if character is valid Token character else it returns 167 | * false 168 | */ 169 | boolean isValidTokenChar( 170 | char c) 171 | { 172 | if ( ( (c >= '\u0000') && (c <='\u0020') ) || 173 | ( (c >= '\u003a') && (c <= '\u0040') ) || 174 | ( (c >= '\u005b') && (c <= '\u005d') ) || 175 | ('\u002c' == c) || 176 | ('\u0025' == c) || 177 | ('\u0028' == c) || 178 | ('\u0029' == c) || 179 | ('\u007b' == c) || 180 | ('\u007d' == c) || 181 | ('\u007f' == c) ) 182 | return false; 183 | 184 | return true; 185 | } 186 | 187 | /** 188 | * This function returns TRUE if the character is linear white space (LWS). 189 | * LWS = [CRLF] 1*( SP | HT ) 190 | * 191 | * @param c character to be validated 192 | * 193 | * @return True if character is liner whitespace else it returns false 194 | */ 195 | boolean isWhiteSpace( 196 | char c) 197 | { 198 | if ( ('\t' == c) || // HORIZONTAL TABULATION. 199 | ('\n' == c) || // LINE FEED. 200 | ('\r' == c) || // CARRIAGE RETURN. 201 | ('\u0020' == c) ) 202 | return true; 203 | 204 | return false; 205 | } 206 | 207 | } 208 | 209 | -------------------------------------------------------------------------------- /src/custom/org/jivesoftware/smack/util/DNSUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Revision: 1456 $ 3 | * $Date: 2005-06-01 22:04:54 -0700 (Wed, 01 Jun 2005) $ 4 | * 5 | * Copyright 2003-2005 Jive Software. 6 | * 7 | * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package org.jivesoftware.smack.util; 21 | 22 | import java.util.Map; 23 | 24 | import org.xbill.DNS.Lookup; 25 | import org.xbill.DNS.Record; 26 | import org.xbill.DNS.SRVRecord; 27 | import org.xbill.DNS.TextParseException; 28 | import org.xbill.DNS.Type; 29 | 30 | /** 31 | * Utilty class to perform DNS lookups for XMPP services. 32 | * 33 | * @author Matt Tucker 34 | */ 35 | public class DNSUtil { 36 | 37 | /** 38 | * Create a cache to hold the 100 most recently accessed DNS lookups for a 39 | * period of 10 minutes. 40 | */ 41 | @SuppressWarnings("unchecked") 42 | private static Map ccache = new Cache(100, 1000 * 60 * 10); 43 | @SuppressWarnings("unchecked") 44 | private static Map scache = new Cache(100, 1000 * 60 * 10); 45 | 46 | private static HostAddress resolveSRV(String domain) { 47 | String bestHost = null; 48 | int bestPort = -1; 49 | int bestPriority = Integer.MAX_VALUE; 50 | int bestWeight = 0; 51 | Lookup lookup; 52 | try { 53 | lookup = new Lookup(domain, Type.SRV); 54 | Record recs[] = lookup.run(); 55 | if (recs == null) { return null; } 56 | for (Record rec : recs) { 57 | SRVRecord record = (SRVRecord) rec; 58 | if (record != null && record.getTarget() != null) { 59 | int weight = (int) (record.getWeight() * record.getWeight() * Math 60 | .random()); 61 | if (record.getPriority() < bestPriority) { 62 | bestPriority = record.getPriority(); 63 | bestWeight = weight; 64 | bestHost = record.getTarget().toString(); 65 | bestPort = record.getPort(); 66 | } else if (record.getPriority() == bestPriority) { 67 | if (weight > bestWeight) { 68 | bestPriority = record.getPriority(); 69 | bestWeight = weight; 70 | bestHost = record.getTarget().toString(); 71 | bestPort = record.getPort(); 72 | } 73 | } 74 | } 75 | } 76 | } catch (TextParseException e) { 77 | } catch (NullPointerException e) { 78 | } 79 | if (bestHost == null) { 80 | return null; 81 | } 82 | // Host entries in DNS should end with a ".". 83 | if (bestHost.endsWith(".")) { 84 | bestHost = bestHost.substring(0, bestHost.length() - 1); 85 | } 86 | return new HostAddress(bestHost, bestPort); 87 | } 88 | 89 | /** 90 | * Returns the host name and port that the specified XMPP server can be 91 | * reached at for client-to-server communication. A DNS lookup for a SRV 92 | * record in the form "_xmpp-client._tcp.example.com" is attempted, 93 | * according to section 14.4 of RFC 3920. If that lookup fails, a lookup in 94 | * the older form of "_jabber._tcp.example.com" is attempted since servers 95 | * that implement an older version of the protocol may be listed using that 96 | * notation. If that lookup fails as well, it's assumed that the XMPP server 97 | * lives at the host resolved by a DNS lookup at the specified domain on the 98 | * default port of 5222. 99 | *

100 | * 101 | * As an example, a lookup for "example.com" may return 102 | * "im.example.com:5269". 103 | * 104 | * Note on SRV record selection. We now check priority and weight, but we 105 | * still don't do this correctly. The missing behavior is this: if we fail 106 | * to reach a host based on its SRV record then we need to select another 107 | * host from the other SRV records. In Smack 3.1.1 we're not going to be 108 | * able to do the major system redesign to correct this. 109 | * 110 | * @param domain 111 | * the domain. 112 | * @return a HostAddress, which encompasses the hostname and port that the 113 | * XMPP server can be reached at for the specified domain. 114 | */ 115 | public static HostAddress resolveXMPPDomain(String domain) { 116 | // Return item from cache if it exists. 117 | synchronized (ccache) { 118 | if (ccache.containsKey(domain)) { 119 | HostAddress address = (HostAddress) ccache.get(domain); 120 | if (address != null) { 121 | return address; 122 | } 123 | } 124 | } 125 | HostAddress result = resolveSRV("_xmpp-client._tcp." + domain); 126 | if (result == null) { 127 | result = resolveSRV("_jabber._tcp." + domain); 128 | } 129 | if (result == null) { 130 | result = new HostAddress(domain, 5222); 131 | } 132 | // Add item to cache. 133 | synchronized (ccache) { 134 | ccache.put(domain, result); 135 | } 136 | return result; 137 | } 138 | 139 | /** 140 | * Returns the host name and port that the specified XMPP server can be 141 | * reached at for server-to-server communication. A DNS lookup for a SRV 142 | * record in the form "_xmpp-server._tcp.example.com" is attempted, 143 | * according to section 14.4 of RFC 3920. If that lookup fails, a lookup in 144 | * the older form of "_jabber._tcp.example.com" is attempted since servers 145 | * that implement an older version of the protocol may be listed using that 146 | * notation. If that lookup fails as well, it's assumed that the XMPP server 147 | * lives at the host resolved by a DNS lookup at the specified domain on the 148 | * default port of 5269. 149 | *

150 | * 151 | * As an example, a lookup for "example.com" may return 152 | * "im.example.com:5269". 153 | * 154 | * @param domain 155 | * the domain. 156 | * @return a HostAddress, which encompasses the hostname and port that the 157 | * XMPP server can be reached at for the specified domain. 158 | */ 159 | public static HostAddress resolveXMPPServerDomain(String domain) { 160 | // Return item from cache if it exists. 161 | synchronized (scache) { 162 | if (scache.containsKey(domain)) { 163 | HostAddress address = (HostAddress) scache.get(domain); 164 | if (address != null) { 165 | return address; 166 | } 167 | } 168 | } 169 | HostAddress result = resolveSRV("_xmpp-server._tcp." + domain); 170 | if (result == null) { 171 | result = resolveSRV("_jabber._tcp." + domain); 172 | } 173 | if (result == null) { 174 | result = new HostAddress(domain, 5269); 175 | } 176 | // Add item to cache. 177 | synchronized (scache) { 178 | scache.put(domain, result); 179 | } 180 | return result; 181 | } 182 | 183 | /** 184 | * Encapsulates a hostname and port. 185 | */ 186 | public static class HostAddress { 187 | 188 | private String host; 189 | private int port; 190 | 191 | private HostAddress(String host, int port) { 192 | this.host = host; 193 | this.port = port; 194 | } 195 | 196 | /** 197 | * Returns the hostname. 198 | * 199 | * @return the hostname. 200 | */ 201 | public String getHost() { 202 | return host; 203 | } 204 | 205 | /** 206 | * Returns the port. 207 | * 208 | * @return the port. 209 | */ 210 | public int getPort() { 211 | return port; 212 | } 213 | 214 | public String toString() { 215 | return host + ":" + port; 216 | } 217 | 218 | public boolean equals(Object o) { 219 | if (this == o) { 220 | return true; 221 | } 222 | if (!(o instanceof HostAddress)) { 223 | return false; 224 | } 225 | 226 | final HostAddress address = (HostAddress) o; 227 | 228 | if (!host.equals(address.host)) { 229 | return false; 230 | } 231 | return port == address.port; 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/custom/com/kenai/jbosh/QName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The Apache Software License, Version 1.1 3 | * 4 | * 5 | * Copyright (c) 2001-2003 The Apache Software Foundation. All rights 6 | * reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in 17 | * the documentation and/or other materials provided with the 18 | * distribution. 19 | * 20 | * 3. The end-user documentation included with the redistribution, 21 | * if any, must include the following acknowledgment: 22 | * "This product includes software developed by the 23 | * Apache Software Foundation (http://www.apache.org/)." 24 | * Alternately, this acknowledgment may appear in the software itself, 25 | * if and wherever such third-party acknowledgments normally appear. 26 | * 27 | * 4. The names "Axis" and "Apache Software Foundation" must 28 | * not be used to endorse or promote products derived from this 29 | * software without prior written permission. For written 30 | * permission, please contact apache@apache.org. 31 | * 32 | * 5. Products derived from this software may not be called "Apache", 33 | * nor may "Apache" appear in their name, without prior written 34 | * permission of the Apache Software Foundation. 35 | * 36 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 37 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 38 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 39 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 40 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 41 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 42 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 43 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 44 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 46 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 47 | * SUCH DAMAGE. 48 | * ==================================================================== 49 | * 50 | * This software consists of voluntary contributions made by many 51 | * individuals on behalf of the Apache Software Foundation. For more 52 | * information on the Apache Software Foundation, please see 53 | * . 54 | */ 55 | package com.kenai.jbosh; 56 | 57 | import java.io.IOException; 58 | import java.io.ObjectInputStream; 59 | import java.io.Serializable; 60 | 61 | /** 62 | * QName class represents the value of a qualified name 63 | * as specified in XML 64 | * Schema Part2: Datatypes specification. 65 | *

66 | * The value of a QName contains a namespaceURI, a localPart and a prefix. 67 | * The localPart provides the local part of the qualified name. The 68 | * namespaceURI is a URI reference identifying the namespace. 69 | * 70 | * @version 1.1 71 | */ 72 | public class QName implements Serializable { 73 | 74 | /** comment/shared empty string */ 75 | private static final String emptyString = "".intern(); 76 | 77 | /** Field namespaceURI */ 78 | private String namespaceURI; 79 | 80 | /** Field localPart */ 81 | private String localPart; 82 | 83 | /** Field prefix */ 84 | private String prefix; 85 | 86 | /** 87 | * Constructor for the QName. 88 | * 89 | * @param localPart Local part of the QName 90 | */ 91 | public QName(String localPart) { 92 | this(emptyString, localPart, emptyString); 93 | } 94 | 95 | /** 96 | * Constructor for the QName. 97 | * 98 | * @param namespaceURI Namespace URI for the QName 99 | * @param localPart Local part of the QName. 100 | */ 101 | public QName(String namespaceURI, String localPart) { 102 | this(namespaceURI, localPart, emptyString); 103 | } 104 | 105 | /** 106 | * Constructor for the QName. 107 | * 108 | * @param namespaceURI Namespace URI for the QName 109 | * @param localPart Local part of the QName. 110 | * @param prefix Prefix of the QName. 111 | */ 112 | public QName(String namespaceURI, String localPart, String prefix) { 113 | this.namespaceURI = (namespaceURI == null) 114 | ? emptyString 115 | : namespaceURI.intern(); 116 | if (localPart == null) { 117 | throw new IllegalArgumentException("invalid QName local part"); 118 | } else { 119 | this.localPart = localPart.intern(); 120 | } 121 | 122 | if (prefix == null) { 123 | throw new IllegalArgumentException("invalid QName prefix"); 124 | } else { 125 | this.prefix = prefix.intern(); 126 | } 127 | } 128 | 129 | /** 130 | * Gets the Namespace URI for this QName 131 | * 132 | * @return Namespace URI 133 | */ 134 | public String getNamespaceURI() { 135 | return namespaceURI; 136 | } 137 | 138 | /** 139 | * Gets the Local part for this QName 140 | * 141 | * @return Local part 142 | */ 143 | public String getLocalPart() { 144 | return localPart; 145 | } 146 | 147 | /** 148 | * Gets the Prefix for this QName 149 | * 150 | * @return Prefix 151 | */ 152 | public String getPrefix() { 153 | return prefix; 154 | } 155 | 156 | /** 157 | * Returns a string representation of this QName 158 | * 159 | * @return a string representation of the QName 160 | */ 161 | public String toString() { 162 | 163 | return ((namespaceURI == emptyString) 164 | ? localPart 165 | : '{' + namespaceURI + '}' + localPart); 166 | } 167 | 168 | /** 169 | * Tests this QName for equality with another object. 170 | *

171 | * If the given object is not a QName or is null then this method 172 | * returns false. 173 | *

174 | * For two QNames to be considered equal requires that both 175 | * localPart and namespaceURI must be equal. This method uses 176 | * String.equals to check equality of localPart 177 | * and namespaceURI. Any class that extends QName is required 178 | * to satisfy this equality contract. 179 | *

180 | * This method satisfies the general contract of the Object.equals method. 181 | * 182 | * @param obj the reference object with which to compare 183 | * 184 | * @return true if the given object is identical to this 185 | * QName: false otherwise. 186 | */ 187 | public final boolean equals(Object obj) { 188 | 189 | if (obj == this) { 190 | return true; 191 | } 192 | 193 | if (!(obj instanceof QName)) { 194 | return false; 195 | } 196 | 197 | if ((namespaceURI == ((QName) obj).namespaceURI) 198 | && (localPart == ((QName) obj).localPart)) { 199 | return true; 200 | } 201 | 202 | return false; 203 | } 204 | 205 | /** 206 | * Returns a QName holding the value of the specified String. 207 | *

208 | * The string must be in the form returned by the QName.toString() 209 | * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}" 210 | * part being optional. 211 | *

212 | * This method doesn't do a full validation of the resulting QName. 213 | * In particular, it doesn't check that the resulting namespace URI 214 | * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting 215 | * local part is a legal NCName per the XML Namespaces specification. 216 | * 217 | * @param s the string to be parsed 218 | * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName 219 | * @return QName corresponding to the given String 220 | */ 221 | public static QName valueOf(String s) { 222 | 223 | if ((s == null) || s.equals("")) { 224 | throw new IllegalArgumentException("invalid QName literal"); 225 | } 226 | 227 | if (s.charAt(0) == '{') { 228 | int i = s.indexOf('}'); 229 | 230 | if (i == -1) { 231 | throw new IllegalArgumentException("invalid QName literal"); 232 | } 233 | 234 | if (i == s.length() - 1) { 235 | throw new IllegalArgumentException("invalid QName literal"); 236 | } else { 237 | return new QName(s.substring(1, i), s.substring(i + 1)); 238 | } 239 | } else { 240 | return new QName(s); 241 | } 242 | } 243 | 244 | /** 245 | * Returns a hash code value for this QName object. The hash code 246 | * is based on both the localPart and namespaceURI parts of the 247 | * QName. This method satisfies the general contract of the 248 | * Object.hashCode method. 249 | * 250 | * @return a hash code value for this Qname object 251 | */ 252 | public final int hashCode() { 253 | return namespaceURI.hashCode() ^ localPart.hashCode(); 254 | } 255 | 256 | /** 257 | * Ensure that deserialization properly interns the results. 258 | * @param in the ObjectInputStream to be read 259 | */ 260 | private void readObject(ObjectInputStream in) throws 261 | IOException, ClassNotFoundException { 262 | in.defaultReadObject(); 263 | 264 | namespaceURI = namespaceURI.intern(); 265 | localPart = localPart.intern(); 266 | prefix = prefix.intern(); 267 | } 268 | } 269 | 270 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/DirectiveList.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/DirectiveList.java,v 1.4 2005/01/17 15:00:54 sunilk Exp $ 3 | * 4 | * Copyright (C) 2002 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | import java.util.*; 18 | import org.apache.harmony.javax.security.sasl.*; 19 | import java.io.UnsupportedEncodingException; 20 | 21 | /** 22 | * Implements the DirectiveList class whihc will be used by the 23 | * DigestMD5SaslClient class 24 | */ 25 | class DirectiveList extends Object 26 | { 27 | private static final int STATE_LOOKING_FOR_FIRST_DIRECTIVE = 1; 28 | private static final int STATE_LOOKING_FOR_DIRECTIVE = 2; 29 | private static final int STATE_SCANNING_NAME = 3; 30 | private static final int STATE_LOOKING_FOR_EQUALS = 4; 31 | private static final int STATE_LOOKING_FOR_VALUE = 5; 32 | private static final int STATE_LOOKING_FOR_COMMA = 6; 33 | private static final int STATE_SCANNING_QUOTED_STRING_VALUE = 7; 34 | private static final int STATE_SCANNING_TOKEN_VALUE = 8; 35 | private static final int STATE_NO_UTF8_SUPPORT = 9; 36 | 37 | private int m_curPos; 38 | private int m_errorPos; 39 | private String m_directives; 40 | private int m_state; 41 | private ArrayList m_directiveList; 42 | private String m_curName; 43 | private int m_scanStart; 44 | 45 | /** 46 | * Constructs a new DirectiveList. 47 | */ 48 | DirectiveList( 49 | byte[] directives) 50 | { 51 | m_curPos = 0; 52 | m_state = STATE_LOOKING_FOR_FIRST_DIRECTIVE; 53 | m_directiveList = new ArrayList(10); 54 | m_scanStart = 0; 55 | m_errorPos = -1; 56 | try 57 | { 58 | m_directives = new String(directives, "UTF-8"); 59 | } 60 | catch(UnsupportedEncodingException e) 61 | { 62 | m_state = STATE_NO_UTF8_SUPPORT; 63 | } 64 | } 65 | 66 | /** 67 | * This function takes a US-ASCII character string containing a list of comma 68 | * separated directives, and parses the string into the individual directives 69 | * and their values. A directive consists of a token specifying the directive 70 | * name followed by an equal sign (=) and the directive value. The value is 71 | * either a token or a quoted string 72 | * 73 | * @exception SaslException If an error Occurs 74 | */ 75 | void parseDirectives() throws SaslException 76 | { 77 | char prevChar; 78 | char currChar; 79 | int rc = 0; 80 | boolean haveQuotedPair = false; 81 | String currentName = ""; 82 | 83 | if (m_state == STATE_NO_UTF8_SUPPORT) 84 | throw new SaslException("No UTF-8 support on platform"); 85 | 86 | prevChar = 0; 87 | 88 | while (m_curPos < m_directives.length()) 89 | { 90 | currChar = m_directives.charAt(m_curPos); 91 | switch (m_state) 92 | { 93 | case STATE_LOOKING_FOR_FIRST_DIRECTIVE: 94 | case STATE_LOOKING_FOR_DIRECTIVE: 95 | if (isWhiteSpace(currChar)) 96 | { 97 | break; 98 | } 99 | else if (isValidTokenChar(currChar)) 100 | { 101 | m_scanStart = m_curPos; 102 | m_state = STATE_SCANNING_NAME; 103 | } 104 | else 105 | { 106 | m_errorPos = m_curPos; 107 | throw new SaslException("Parse error: Invalid name character"); 108 | } 109 | break; 110 | 111 | case STATE_SCANNING_NAME: 112 | if (isValidTokenChar(currChar)) 113 | { 114 | break; 115 | } 116 | else if (isWhiteSpace(currChar)) 117 | { 118 | currentName = m_directives.substring(m_scanStart, m_curPos); 119 | m_state = STATE_LOOKING_FOR_EQUALS; 120 | } 121 | else if ('=' == currChar) 122 | { 123 | currentName = m_directives.substring(m_scanStart, m_curPos); 124 | m_state = STATE_LOOKING_FOR_VALUE; 125 | } 126 | else 127 | { 128 | m_errorPos = m_curPos; 129 | throw new SaslException("Parse error: Invalid name character"); 130 | } 131 | break; 132 | 133 | case STATE_LOOKING_FOR_EQUALS: 134 | if (isWhiteSpace(currChar)) 135 | { 136 | break; 137 | } 138 | else if ('=' == currChar) 139 | { 140 | m_state = STATE_LOOKING_FOR_VALUE; 141 | } 142 | else 143 | { 144 | m_errorPos = m_curPos; 145 | throw new SaslException("Parse error: Expected equals sign '='."); 146 | } 147 | break; 148 | 149 | case STATE_LOOKING_FOR_VALUE: 150 | if (isWhiteSpace(currChar)) 151 | { 152 | break; 153 | } 154 | else if ('"' == currChar) 155 | { 156 | m_scanStart = m_curPos+1; /* don't include the quote */ 157 | m_state = STATE_SCANNING_QUOTED_STRING_VALUE; 158 | } 159 | else if (isValidTokenChar(currChar)) 160 | { 161 | m_scanStart = m_curPos; 162 | m_state = STATE_SCANNING_TOKEN_VALUE; 163 | } 164 | else 165 | { 166 | m_errorPos = m_curPos; 167 | throw new SaslException("Parse error: Unexpected character"); 168 | } 169 | break; 170 | 171 | case STATE_SCANNING_TOKEN_VALUE: 172 | if (isValidTokenChar(currChar)) 173 | { 174 | break; 175 | } 176 | else if (isWhiteSpace(currChar)) 177 | { 178 | addDirective(currentName, false); 179 | m_state = STATE_LOOKING_FOR_COMMA; 180 | } 181 | else if (',' == currChar) 182 | { 183 | addDirective(currentName, false); 184 | m_state = STATE_LOOKING_FOR_DIRECTIVE; 185 | } 186 | else 187 | { 188 | m_errorPos = m_curPos; 189 | throw new SaslException("Parse error: Invalid value character"); 190 | } 191 | break; 192 | 193 | case STATE_SCANNING_QUOTED_STRING_VALUE: 194 | if ('\\' == currChar) 195 | haveQuotedPair = true; 196 | if ( ('"' == currChar) && 197 | ('\\' != prevChar) ) 198 | { 199 | addDirective(currentName, haveQuotedPair); 200 | haveQuotedPair = false; 201 | m_state = STATE_LOOKING_FOR_COMMA; 202 | } 203 | break; 204 | 205 | case STATE_LOOKING_FOR_COMMA: 206 | if (isWhiteSpace(currChar)) 207 | break; 208 | else if (currChar == ',') 209 | m_state = STATE_LOOKING_FOR_DIRECTIVE; 210 | else 211 | { 212 | m_errorPos = m_curPos; 213 | throw new SaslException("Parse error: Expected a comma."); 214 | } 215 | break; 216 | } 217 | if (0 != rc) 218 | break; 219 | prevChar = currChar; 220 | m_curPos++; 221 | } /* end while loop */ 222 | 223 | 224 | if (rc == 0) 225 | { 226 | /* check the ending state */ 227 | switch (m_state) 228 | { 229 | case STATE_SCANNING_TOKEN_VALUE: 230 | addDirective(currentName, false); 231 | break; 232 | 233 | case STATE_LOOKING_FOR_FIRST_DIRECTIVE: 234 | case STATE_LOOKING_FOR_COMMA: 235 | break; 236 | 237 | case STATE_LOOKING_FOR_DIRECTIVE: 238 | throw new SaslException("Parse error: Trailing comma."); 239 | 240 | case STATE_SCANNING_NAME: 241 | case STATE_LOOKING_FOR_EQUALS: 242 | case STATE_LOOKING_FOR_VALUE: 243 | throw new SaslException("Parse error: Missing value."); 244 | 245 | case STATE_SCANNING_QUOTED_STRING_VALUE: 246 | throw new SaslException("Parse error: Missing closing quote."); 247 | } 248 | } 249 | 250 | } 251 | 252 | /** 253 | * This function returns TRUE if the character is a valid token character. 254 | * 255 | * token = 1* 256 | * 257 | * separators = "(" | ")" | "<" | ">" | "@" 258 | * | "," | ";" | ":" | "\" | <"> 259 | * | "/" | "[" | "]" | "?" | "=" 260 | * | "{" | "}" | SP | HT 261 | * 262 | * CTL = 264 | * 265 | * CHAR = 266 | * 267 | * @param c character to be tested 268 | * 269 | * @return Returns TRUE if the character is a valid token character. 270 | */ 271 | boolean isValidTokenChar( 272 | char c) 273 | { 274 | if ( ( (c >= '\u0000') && (c <='\u0020') ) || 275 | ( (c >= '\u003a') && (c <= '\u0040') ) || 276 | ( (c >= '\u005b') && (c <= '\u005d') ) || 277 | ('\u002c' == c) || 278 | ('\u0025' == c) || 279 | ('\u0028' == c) || 280 | ('\u0029' == c) || 281 | ('\u007b' == c) || 282 | ('\u007d' == c) || 283 | ('\u007f' == c) ) 284 | return false; 285 | 286 | return true; 287 | } 288 | 289 | /** 290 | * This function returns TRUE if the character is linear white space (LWS). 291 | * LWS = [CRLF] 1*( SP | HT ) 292 | * @param c Input charcter to be tested 293 | * 294 | * @return Returns TRUE if the character is linear white space (LWS) 295 | */ 296 | boolean isWhiteSpace( 297 | char c) 298 | { 299 | if ( ('\t' == c) || // HORIZONTAL TABULATION. 300 | ('\n' == c) || // LINE FEED. 301 | ('\r' == c) || // CARRIAGE RETURN. 302 | ('\u0020' == c) ) 303 | return true; 304 | 305 | return false; 306 | } 307 | 308 | /** 309 | * This function creates a directive record and adds it to the list, the 310 | * value will be added later after it is parsed. 311 | * 312 | * @param name Name 313 | * @param haveQuotedPair true if quoted pair is there else false 314 | */ 315 | void addDirective( 316 | String name, 317 | boolean haveQuotedPair) 318 | { 319 | String value; 320 | int inputIndex; 321 | int valueIndex; 322 | char valueChar; 323 | int type; 324 | 325 | if (!haveQuotedPair) 326 | { 327 | value = m_directives.substring(m_scanStart, m_curPos); 328 | } 329 | else 330 | { //copy one character at a time skipping backslash excapes. 331 | StringBuffer valueBuf = new StringBuffer(m_curPos - m_scanStart); 332 | valueIndex = 0; 333 | inputIndex = m_scanStart; 334 | while (inputIndex < m_curPos) 335 | { 336 | if ('\\' == (valueChar = m_directives.charAt(inputIndex))) 337 | inputIndex++; 338 | valueBuf.setCharAt(valueIndex, m_directives.charAt(inputIndex)); 339 | valueIndex++; 340 | inputIndex++; 341 | } 342 | value = new String(valueBuf); 343 | } 344 | 345 | if (m_state == STATE_SCANNING_QUOTED_STRING_VALUE) 346 | type = ParsedDirective.QUOTED_STRING_VALUE; 347 | else 348 | type = ParsedDirective.TOKEN_VALUE; 349 | m_directiveList.add(new ParsedDirective(name, value, type)); 350 | } 351 | 352 | 353 | /** 354 | * Returns the List iterator. 355 | * 356 | * @return Returns the Iterator Object for the List. 357 | */ 358 | Iterator getIterator() 359 | { 360 | return m_directiveList.iterator(); 361 | } 362 | } 363 | 364 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/DigestChallenge.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/DigestChallenge.java,v 1.3 2005/01/17 15:00:54 sunilk Exp $ 3 | * 4 | * Copyright (C) 2003 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | import java.util.*; 18 | import org.apache.harmony.javax.security.sasl.*; 19 | 20 | /** 21 | * Implements the DigestChallenge class which will be used by the 22 | * DigestMD5SaslClient class 23 | */ 24 | class DigestChallenge extends Object 25 | { 26 | public static final int QOP_AUTH = 0x01; 27 | public static final int QOP_AUTH_INT = 0x02; 28 | public static final int QOP_AUTH_CONF = 0x04; 29 | public static final int QOP_UNRECOGNIZED = 0x08; 30 | 31 | private static final int CIPHER_3DES = 0x01; 32 | private static final int CIPHER_DES = 0x02; 33 | private static final int CIPHER_RC4_40 = 0x04; 34 | private static final int CIPHER_RC4 = 0x08; 35 | private static final int CIPHER_RC4_56 = 0x10; 36 | private static final int CIPHER_UNRECOGNIZED = 0x20; 37 | private static final int CIPHER_RECOGNIZED_MASK = 38 | CIPHER_3DES | CIPHER_DES | CIPHER_RC4_40 | CIPHER_RC4 | CIPHER_RC4_56; 39 | 40 | private ArrayList m_realms; 41 | private String m_nonce; 42 | private int m_qop; 43 | private boolean m_staleFlag; 44 | private int m_maxBuf; 45 | private String m_characterSet; 46 | private String m_algorithm; 47 | private int m_cipherOptions; 48 | 49 | DigestChallenge( 50 | byte[] challenge) 51 | throws SaslException 52 | { 53 | m_realms = new ArrayList(5); 54 | m_nonce = null; 55 | m_qop = 0; 56 | m_staleFlag = false; 57 | m_maxBuf = -1; 58 | m_characterSet = null; 59 | m_algorithm = null; 60 | m_cipherOptions = 0; 61 | 62 | DirectiveList dirList = new DirectiveList(challenge); 63 | try 64 | { 65 | dirList.parseDirectives(); 66 | checkSemantics(dirList); 67 | } 68 | catch (SaslException e) 69 | { 70 | } 71 | } 72 | 73 | /** 74 | * Checks the semantics of the directives in the directive list as parsed 75 | * from the digest challenge byte array. 76 | * 77 | * @param dirList the list of directives parsed from the digest challenge 78 | * 79 | * @exception SaslException If a semantic error occurs 80 | */ 81 | void checkSemantics( 82 | DirectiveList dirList) throws SaslException 83 | { 84 | Iterator directives = dirList.getIterator(); 85 | ParsedDirective directive; 86 | String name; 87 | 88 | while (directives.hasNext()) 89 | { 90 | directive = (ParsedDirective)directives.next(); 91 | name = directive.getName(); 92 | if (name.equals("realm")) 93 | handleRealm(directive); 94 | else if (name.equals("nonce")) 95 | handleNonce(directive); 96 | else if (name.equals("qop")) 97 | handleQop(directive); 98 | else if (name.equals("maxbuf")) 99 | handleMaxbuf(directive); 100 | else if (name.equals("charset")) 101 | handleCharset(directive); 102 | else if (name.equals("algorithm")) 103 | handleAlgorithm(directive); 104 | else if (name.equals("cipher")) 105 | handleCipher(directive); 106 | else if (name.equals("stale")) 107 | handleStale(directive); 108 | } 109 | 110 | /* post semantic check */ 111 | if (-1 == m_maxBuf) 112 | m_maxBuf = 65536; 113 | 114 | if (m_qop == 0) 115 | m_qop = QOP_AUTH; 116 | else if ( (m_qop & QOP_AUTH) != QOP_AUTH ) 117 | throw new SaslException("Only qop-auth is supported by client"); 118 | else if ( ((m_qop & QOP_AUTH_CONF) == QOP_AUTH_CONF) && 119 | (0 == (m_cipherOptions & CIPHER_RECOGNIZED_MASK)) ) 120 | throw new SaslException("Invalid cipher options"); 121 | else if (null == m_nonce) 122 | throw new SaslException("Missing nonce directive"); 123 | else if (m_staleFlag) 124 | throw new SaslException("Unexpected stale flag"); 125 | else if ( null == m_algorithm ) 126 | throw new SaslException("Missing algorithm directive"); 127 | } 128 | 129 | /** 130 | * This function implements the semenatics of the nonce directive. 131 | * 132 | * @param pd ParsedDirective 133 | * 134 | * @exception SaslException If an error occurs due to too many nonce 135 | * values 136 | */ 137 | void handleNonce( 138 | ParsedDirective pd) throws SaslException 139 | { 140 | if (null != m_nonce) 141 | throw new SaslException("Too many nonce values."); 142 | 143 | m_nonce = pd.getValue(); 144 | } 145 | 146 | /** 147 | * This function implements the semenatics of the realm directive. 148 | * 149 | * @param pd ParsedDirective 150 | */ 151 | void handleRealm( 152 | ParsedDirective pd) 153 | { 154 | m_realms.add(pd.getValue()); 155 | } 156 | 157 | /** 158 | * This function implements the semenatics of the qop (quality of protection) 159 | * directive. The value of the qop directive is as defined below: 160 | * qop-options = "qop" "=" <"> qop-list <"> 161 | * qop-list = 1#qop-value 162 | * qop-value = "auth" | "auth-int" | "auth-conf" | token 163 | * 164 | * @param pd ParsedDirective 165 | * 166 | * @exception SaslException If an error occurs due to too many qop 167 | * directives 168 | */ 169 | void handleQop( 170 | ParsedDirective pd) throws SaslException 171 | { 172 | String token; 173 | TokenParser parser; 174 | 175 | if (m_qop != 0) 176 | throw new SaslException("Too many qop directives."); 177 | 178 | parser = new TokenParser(pd.getValue()); 179 | for (token = parser.parseToken(); 180 | token != null; 181 | token = parser.parseToken()) 182 | { 183 | if (token.equals("auth")) 184 | m_qop |= QOP_AUTH; 185 | else if (token.equals("auth-int")) 186 | m_qop |= QOP_AUTH_INT; 187 | else if (token.equals("auth-conf")) 188 | m_qop |= QOP_AUTH_CONF; 189 | else 190 | m_qop |= QOP_UNRECOGNIZED; 191 | } 192 | } 193 | 194 | /** 195 | * This function implements the semenatics of the Maxbuf directive. 196 | * the value is defined as: 1*DIGIT 197 | * 198 | * @param pd ParsedDirective 199 | * 200 | * @exception SaslException If an error occur 201 | */ 202 | void handleMaxbuf( 203 | ParsedDirective pd) throws SaslException 204 | { 205 | if (-1 != m_maxBuf) /*it's initialized to -1 */ 206 | throw new SaslException("Too many maxBuf directives."); 207 | 208 | m_maxBuf = Integer.parseInt(pd.getValue()); 209 | 210 | if (0 == m_maxBuf) 211 | throw new SaslException("Max buf value must be greater than zero."); 212 | } 213 | 214 | /** 215 | * This function implements the semenatics of the charset directive. 216 | * the value is defined as: 1*DIGIT 217 | * 218 | * @param pd ParsedDirective 219 | * 220 | * @exception SaslException If an error occurs dur to too many charset 221 | * directives or Invalid character encoding 222 | * directive 223 | */ 224 | void handleCharset( 225 | ParsedDirective pd) throws SaslException 226 | { 227 | if (null != m_characterSet) 228 | throw new SaslException("Too many charset directives."); 229 | 230 | m_characterSet = pd.getValue(); 231 | 232 | if (!m_characterSet.equals("utf-8")) 233 | throw new SaslException("Invalid character encoding directive"); 234 | } 235 | 236 | /** 237 | * This function implements the semenatics of the charset directive. 238 | * the value is defined as: 1*DIGIT 239 | * 240 | * @param pd ParsedDirective 241 | * 242 | * @exception SaslException If an error occurs due to too many algorith 243 | * directive or Invalid algorithm directive 244 | * value 245 | */ 246 | void handleAlgorithm( 247 | ParsedDirective pd) throws SaslException 248 | { 249 | if (null != m_algorithm) 250 | throw new SaslException("Too many algorithm directives."); 251 | 252 | m_algorithm = pd.getValue(); 253 | 254 | if (!"md5-sess".equals(m_algorithm)) 255 | throw new SaslException("Invalid algorithm directive value: " + 256 | m_algorithm); 257 | } 258 | 259 | /** 260 | * This function implements the semenatics of the cipher-opts directive 261 | * directive. The value of the qop directive is as defined below: 262 | * qop-options = "qop" "=" <"> qop-list <"> 263 | * qop-list = 1#qop-value 264 | * qop-value = "auth" | "auth-int" | "auth-conf" | token 265 | * 266 | * @param pd ParsedDirective 267 | * 268 | * @exception SaslException If an error occurs due to Too many cipher 269 | * directives 270 | */ 271 | void handleCipher( 272 | ParsedDirective pd) throws SaslException 273 | { 274 | String token; 275 | TokenParser parser; 276 | 277 | if (0 != m_cipherOptions) 278 | throw new SaslException("Too many cipher directives."); 279 | 280 | parser = new TokenParser(pd.getValue()); 281 | token = parser.parseToken(); 282 | for (token = parser.parseToken(); 283 | token != null; 284 | token = parser.parseToken()) 285 | { 286 | if ("3des".equals(token)) 287 | m_cipherOptions |= CIPHER_3DES; 288 | else if ("des".equals(token)) 289 | m_cipherOptions |= CIPHER_DES; 290 | else if ("rc4-40".equals(token)) 291 | m_cipherOptions |= CIPHER_RC4_40; 292 | else if ("rc4".equals(token)) 293 | m_cipherOptions |= CIPHER_RC4; 294 | else if ("rc4-56".equals(token)) 295 | m_cipherOptions |= CIPHER_RC4_56; 296 | else 297 | m_cipherOptions |= CIPHER_UNRECOGNIZED; 298 | } 299 | 300 | if (m_cipherOptions == 0) 301 | m_cipherOptions = CIPHER_UNRECOGNIZED; 302 | } 303 | 304 | /** 305 | * This function implements the semenatics of the stale directive. 306 | * 307 | * @param pd ParsedDirective 308 | * 309 | * @exception SaslException If an error occurs due to Too many stale 310 | * directives or Invalid stale directive value 311 | */ 312 | void handleStale( 313 | ParsedDirective pd) throws SaslException 314 | { 315 | if (false != m_staleFlag) 316 | throw new SaslException("Too many stale directives."); 317 | 318 | if ("true".equals(pd.getValue())) 319 | m_staleFlag = true; 320 | else 321 | throw new SaslException("Invalid stale directive value: " + 322 | pd.getValue()); 323 | } 324 | 325 | /** 326 | * Return the list of the All the Realms 327 | * 328 | * @return List of all the realms 329 | */ 330 | public ArrayList getRealms() 331 | { 332 | return m_realms; 333 | } 334 | 335 | /** 336 | * @return Returns the Nonce 337 | */ 338 | public String getNonce() 339 | { 340 | return m_nonce; 341 | } 342 | 343 | /** 344 | * Return the quality-of-protection 345 | * 346 | * @return The quality-of-protection 347 | */ 348 | public int getQop() 349 | { 350 | return m_qop; 351 | } 352 | 353 | /** 354 | * @return The state of the Staleflag 355 | */ 356 | public boolean getStaleFlag() 357 | { 358 | return m_staleFlag; 359 | } 360 | 361 | /** 362 | * @return The Maximum Buffer value 363 | */ 364 | public int getMaxBuf() 365 | { 366 | return m_maxBuf; 367 | } 368 | 369 | /** 370 | * @return character set values as string 371 | */ 372 | public String getCharacterSet() 373 | { 374 | return m_characterSet; 375 | } 376 | 377 | /** 378 | * @return The String value of the algorithm 379 | */ 380 | public String getAlgorithm() 381 | { 382 | return m_algorithm; 383 | } 384 | 385 | /** 386 | * @return The cipher options 387 | */ 388 | public int getCipherOptions() 389 | { 390 | return m_cipherOptions; 391 | } 392 | } 393 | 394 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Licences 2 | 3 | All code in this repository is licensed under either 4 | (1) Apache License Version 2.0 5 | (2) THE OPENLDAP PUBLIC LICENSE (only src/novell-openldap-jldap) 6 | 7 | No GNU code will be included. Other patches are always welcome. 8 | 9 | 10 | 11 | The OpenLDAP Public License 12 | Version 2.8, 17 August 2003 13 | 14 | Redistribution and use of this software and associated documentation 15 | ("Software"), with or without modification, are permitted provided 16 | that the following conditions are met: 17 | 18 | 1. Redistributions in source form must retain copyright statements 19 | and notices, 20 | 21 | 2. Redistributions in binary form must reproduce applicable copyright 22 | statements and notices, this list of conditions, and the following 23 | disclaimer in the documentation and/or other materials provided 24 | with the distribution, and 25 | 26 | 3. Redistributions must contain a verbatim copy of this document. 27 | 28 | The OpenLDAP Foundation may revise this license from time to time. 29 | Each revision is distinguished by a version number. You may use 30 | this Software under terms of this license revision or under the 31 | terms of any subsequent revision of the license. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS 34 | CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 35 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 36 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 37 | SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) 38 | OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 41 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 42 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 44 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | 47 | The names of the authors and copyright holders must not be used in 48 | advertising or otherwise to promote the sale, use or other dealing 49 | in this Software without specific, written prior permission. Title 50 | to copyright in this Software shall at all times remain with copyright 51 | holders. 52 | 53 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 54 | 55 | Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, 56 | California, USA. All Rights Reserved. Permission to copy and 57 | distribute verbatim copies of this document is granted. 58 | 59 | 60 | 61 | 62 | Apache License 63 | Version 2.0, January 2004 64 | http://www.apache.org/licenses/ 65 | 66 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 67 | 68 | 1. Definitions. 69 | 70 | "License" shall mean the terms and conditions for use, reproduction, 71 | and distribution as defined by Sections 1 through 9 of this document. 72 | 73 | "Licensor" shall mean the copyright owner or entity authorized by 74 | the copyright owner that is granting the License. 75 | 76 | "Legal Entity" shall mean the union of the acting entity and all 77 | other entities that control, are controlled by, or are under common 78 | control with that entity. For the purposes of this definition, 79 | "control" means (i) the power, direct or indirect, to cause the 80 | direction or management of such entity, whether by contract or 81 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 82 | outstanding shares, or (iii) beneficial ownership of such entity. 83 | 84 | "You" (or "Your") shall mean an individual or Legal Entity 85 | exercising permissions granted by this License. 86 | 87 | "Source" form shall mean the preferred form for making modifications, 88 | including but not limited to software source code, documentation 89 | source, and configuration files. 90 | 91 | "Object" form shall mean any form resulting from mechanical 92 | transformation or translation of a Source form, including but 93 | not limited to compiled object code, generated documentation, 94 | and conversions to other media types. 95 | 96 | "Work" shall mean the work of authorship, whether in Source or 97 | Object form, made available under the License, as indicated by a 98 | copyright notice that is included in or attached to the work 99 | (an example is provided in the Appendix below). 100 | 101 | "Derivative Works" shall mean any work, whether in Source or Object 102 | form, that is based on (or derived from) the Work and for which the 103 | editorial revisions, annotations, elaborations, or other modifications 104 | represent, as a whole, an original work of authorship. For the purposes 105 | of this License, Derivative Works shall not include works that remain 106 | separable from, or merely link (or bind by name) to the interfaces of, 107 | the Work and Derivative Works thereof. 108 | 109 | "Contribution" shall mean any work of authorship, including 110 | the original version of the Work and any modifications or additions 111 | to that Work or Derivative Works thereof, that is intentionally 112 | submitted to Licensor for inclusion in the Work by the copyright owner 113 | or by an individual or Legal Entity authorized to submit on behalf of 114 | the copyright owner. For the purposes of this definition, "submitted" 115 | means any form of electronic, verbal, or written communication sent 116 | to the Licensor or its representatives, including but not limited to 117 | communication on electronic mailing lists, source code control systems, 118 | and issue tracking systems that are managed by, or on behalf of, the 119 | Licensor for the purpose of discussing and improving the Work, but 120 | excluding communication that is conspicuously marked or otherwise 121 | designated in writing by the copyright owner as "Not a Contribution." 122 | 123 | "Contributor" shall mean Licensor and any individual or Legal Entity 124 | on behalf of whom a Contribution has been received by Licensor and 125 | subsequently incorporated within the Work. 126 | 127 | 2. Grant of Copyright License. Subject to the terms and conditions of 128 | this License, each Contributor hereby grants to You a perpetual, 129 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 130 | copyright license to reproduce, prepare Derivative Works of, 131 | publicly display, publicly perform, sublicense, and distribute the 132 | Work and such Derivative Works in Source or Object form. 133 | 134 | 3. Grant of Patent License. Subject to the terms and conditions of 135 | this License, each Contributor hereby grants to You a perpetual, 136 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 137 | (except as stated in this section) patent license to make, have made, 138 | use, offer to sell, sell, import, and otherwise transfer the Work, 139 | where such license applies only to those patent claims licensable 140 | by such Contributor that are necessarily infringed by their 141 | Contribution(s) alone or by combination of their Contribution(s) 142 | with the Work to which such Contribution(s) was submitted. If You 143 | institute patent litigation against any entity (including a 144 | cross-claim or counterclaim in a lawsuit) alleging that the Work 145 | or a Contribution incorporated within the Work constitutes direct 146 | or contributory patent infringement, then any patent licenses 147 | granted to You under this License for that Work shall terminate 148 | as of the date such litigation is filed. 149 | 150 | 4. Redistribution. You may reproduce and distribute copies of the 151 | Work or Derivative Works thereof in any medium, with or without 152 | modifications, and in Source or Object form, provided that You 153 | meet the following conditions: 154 | 155 | (a) You must give any other recipients of the Work or 156 | Derivative Works a copy of this License; and 157 | 158 | (b) You must cause any modified files to carry prominent notices 159 | stating that You changed the files; and 160 | 161 | (c) You must retain, in the Source form of any Derivative Works 162 | that You distribute, all copyright, patent, trademark, and 163 | attribution notices from the Source form of the Work, 164 | excluding those notices that do not pertain to any part of 165 | the Derivative Works; and 166 | 167 | (d) If the Work includes a "NOTICE" text file as part of its 168 | distribution, then any Derivative Works that You distribute must 169 | include a readable copy of the attribution notices contained 170 | within such NOTICE file, excluding those notices that do not 171 | pertain to any part of the Derivative Works, in at least one 172 | of the following places: within a NOTICE text file distributed 173 | as part of the Derivative Works; within the Source form or 174 | documentation, if provided along with the Derivative Works; or, 175 | within a display generated by the Derivative Works, if and 176 | wherever such third-party notices normally appear. The contents 177 | of the NOTICE file are for informational purposes only and 178 | do not modify the License. You may add Your own attribution 179 | notices within Derivative Works that You distribute, alongside 180 | or as an addendum to the NOTICE text from the Work, provided 181 | that such additional attribution notices cannot be construed 182 | as modifying the License. 183 | 184 | You may add Your own copyright statement to Your modifications and 185 | may provide additional or different license terms and conditions 186 | for use, reproduction, or distribution of Your modifications, or 187 | for any such Derivative Works as a whole, provided Your use, 188 | reproduction, and distribution of the Work otherwise complies with 189 | the conditions stated in this License. 190 | 191 | 5. Submission of Contributions. Unless You explicitly state otherwise, 192 | any Contribution intentionally submitted for inclusion in the Work 193 | by You to the Licensor shall be under the terms and conditions of 194 | this License, without any additional terms or conditions. 195 | Notwithstanding the above, nothing herein shall supersede or modify 196 | the terms of any separate license agreement you may have executed 197 | with Licensor regarding such Contributions. 198 | 199 | 6. Trademarks. This License does not grant permission to use the trade 200 | names, trademarks, service marks, or product names of the Licensor, 201 | except as required for reasonable and customary use in describing the 202 | origin of the Work and reproducing the content of the NOTICE file. 203 | 204 | 7. Disclaimer of Warranty. Unless required by applicable law or 205 | agreed to in writing, Licensor provides the Work (and each 206 | Contributor provides its Contributions) on an "AS IS" BASIS, 207 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 208 | implied, including, without limitation, any warranties or conditions 209 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 210 | PARTICULAR PURPOSE. You are solely responsible for determining the 211 | appropriateness of using or redistributing the Work and assume any 212 | risks associated with Your exercise of permissions under this License. 213 | 214 | 8. Limitation of Liability. In no event and under no legal theory, 215 | whether in tort (including negligence), contract, or otherwise, 216 | unless required by applicable law (such as deliberate and grossly 217 | negligent acts) or agreed to in writing, shall any Contributor be 218 | liable to You for damages, including any direct, indirect, special, 219 | incidental, or consequential damages of any character arising as a 220 | result of this License or out of the use or inability to use the 221 | Work (including but not limited to damages for loss of goodwill, 222 | work stoppage, computer failure or malfunction, or any and all 223 | other commercial damages or losses), even if such Contributor 224 | has been advised of the possibility of such damages. 225 | 226 | 9. Accepting Warranty or Additional Liability. While redistributing 227 | the Work or Derivative Works thereof, You may choose to offer, 228 | and charge a fee for, acceptance of support, warranty, indemnity, 229 | or other liability obligations and/or rights consistent with this 230 | License. However, in accepting such obligations, You may act only 231 | on Your own behalf and on Your sole responsibility, not on behalf 232 | of any other Contributor, and only if You agree to indemnify, 233 | defend, and hold each Contributor harmless for any liability 234 | incurred by, or claims asserted against, such Contributor by reason 235 | of your accepting any such warranty or additional liability. 236 | 237 | END OF TERMS AND CONDITIONS 238 | 239 | APPENDIX: How to apply the Apache License to your work. 240 | 241 | To apply the Apache License to your work, attach the following 242 | boilerplate notice, with the fields enclosed by brackets "[]" 243 | replaced with your own identifying information. (Don't include 244 | the brackets!) The text should be enclosed in the appropriate 245 | comment syntax for the file format. We also recommend that a 246 | file or class name and description of purpose be included on the 247 | same "printed page" as the copyright notice for easier 248 | identification within third-party archives. 249 | 250 | Copyright [yyyy] [name of copyright owner] 251 | 252 | Licensed under the Apache License, Version 2.0 (the "License"); 253 | you may not use this file except in compliance with the License. 254 | You may obtain a copy of the License at 255 | 256 | http://www.apache.org/licenses/LICENSE-2.0 257 | 258 | Unless required by applicable law or agreed to in writing, software 259 | distributed under the License is distributed on an "AS IS" BASIS, 260 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 261 | See the License for the specific language governing permissions and 262 | limitations under the License. 263 | 264 | -------------------------------------------------------------------------------- /patch/33-jbosh-android.patch: -------------------------------------------------------------------------------- 1 | diff --git com/kenai/jbosh/ApacheHTTPResponse.java com/kenai/jbosh/ApacheHTTPResponse.java 2 | new file mode 100644 3 | index 0000000..9f6731f 4 | --- /dev/null 5 | +++ com/kenai/jbosh/ApacheHTTPResponse.java 6 | @@ -0,0 +1,253 @@ 7 | +/* 8 | + * Copyright 2009 Guenther Niess 9 | + * 10 | + * Licensed under the Apache License, Version 2.0 (the "License"); 11 | + * you may not use this file except in compliance with the License. 12 | + * You may obtain a copy of the License at 13 | + * 14 | + * http://www.apache.org/licenses/LICENSE-2.0 15 | + * 16 | + * Unless required by applicable law or agreed to in writing, software 17 | + * distributed under the License is distributed on an "AS IS" BASIS, 18 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | + * See the License for the specific language governing permissions and 20 | + * limitations under the License. 21 | + */ 22 | + 23 | +package com.kenai.jbosh; 24 | + 25 | +import java.io.IOException; 26 | +import java.util.concurrent.locks.Lock; 27 | +import java.util.concurrent.locks.ReentrantLock; 28 | + 29 | +import org.apache.http.HttpEntity; 30 | +import org.apache.http.HttpResponse; 31 | +import org.apache.http.client.HttpClient; 32 | +import org.apache.http.client.methods.HttpPost; 33 | +import org.apache.http.entity.ByteArrayEntity; 34 | + 35 | +import org.apache.http.protocol.BasicHttpContext; 36 | +import org.apache.http.protocol.HttpContext; 37 | +import org.apache.http.util.EntityUtils; 38 | + 39 | +final class ApacheHTTPResponse implements HTTPResponse { 40 | + 41 | + /////////////////////////////////////////////////////////////////////////// 42 | + // Constants: 43 | + 44 | + /** 45 | + * Name of the accept encoding header. 46 | + */ 47 | + private static final String ACCEPT_ENCODING = "Accept-Encoding"; 48 | + 49 | + /** 50 | + * Value to use for the ACCEPT_ENCODING header. 51 | + */ 52 | + private static final String ACCEPT_ENCODING_VAL = 53 | + ZLIBCodec.getID() + ", " + GZIPCodec.getID(); 54 | + 55 | + /** 56 | + * Name of the character set to encode the body to/from. 57 | + */ 58 | + private static final String CHARSET = "UTF-8"; 59 | + 60 | + /** 61 | + * Content type to use when transmitting the body data. 62 | + */ 63 | + private static final String CONTENT_TYPE = "text/xml; charset=utf-8"; 64 | + 65 | + /////////////////////////////////////////////////////////////////////////// 66 | + // Class variables: 67 | + 68 | + /** 69 | + * Lock used for internal synchronization. 70 | + */ 71 | + private final Lock lock = new ReentrantLock(); 72 | + 73 | + /** 74 | + * The execution state of an HTTP process. 75 | + */ 76 | + private final HttpContext context; 77 | + 78 | + /** 79 | + * HttpClient instance to use to communicate. 80 | + */ 81 | + private final HttpClient client; 82 | + 83 | + /** 84 | + * The HTTP POST request is sent to the server. 85 | + */ 86 | + private final HttpPost post; 87 | + 88 | + /** 89 | + * A flag which indicates if the transmission was already done. 90 | + */ 91 | + private boolean sent; 92 | + 93 | + /** 94 | + * Exception to throw when the response data is attempted to be accessed, 95 | + * or {@code null} if no exception should be thrown. 96 | + */ 97 | + private BOSHException toThrow; 98 | + 99 | + /** 100 | + * The response body which was received from the server or {@code null} 101 | + * if that has not yet happened. 102 | + */ 103 | + private AbstractBody body; 104 | + 105 | + /** 106 | + * The HTTP response status code. 107 | + */ 108 | + private int statusCode; 109 | + 110 | + /////////////////////////////////////////////////////////////////////////// 111 | + // Constructors: 112 | + 113 | + /** 114 | + * Create and send a new request to the upstream connection manager, 115 | + * providing deferred access to the results to be returned. 116 | + * 117 | + * @param client client instance to use when sending the request 118 | + * @param cfg client configuration 119 | + * @param params connection manager parameters from the session creation 120 | + * response, or {@code null} if the session has not yet been established 121 | + * @param request body of the client request 122 | + */ 123 | + ApacheHTTPResponse( 124 | + final HttpClient client, 125 | + final BOSHClientConfig cfg, 126 | + final CMSessionParams params, 127 | + final AbstractBody request) { 128 | + super(); 129 | + this.client = client; 130 | + this.context = new BasicHttpContext(); 131 | + this.post = new HttpPost(cfg.getURI().toString()); 132 | + this.sent = false; 133 | + 134 | + try { 135 | + String xml = request.toXML(); 136 | + byte[] data = xml.getBytes(CHARSET); 137 | + 138 | + String encoding = null; 139 | + if (cfg.isCompressionEnabled() && params != null) { 140 | + AttrAccept accept = params.getAccept(); 141 | + if (accept != null) { 142 | + if (accept.isAccepted(ZLIBCodec.getID())) { 143 | + encoding = ZLIBCodec.getID(); 144 | + data = ZLIBCodec.encode(data); 145 | + } else if (accept.isAccepted(GZIPCodec.getID())) { 146 | + encoding = GZIPCodec.getID(); 147 | + data = GZIPCodec.encode(data); 148 | + } 149 | + } 150 | + } 151 | + 152 | + ByteArrayEntity entity = new ByteArrayEntity(data); 153 | + entity.setContentType(CONTENT_TYPE); 154 | + if (encoding != null) { 155 | + entity.setContentEncoding(encoding); 156 | + } 157 | + post.setEntity(entity); 158 | + if (cfg.isCompressionEnabled()) { 159 | + post.setHeader(ACCEPT_ENCODING, ACCEPT_ENCODING_VAL); 160 | + } 161 | + } catch (Exception e) { 162 | + toThrow = new BOSHException("Could not generate request", e); 163 | + } 164 | + } 165 | + 166 | + /////////////////////////////////////////////////////////////////////////// 167 | + // HTTPResponse interface methods: 168 | + 169 | + /** 170 | + * Abort the client transmission and response processing. 171 | + */ 172 | + public void abort() { 173 | + if (post != null) { 174 | + post.abort(); 175 | + toThrow = new BOSHException("HTTP request aborted"); 176 | + } 177 | + } 178 | + 179 | + /** 180 | + * Wait for and then return the response body. 181 | + * 182 | + * @return body of the response 183 | + * @throws InterruptedException if interrupted while awaiting the response 184 | + * @throws BOSHException on communication failure 185 | + */ 186 | + public AbstractBody getBody() throws InterruptedException, BOSHException { 187 | + if (toThrow != null) { 188 | + throw(toThrow); 189 | + } 190 | + lock.lock(); 191 | + try { 192 | + if (!sent) { 193 | + awaitResponse(); 194 | + } 195 | + } finally { 196 | + lock.unlock(); 197 | + } 198 | + return body; 199 | + } 200 | + 201 | + /** 202 | + * Wait for and then return the response HTTP status code. 203 | + * 204 | + * @return HTTP status code of the response 205 | + * @throws InterruptedException if interrupted while awaiting the response 206 | + * @throws BOSHException on communication failure 207 | + */ 208 | + public int getHTTPStatus() throws InterruptedException, BOSHException { 209 | + if (toThrow != null) { 210 | + throw(toThrow); 211 | + } 212 | + lock.lock(); 213 | + try { 214 | + if (!sent) { 215 | + awaitResponse(); 216 | + } 217 | + } finally { 218 | + lock.unlock(); 219 | + } 220 | + return statusCode; 221 | + } 222 | + 223 | + /////////////////////////////////////////////////////////////////////////// 224 | + // Package-private methods: 225 | + 226 | + /** 227 | + * Await the response, storing the result in the instance variables of 228 | + * this class when they arrive. 229 | + * 230 | + * @throws InterruptedException if interrupted while awaiting the response 231 | + * @throws BOSHException on communication failure 232 | + */ 233 | + private synchronized void awaitResponse() throws BOSHException { 234 | + HttpEntity entity = null; 235 | + try { 236 | + HttpResponse httpResp = client.execute(post, context); 237 | + entity = httpResp.getEntity(); 238 | + byte[] data = EntityUtils.toByteArray(entity); 239 | + String encoding = entity.getContentEncoding() != null ? 240 | + entity.getContentEncoding().getValue() : 241 | + null; 242 | + if (ZLIBCodec.getID().equalsIgnoreCase(encoding)) { 243 | + data = ZLIBCodec.decode(data); 244 | + } else if (GZIPCodec.getID().equalsIgnoreCase(encoding)) { 245 | + data = GZIPCodec.decode(data); 246 | + } 247 | + body = StaticBody.fromString(new String(data, CHARSET)); 248 | + statusCode = httpResp.getStatusLine().getStatusCode(); 249 | + sent = true; 250 | + } catch (IOException iox) { 251 | + abort(); 252 | + toThrow = new BOSHException("Could not obtain response", iox); 253 | + throw(toThrow); 254 | + } catch (RuntimeException ex) { 255 | + abort(); 256 | + throw(ex); 257 | + } 258 | + } 259 | +} 260 | diff --git com/kenai/jbosh/ApacheHTTPSender.java com/kenai/jbosh/ApacheHTTPSender.java 261 | new file mode 100644 262 | index 0000000..2abb4ee 263 | --- /dev/null 264 | +++ com/kenai/jbosh/ApacheHTTPSender.java 265 | @@ -0,0 +1,156 @@ 266 | +/* 267 | + * Copyright 2009 Guenther Niess 268 | + * 269 | + * Licensed under the Apache License, Version 2.0 (the "License"); 270 | + * you may not use this file except in compliance with the License. 271 | + * You may obtain a copy of the License at 272 | + * 273 | + * http://www.apache.org/licenses/LICENSE-2.0 274 | + * 275 | + * Unless required by applicable law or agreed to in writing, software 276 | + * distributed under the License is distributed on an "AS IS" BASIS, 277 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 278 | + * See the License for the specific language governing permissions and 279 | + * limitations under the License. 280 | + */ 281 | + 282 | +package com.kenai.jbosh; 283 | + 284 | +import java.util.concurrent.locks.Lock; 285 | +import java.util.concurrent.locks.ReentrantLock; 286 | + 287 | +import org.apache.http.HttpHost; 288 | +import org.apache.http.HttpVersion; 289 | +import org.apache.http.client.HttpClient; 290 | +import org.apache.http.conn.ClientConnectionManager; 291 | +import org.apache.http.conn.params.ConnManagerParams; 292 | +import org.apache.http.conn.params.ConnRoutePNames; 293 | +import org.apache.http.conn.scheme.PlainSocketFactory; 294 | +import org.apache.http.conn.scheme.Scheme; 295 | +import org.apache.http.conn.scheme.SchemeRegistry; 296 | +import org.apache.http.conn.ssl.SSLSocketFactory; 297 | +import org.apache.http.impl.client.DefaultHttpClient; 298 | +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 299 | +import org.apache.http.params.BasicHttpParams; 300 | +import org.apache.http.params.HttpParams; 301 | +import org.apache.http.params.HttpProtocolParams; 302 | + 303 | +/** 304 | + * Implementation of the {@code HTTPSender} interface which uses the 305 | + * Apache HttpClient API to send messages to the connection manager. 306 | + */ 307 | +final class ApacheHTTPSender implements HTTPSender { 308 | + 309 | + /** 310 | + * Lock used for internal synchronization. 311 | + */ 312 | + private final Lock lock = new ReentrantLock(); 313 | + 314 | + /** 315 | + * Session configuration. 316 | + */ 317 | + private BOSHClientConfig cfg; 318 | + 319 | + /** 320 | + * HttpClient instance to use to communicate. 321 | + */ 322 | + private HttpClient httpClient; 323 | + 324 | + /////////////////////////////////////////////////////////////////////////// 325 | + // Constructors: 326 | + 327 | + /** 328 | + * Prevent construction apart from our package. 329 | + */ 330 | + ApacheHTTPSender() { 331 | + // Load Apache HTTP client class 332 | + HttpClient.class.getName(); 333 | + } 334 | + 335 | + /////////////////////////////////////////////////////////////////////////// 336 | + // HTTPSender interface methods: 337 | + 338 | + /** 339 | + * {@inheritDoc} 340 | + */ 341 | + public void init(final BOSHClientConfig session) { 342 | + lock.lock(); 343 | + try { 344 | + cfg = session; 345 | + httpClient = initHttpClient(session); 346 | + } finally { 347 | + lock.unlock(); 348 | + } 349 | + } 350 | + 351 | + /** 352 | + * {@inheritDoc} 353 | + */ 354 | + public void destroy() { 355 | + lock.lock(); 356 | + try { 357 | + if (httpClient != null) { 358 | + httpClient.getConnectionManager().shutdown(); 359 | + } 360 | + } finally { 361 | + cfg = null; 362 | + httpClient = null; 363 | + lock.unlock(); 364 | + } 365 | + } 366 | + 367 | + /** 368 | + * {@inheritDoc} 369 | + */ 370 | + public HTTPResponse send( 371 | + final CMSessionParams params, 372 | + final AbstractBody body) { 373 | + HttpClient mClient; 374 | + BOSHClientConfig mCfg; 375 | + lock.lock(); 376 | + try { 377 | + if (httpClient == null) { 378 | + httpClient = initHttpClient(cfg); 379 | + } 380 | + mClient = httpClient; 381 | + mCfg = cfg; 382 | + } finally { 383 | + lock.unlock(); 384 | + } 385 | + return new ApacheHTTPResponse(mClient, mCfg, params, body); 386 | + } 387 | + 388 | + /////////////////////////////////////////////////////////////////////////// 389 | + // Package-private methods: 390 | + 391 | + private synchronized HttpClient initHttpClient(final BOSHClientConfig config) { 392 | + // Create and initialize HTTP parameters 393 | + HttpParams params = new BasicHttpParams(); 394 | + ConnManagerParams.setMaxTotalConnections(params, 100); 395 | + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 396 | + HttpProtocolParams.setUseExpectContinue(params, false); 397 | + if (config != null && 398 | + config.getProxyHost() != null && 399 | + config.getProxyPort() != 0) { 400 | + HttpHost proxy = new HttpHost( 401 | + config.getProxyHost(), 402 | + config.getProxyPort()); 403 | + params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 404 | + } 405 | + 406 | + // Create and initialize scheme registry 407 | + SchemeRegistry schemeRegistry = new SchemeRegistry(); 408 | + schemeRegistry.register( 409 | + new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 410 | + SSLSocketFactory sslFactory = SSLSocketFactory.getSocketFactory(); 411 | + sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 412 | + schemeRegistry.register( 413 | + new Scheme("https", sslFactory, 443)); 414 | + 415 | + // Create an HttpClient with the ThreadSafeClientConnManager. 416 | + // This connection manager must be used if more than one thread will 417 | + // be using the HttpClient. 418 | + ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry); 419 | + return new DefaultHttpClient(cm, params); 420 | + } 421 | +} 422 | diff --git com/kenai/jbosh/BodyParserXmlPull.java com/kenai/jbosh/BodyParserXmlPull.java 423 | index cc95236..5f23b06 100644 424 | --- com/kenai/jbosh/BodyParserXmlPull.java 425 | +++ com/kenai/jbosh/BodyParserXmlPull.java 426 | @@ -22,7 +22,6 @@ import java.lang.ref.SoftReference; 427 | import java.util.logging.Level; 428 | import java.util.logging.Logger; 429 | import javax.xml.XMLConstants; 430 | -import javax.xml.namespace.QName; 431 | import org.xmlpull.v1.XmlPullParser; 432 | import org.xmlpull.v1.XmlPullParserException; 433 | import org.xmlpull.v1.XmlPullParserFactory; 434 | diff --git com/kenai/jbosh/BodyQName.java com/kenai/jbosh/BodyQName.java 435 | index fc7ab0c..83acdf1 100644 436 | --- com/kenai/jbosh/BodyQName.java 437 | +++ com/kenai/jbosh/BodyQName.java 438 | @@ -16,8 +16,6 @@ 439 | 440 | package com.kenai.jbosh; 441 | 442 | -import javax.xml.namespace.QName; 443 | - 444 | /** 445 | * Qualified name of an attribute of the wrapper element. This class is 446 | * analagous to the {@code javax.xml.namespace.QName} class. 447 | diff --git com/kenai/jbosh/ComposableBody.java com/kenai/jbosh/ComposableBody.java 448 | index d375478..7f3b159 100644 449 | --- com/kenai/jbosh/ComposableBody.java 450 | +++ com/kenai/jbosh/ComposableBody.java 451 | @@ -58,8 +58,8 @@ public final class ComposableBody extends AbstractBody { 452 | * BOSH message. 453 | */ 454 | private static final Pattern BOSH_START = 455 | - Pattern.compile("<" + "(?:(?:[^:\t\n\r >]+:)|(?:\\{[^\\}>]*?}))?" 456 | - + "body" + "(?:[\t\n\r ][^>]*?)?" + "(/>|>)"); 457 | + Pattern.compile("<" 458 | + + "body" + "(?:[\t\n\r ][^>]*?)?" + "(/>|>)", Pattern.UNICODE_CASE); 459 | 460 | /** 461 | * Map of all attributes to their values. 462 | 463 | -------------------------------------------------------------------------------- /src/novell-openldap-jldap/com/novell/sasl/client/DigestMD5SaslClient.java: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** 2 | * $OpenLDAP: /com/novell/sasl/client/DigestMD5SaslClient.java,v 1.4 2005/01/17 15:00:54 sunilk Exp $ 3 | * 4 | * Copyright (C) 2003 Novell, Inc. All Rights Reserved. 5 | * 6 | * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 7 | * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT 8 | * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS 9 | * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" 10 | * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION 11 | * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP 12 | * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT 13 | * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 14 | ******************************************************************************/ 15 | package com.novell.sasl.client; 16 | 17 | import org.apache.harmony.javax.security.sasl.*; 18 | import org.apache.harmony.javax.security.auth.callback.*; 19 | import java.security.SecureRandom; 20 | import java.security.MessageDigest; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.io.UnsupportedEncodingException; 23 | import java.io.IOException; 24 | import java.util.*; 25 | 26 | /** 27 | * Implements the Client portion of DigestMD5 Sasl mechanism. 28 | */ 29 | public class DigestMD5SaslClient implements SaslClient 30 | { 31 | private String m_authorizationId = ""; 32 | private String m_protocol = ""; 33 | private String m_serverName = ""; 34 | private Map m_props; 35 | private CallbackHandler m_cbh; 36 | private int m_state; 37 | private String m_qopValue = ""; 38 | private char[] m_HA1 = null; 39 | private String m_digestURI; 40 | private DigestChallenge m_dc; 41 | private String m_clientNonce = ""; 42 | private String m_realm = ""; 43 | private String m_name = ""; 44 | 45 | private static final int STATE_INITIAL = 0; 46 | private static final int STATE_DIGEST_RESPONSE_SENT = 1; 47 | private static final int STATE_VALID_SERVER_RESPONSE = 2; 48 | private static final int STATE_INVALID_SERVER_RESPONSE = 3; 49 | private static final int STATE_DISPOSED = 4; 50 | 51 | private static final int NONCE_BYTE_COUNT = 32; 52 | private static final int NONCE_HEX_COUNT = 2*NONCE_BYTE_COUNT; 53 | 54 | private static final String DIGEST_METHOD = "AUTHENTICATE"; 55 | 56 | /** 57 | * Creates an DigestMD5SaslClient object using the parameters supplied. 58 | * Assumes that the QOP, STRENGTH, and SERVER_AUTH properties are 59 | * contained in props 60 | * 61 | * @param authorizationId The possibly null protocol-dependent 62 | * identification to be used for authorization. If 63 | * null or empty, the server derives an authorization 64 | * ID from the client's authentication credentials. 65 | * When the SASL authentication completes 66 | * successfully, the specified entity is granted 67 | * access. 68 | * 69 | * @param protocol The non-null string name of the protocol for which 70 | * the authentication is being performed (e.g. "ldap") 71 | * 72 | * @param serverName The non-null fully qualified host name of the server 73 | * to authenticate to 74 | * 75 | * @param props The possibly null set of properties used to select 76 | * the SASL mechanism and to configure the 77 | * authentication exchange of the selected mechanism. 78 | * See the Sasl class for a list of standard properties. 79 | * Other, possibly mechanism-specific, properties can 80 | * be included. Properties not relevant to the selected 81 | * mechanism are ignored. 82 | * 83 | * @param cbh The possibly null callback handler to used by the 84 | * SASL mechanisms to get further information from the 85 | * application/library to complete the authentication. 86 | * For example, a SASL mechanism might require the 87 | * authentication ID, password and realm from the 88 | * caller. The authentication ID is requested by using 89 | * a NameCallback. The password is requested by using 90 | * a PasswordCallback. The realm is requested by using 91 | * a RealmChoiceCallback if there is a list of realms 92 | * to choose from, and by using a RealmCallback if the 93 | * realm must be entered. 94 | * 95 | * @return A possibly null SaslClient created using the 96 | * parameters supplied. If null, this factory cannot 97 | * produce a SaslClient using the parameters supplied. 98 | * 99 | * @exception SaslException If a SaslClient instance cannot be created 100 | * because of an error 101 | */ 102 | public static SaslClient getClient( 103 | String authorizationId, 104 | String protocol, 105 | String serverName, 106 | Map props, 107 | CallbackHandler cbh) 108 | { 109 | String desiredQOP = (String)props.get(Sasl.QOP); 110 | String desiredStrength = (String)props.get(Sasl.STRENGTH); 111 | String serverAuth = (String)props.get(Sasl.SERVER_AUTH); 112 | 113 | //only support qop equal to auth 114 | if ((desiredQOP != null) && !"auth".equals(desiredQOP)) 115 | return null; 116 | 117 | //doesn't support server authentication 118 | if ((serverAuth != null) && !"false".equals(serverAuth)) 119 | return null; 120 | 121 | //need a callback handler to get the password 122 | if (cbh == null) 123 | return null; 124 | 125 | return new DigestMD5SaslClient(authorizationId, protocol, 126 | serverName, props, cbh); 127 | } 128 | 129 | /** 130 | * Creates an DigestMD5SaslClient object using the parameters supplied. 131 | * Assumes that the QOP, STRENGTH, and SERVER_AUTH properties are 132 | * contained in props 133 | * 134 | * @param authorizationId The possibly null protocol-dependent 135 | * identification to be used for authorization. If 136 | * null or empty, the server derives an authorization 137 | * ID from the client's authentication credentials. 138 | * When the SASL authentication completes 139 | * successfully, the specified entity is granted 140 | * access. 141 | * 142 | * @param protocol The non-null string name of the protocol for which 143 | * the authentication is being performed (e.g. "ldap") 144 | * 145 | * @param serverName The non-null fully qualified host name of the server 146 | * to authenticate to 147 | * 148 | * @param props The possibly null set of properties used to select 149 | * the SASL mechanism and to configure the 150 | * authentication exchange of the selected mechanism. 151 | * See the Sasl class for a list of standard properties. 152 | * Other, possibly mechanism-specific, properties can 153 | * be included. Properties not relevant to the selected 154 | * mechanism are ignored. 155 | * 156 | * @param cbh The possibly null callback handler to used by the 157 | * SASL mechanisms to get further information from the 158 | * application/library to complete the authentication. 159 | * For example, a SASL mechanism might require the 160 | * authentication ID, password and realm from the 161 | * caller. The authentication ID is requested by using 162 | * a NameCallback. The password is requested by using 163 | * a PasswordCallback. The realm is requested by using 164 | * a RealmChoiceCallback if there is a list of realms 165 | * to choose from, and by using a RealmCallback if the 166 | * realm must be entered. 167 | * 168 | */ 169 | private DigestMD5SaslClient( 170 | String authorizationId, 171 | String protocol, 172 | String serverName, 173 | Map props, 174 | CallbackHandler cbh) 175 | { 176 | m_authorizationId = authorizationId; 177 | m_protocol = protocol; 178 | m_serverName = serverName; 179 | m_props = props; 180 | m_cbh = cbh; 181 | 182 | m_state = STATE_INITIAL; 183 | } 184 | 185 | /** 186 | * Determines if this mechanism has an optional initial response. If true, 187 | * caller should call evaluateChallenge() with an empty array to get the 188 | * initial response. 189 | * 190 | * @return true if this mechanism has an initial response 191 | */ 192 | public boolean hasInitialResponse() 193 | { 194 | return false; 195 | } 196 | 197 | /** 198 | * Determines if the authentication exchange has completed. This method 199 | * may be called at any time, but typically, it will not be called until 200 | * the caller has received indication from the server (in a protocol- 201 | * specific manner) that the exchange has completed. 202 | * 203 | * @return true if the authentication exchange has completed; 204 | * false otherwise. 205 | */ 206 | public boolean isComplete() 207 | { 208 | if ((m_state == STATE_VALID_SERVER_RESPONSE) || 209 | (m_state == STATE_INVALID_SERVER_RESPONSE) || 210 | (m_state == STATE_DISPOSED)) 211 | return true; 212 | else 213 | return false; 214 | } 215 | 216 | /** 217 | * Unwraps a byte array received from the server. This method can be called 218 | * only after the authentication exchange has completed (i.e., when 219 | * isComplete() returns true) and only if the authentication exchange has 220 | * negotiated integrity and/or privacy as the quality of protection; 221 | * otherwise, an IllegalStateException is thrown. 222 | * 223 | * incoming is the contents of the SASL buffer as defined in RFC 2222 224 | * without the leading four octet field that represents the length. 225 | * offset and len specify the portion of incoming to use. 226 | * 227 | * @param incoming A non-null byte array containing the encoded bytes 228 | * from the server 229 | * @param offset The starting position at incoming of the bytes to use 230 | * 231 | * @param len The number of bytes from incoming to use 232 | * 233 | * @return A non-null byte array containing the decoded bytes 234 | * 235 | */ 236 | public byte[] unwrap( 237 | byte[] incoming, 238 | int offset, 239 | int len) 240 | throws SaslException 241 | { 242 | throw new IllegalStateException( 243 | "unwrap: QOP has neither integrity nor privacy>"); 244 | } 245 | 246 | /** 247 | * Wraps a byte array to be sent to the server. This method can be called 248 | * only after the authentication exchange has completed (i.e., when 249 | * isComplete() returns true) and only if the authentication exchange has 250 | * negotiated integrity and/or privacy as the quality of protection; 251 | * otherwise, an IllegalStateException is thrown. 252 | * 253 | * The result of this method will make up the contents of the SASL buffer as 254 | * defined in RFC 2222 without the leading four octet field that represents 255 | * the length. offset and len specify the portion of outgoing to use. 256 | * 257 | * @param outgoing A non-null byte array containing the bytes to encode 258 | * @param offset The starting position at outgoing of the bytes to use 259 | * @param len The number of bytes from outgoing to use 260 | * 261 | * @return A non-null byte array containing the encoded bytes 262 | * 263 | * @exception SaslException if incoming cannot be successfully unwrapped. 264 | * 265 | * @exception IllegalStateException if the authentication exchange has 266 | * not completed, or if the negotiated quality of 267 | * protection has neither integrity nor privacy. 268 | */ 269 | public byte[] wrap( 270 | byte[] outgoing, 271 | int offset, 272 | int len) 273 | throws SaslException 274 | { 275 | throw new IllegalStateException( 276 | "wrap: QOP has neither integrity nor privacy>"); 277 | } 278 | 279 | /** 280 | * Retrieves the negotiated property. This method can be called only after 281 | * the authentication exchange has completed (i.e., when isComplete() 282 | * returns true); otherwise, an IllegalStateException is thrown. 283 | * 284 | * @param propName The non-null property name 285 | * 286 | * @return The value of the negotiated property. If null, the property was 287 | * not negotiated or is not applicable to this mechanism. 288 | * 289 | * @exception IllegalStateException if this authentication exchange has 290 | * not completed 291 | */ 292 | public Object getNegotiatedProperty( 293 | String propName) 294 | { 295 | if (m_state != STATE_VALID_SERVER_RESPONSE) 296 | throw new IllegalStateException( 297 | "getNegotiatedProperty: authentication exchange not complete."); 298 | 299 | if (Sasl.QOP.equals(propName)) 300 | return "auth"; 301 | else 302 | return null; 303 | } 304 | 305 | /** 306 | * Disposes of any system resources or security-sensitive information the 307 | * SaslClient might be using. Invoking this method invalidates the 308 | * SaslClient instance. This method is idempotent. 309 | * 310 | * @exception SaslException if a problem was encountered while disposing 311 | * of the resources 312 | */ 313 | public void dispose() 314 | throws SaslException 315 | { 316 | if (m_state != STATE_DISPOSED) 317 | { 318 | m_state = STATE_DISPOSED; 319 | } 320 | } 321 | 322 | /** 323 | * Evaluates the challenge data and generates a response. If a challenge 324 | * is received from the server during the authentication process, this 325 | * method is called to prepare an appropriate next response to submit to 326 | * the server. 327 | * 328 | * @param challenge The non-null challenge sent from the server. The 329 | * challenge array may have zero length. 330 | * 331 | * @return The possibly null reponse to send to the server. It is null 332 | * if the challenge accompanied a "SUCCESS" status and the 333 | * challenge only contains data for the client to update its 334 | * state and no response needs to be sent to the server. 335 | * The response is a zero-length byte array if the client is to 336 | * send a response with no data. 337 | * 338 | * @exception SaslException If an error occurred while processing the 339 | * challenge or generating a response. 340 | */ 341 | public byte[] evaluateChallenge( 342 | byte[] challenge) 343 | throws SaslException 344 | { 345 | byte[] response = null; 346 | 347 | //printState(); 348 | switch (m_state) 349 | { 350 | case STATE_INITIAL: 351 | if (challenge.length == 0) 352 | throw new SaslException("response = byte[0]"); 353 | else 354 | try 355 | { 356 | response = createDigestResponse(challenge). 357 | getBytes("UTF-8"); 358 | m_state = STATE_DIGEST_RESPONSE_SENT; 359 | } 360 | catch (java.io.UnsupportedEncodingException e) 361 | { 362 | throw new SaslException( 363 | "UTF-8 encoding not suppported by platform", e); 364 | } 365 | break; 366 | case STATE_DIGEST_RESPONSE_SENT: 367 | if (checkServerResponseAuth(challenge)) 368 | m_state = STATE_VALID_SERVER_RESPONSE; 369 | else 370 | { 371 | m_state = STATE_INVALID_SERVER_RESPONSE; 372 | throw new SaslException("Could not validate response-auth " + 373 | "value from server"); 374 | } 375 | break; 376 | case STATE_VALID_SERVER_RESPONSE: 377 | case STATE_INVALID_SERVER_RESPONSE: 378 | throw new SaslException("Authentication sequence is complete"); 379 | case STATE_DISPOSED: 380 | throw new SaslException("Client has been disposed"); 381 | default: 382 | throw new SaslException("Unknown client state."); 383 | } 384 | 385 | return response; 386 | } 387 | 388 | /** 389 | * This function takes a 16 byte binary md5-hash value and creates a 32 390 | * character (plus a terminating null character) hex-digit 391 | * representation of binary data. 392 | * 393 | * @param hash 16 byte binary md5-hash value in bytes 394 | * 395 | * @return 32 character (plus a terminating null character) hex-digit 396 | * representation of binary data. 397 | */ 398 | char[] convertToHex( 399 | byte[] hash) 400 | { 401 | int i; 402 | byte j; 403 | byte fifteen = 15; 404 | char[] hex = new char[32]; 405 | 406 | for (i = 0; i < 16; i++) 407 | { 408 | //convert value of top 4 bits to hex char 409 | hex[i*2] = getHexChar((byte)((hash[i] & 0xf0) >> 4)); 410 | //convert value of bottom 4 bits to hex char 411 | hex[(i*2)+1] = getHexChar((byte)(hash[i] & 0x0f)); 412 | } 413 | 414 | return hex; 415 | } 416 | 417 | /** 418 | * Calculates the HA1 portion of the response 419 | * 420 | * @param algorithm Algorith to use. 421 | * @param userName User being authenticated 422 | * @param realm realm information 423 | * @param password password of teh user 424 | * @param nonce nonce value 425 | * @param clientNonce Clients Nonce value 426 | * 427 | * @return HA1 portion of the response in a character array 428 | * 429 | * @exception SaslException If an error occurs 430 | */ 431 | char[] DigestCalcHA1( 432 | String algorithm, 433 | String userName, 434 | String realm, 435 | String password, 436 | String nonce, 437 | String clientNonce) throws SaslException 438 | { 439 | byte[] hash; 440 | 441 | try 442 | { 443 | MessageDigest md = MessageDigest.getInstance("MD5"); 444 | 445 | md.update(userName.getBytes("UTF-8")); 446 | md.update(":".getBytes("UTF-8")); 447 | md.update(realm.getBytes("UTF-8")); 448 | md.update(":".getBytes("UTF-8")); 449 | md.update(password.getBytes("UTF-8")); 450 | hash = md.digest(); 451 | 452 | if ("md5-sess".equals(algorithm)) 453 | { 454 | md.update(hash); 455 | md.update(":".getBytes("UTF-8")); 456 | md.update(nonce.getBytes("UTF-8")); 457 | md.update(":".getBytes("UTF-8")); 458 | md.update(clientNonce.getBytes("UTF-8")); 459 | hash = md.digest(); 460 | } 461 | } 462 | catch(NoSuchAlgorithmException e) 463 | { 464 | throw new SaslException("No provider found for MD5 hash", e); 465 | } 466 | catch(UnsupportedEncodingException e) 467 | { 468 | throw new SaslException( 469 | "UTF-8 encoding not supported by platform.", e); 470 | } 471 | 472 | return convertToHex(hash); 473 | } 474 | 475 | 476 | /** 477 | * This function calculates the response-value of the response directive of 478 | * the digest-response as documented in RFC 2831 479 | * 480 | * @param HA1 H(A1) 481 | * @param serverNonce nonce from server 482 | * @param nonceCount 8 hex digits 483 | * @param clientNonce client nonce 484 | * @param qop qop-value: "", "auth", "auth-int" 485 | * @param method method from the request 486 | * @param digestUri requested URL 487 | * @param clientResponseFlag request-digest or response-digest 488 | * 489 | * @return Response-value of the response directive of the digest-response 490 | * 491 | * @exception SaslException If an error occurs 492 | */ 493 | char[] DigestCalcResponse( 494 | char[] HA1, /* H(A1) */ 495 | String serverNonce, /* nonce from server */ 496 | String nonceCount, /* 8 hex digits */ 497 | String clientNonce, /* client nonce */ 498 | String qop, /* qop-value: "", "auth", "auth-int" */ 499 | String method, /* method from the request */ 500 | String digestUri, /* requested URL */ 501 | boolean clientResponseFlag) /* request-digest or response-digest */ 502 | throws SaslException 503 | { 504 | byte[] HA2; 505 | byte[] respHash; 506 | char[] HA2Hex; 507 | 508 | // calculate H(A2) 509 | try 510 | { 511 | MessageDigest md = MessageDigest.getInstance("MD5"); 512 | if (clientResponseFlag) 513 | md.update(method.getBytes("UTF-8")); 514 | md.update(":".getBytes("UTF-8")); 515 | md.update(digestUri.getBytes("UTF-8")); 516 | if ("auth-int".equals(qop)) 517 | { 518 | md.update(":".getBytes("UTF-8")); 519 | md.update("00000000000000000000000000000000".getBytes("UTF-8")); 520 | } 521 | HA2 = md.digest(); 522 | HA2Hex = convertToHex(HA2); 523 | 524 | // calculate response 525 | md.update(new String(HA1).getBytes("UTF-8")); 526 | md.update(":".getBytes("UTF-8")); 527 | md.update(serverNonce.getBytes("UTF-8")); 528 | md.update(":".getBytes("UTF-8")); 529 | if (qop.length() > 0) 530 | { 531 | md.update(nonceCount.getBytes("UTF-8")); 532 | md.update(":".getBytes("UTF-8")); 533 | md.update(clientNonce.getBytes("UTF-8")); 534 | md.update(":".getBytes("UTF-8")); 535 | md.update(qop.getBytes("UTF-8")); 536 | md.update(":".getBytes("UTF-8")); 537 | } 538 | md.update(new String(HA2Hex).getBytes("UTF-8")); 539 | respHash = md.digest(); 540 | } 541 | catch(NoSuchAlgorithmException e) 542 | { 543 | throw new SaslException("No provider found for MD5 hash", e); 544 | } 545 | catch(UnsupportedEncodingException e) 546 | { 547 | throw new SaslException( 548 | "UTF-8 encoding not supported by platform.", e); 549 | } 550 | 551 | return convertToHex(respHash); 552 | } 553 | 554 | 555 | /** 556 | * Creates the intial response to be sent to the server. 557 | * 558 | * @param challenge Challenge in bytes recived form the Server 559 | * 560 | * @return Initial response to be sent to the server 561 | */ 562 | private String createDigestResponse( 563 | byte[] challenge) 564 | throws SaslException 565 | { 566 | char[] response; 567 | StringBuffer digestResponse = new StringBuffer(512); 568 | int realmSize; 569 | 570 | m_dc = new DigestChallenge(challenge); 571 | 572 | m_digestURI = m_protocol + "/" + m_serverName; 573 | 574 | if ((m_dc.getQop() & DigestChallenge.QOP_AUTH) 575 | == DigestChallenge.QOP_AUTH ) 576 | m_qopValue = "auth"; 577 | else 578 | throw new SaslException("Client only supports qop of 'auth'"); 579 | 580 | //get call back information 581 | Callback[] callbacks = new Callback[3]; 582 | ArrayList realms = m_dc.getRealms(); 583 | realmSize = realms.size(); 584 | if (realmSize == 0) 585 | { 586 | callbacks[0] = new RealmCallback("Realm"); 587 | } 588 | else if (realmSize == 1) 589 | { 590 | callbacks[0] = new RealmCallback("Realm", (String)realms.get(0)); 591 | } 592 | else 593 | { 594 | callbacks[0] = 595 | new RealmChoiceCallback( 596 | "Realm", 597 | (String[])realms.toArray(new String[realmSize]), 598 | 0, //the default choice index 599 | false); //no multiple selections 600 | } 601 | 602 | callbacks[1] = new PasswordCallback("Password", false); 603 | //false = no echo 604 | 605 | if (m_authorizationId == null || m_authorizationId.length() == 0) 606 | callbacks[2] = new NameCallback("Name"); 607 | else 608 | callbacks[2] = new NameCallback("Name", m_authorizationId); 609 | 610 | try 611 | { 612 | m_cbh.handle(callbacks); 613 | } 614 | catch(UnsupportedCallbackException e) 615 | { 616 | throw new SaslException("Handler does not support" + 617 | " necessary callbacks",e); 618 | } 619 | catch(IOException e) 620 | { 621 | throw new SaslException("IO exception in CallbackHandler.", e); 622 | } 623 | 624 | if (realmSize > 1) 625 | { 626 | int[] selections = 627 | ((RealmChoiceCallback)callbacks[0]).getSelectedIndexes(); 628 | 629 | if (selections.length > 0) 630 | m_realm = 631 | ((RealmChoiceCallback)callbacks[0]).getChoices()[selections[0]]; 632 | else 633 | m_realm = ((RealmChoiceCallback)callbacks[0]).getChoices()[0]; 634 | } 635 | else 636 | m_realm = ((RealmCallback)callbacks[0]).getText(); 637 | 638 | m_clientNonce = getClientNonce(); 639 | 640 | m_name = ((NameCallback)callbacks[2]).getName(); 641 | if (m_name == null) 642 | m_name = ((NameCallback)callbacks[2]).getDefaultName(); 643 | if (m_name == null) 644 | throw new SaslException("No user name was specified."); 645 | 646 | m_HA1 = DigestCalcHA1( 647 | m_dc.getAlgorithm(), 648 | m_name, 649 | m_realm, 650 | new String(((PasswordCallback)callbacks[1]).getPassword()), 651 | m_dc.getNonce(), 652 | m_clientNonce); 653 | 654 | response = DigestCalcResponse(m_HA1, 655 | m_dc.getNonce(), 656 | "00000001", 657 | m_clientNonce, 658 | m_qopValue, 659 | "AUTHENTICATE", 660 | m_digestURI, 661 | true); 662 | 663 | digestResponse.append("username=\""); 664 | digestResponse.append(m_authorizationId); 665 | if (0 != m_realm.length()) 666 | { 667 | digestResponse.append("\",realm=\""); 668 | digestResponse.append(m_realm); 669 | } 670 | digestResponse.append("\",cnonce=\""); 671 | digestResponse.append(m_clientNonce); 672 | digestResponse.append("\",nc="); 673 | digestResponse.append("00000001"); //nounce count 674 | digestResponse.append(",qop="); 675 | digestResponse.append(m_qopValue); 676 | digestResponse.append(",digest-uri=\"ldap/"); 677 | digestResponse.append(m_serverName); 678 | digestResponse.append("\",response="); 679 | digestResponse.append(response); 680 | digestResponse.append(",charset=utf-8,nonce=\""); 681 | digestResponse.append(m_dc.getNonce()); 682 | digestResponse.append("\""); 683 | 684 | return digestResponse.toString(); 685 | } 686 | 687 | 688 | /** 689 | * This function validates the server response. This step performs a 690 | * modicum of mutual authentication by verifying that the server knows 691 | * the user's password 692 | * 693 | * @param serverResponse Response recived form Server 694 | * 695 | * @return true if the mutual authentication succeeds; 696 | * else return false 697 | * 698 | * @exception SaslException If an error occurs 699 | */ 700 | boolean checkServerResponseAuth( 701 | byte[] serverResponse) throws SaslException 702 | { 703 | char[] response; 704 | ResponseAuth responseAuth = null; 705 | String responseStr; 706 | 707 | responseAuth = new ResponseAuth(serverResponse); 708 | 709 | response = DigestCalcResponse(m_HA1, 710 | m_dc.getNonce(), 711 | "00000001", 712 | m_clientNonce, 713 | m_qopValue, 714 | DIGEST_METHOD, 715 | m_digestURI, 716 | false); 717 | 718 | responseStr = new String(response); 719 | 720 | return responseStr.equals(responseAuth.getResponseValue()); 721 | } 722 | 723 | 724 | /** 725 | * This function returns hex character representing the value of the input 726 | * 727 | * @param value Input value in byte 728 | * 729 | * @return Hex value of the Input byte value 730 | */ 731 | private static char getHexChar( 732 | byte value) 733 | { 734 | switch (value) 735 | { 736 | case 0: 737 | return '0'; 738 | case 1: 739 | return '1'; 740 | case 2: 741 | return '2'; 742 | case 3: 743 | return '3'; 744 | case 4: 745 | return '4'; 746 | case 5: 747 | return '5'; 748 | case 6: 749 | return '6'; 750 | case 7: 751 | return '7'; 752 | case 8: 753 | return '8'; 754 | case 9: 755 | return '9'; 756 | case 10: 757 | return 'a'; 758 | case 11: 759 | return 'b'; 760 | case 12: 761 | return 'c'; 762 | case 13: 763 | return 'd'; 764 | case 14: 765 | return 'e'; 766 | case 15: 767 | return 'f'; 768 | default: 769 | return 'Z'; 770 | } 771 | } 772 | 773 | /** 774 | * Calculates the Nonce value of the Client 775 | * 776 | * @return Nonce value of the client 777 | * 778 | * @exception SaslException If an error Occurs 779 | */ 780 | String getClientNonce() throws SaslException 781 | { 782 | byte[] nonceBytes = new byte[NONCE_BYTE_COUNT]; 783 | SecureRandom prng; 784 | byte nonceByte; 785 | char[] hexNonce = new char[NONCE_HEX_COUNT]; 786 | 787 | try 788 | { 789 | prng = SecureRandom.getInstance("SHA1PRNG"); 790 | prng.nextBytes(nonceBytes); 791 | for(int i=0; i> 4)); 798 | } 799 | return new String(hexNonce); 800 | } 801 | catch(NoSuchAlgorithmException e) 802 | { 803 | throw new SaslException("No random number generator available", e); 804 | } 805 | } 806 | 807 | /** 808 | * Returns the IANA-registered mechanism name of this SASL client. 809 | * (e.g. "CRAM-MD5", "GSSAPI") 810 | * 811 | * @return "DIGEST-MD5"the IANA-registered mechanism name of this SASL 812 | * client. 813 | */ 814 | public String getMechanismName() 815 | { 816 | return "DIGEST-MD5"; 817 | } 818 | 819 | } //end class DigestMD5SaslClient 820 | 821 | --------------------------------------------------------------------------------