diff --git a/java/ext/apache/.classpath b/java/ext/apache/.classpath new file mode 100644 index 0000000..fb50116 --- /dev/null +++ b/java/ext/apache/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/java/ext/apache/.project b/java/ext/apache/.project new file mode 100644 index 0000000..d57161f --- /dev/null +++ b/java/ext/apache/.project @@ -0,0 +1,17 @@ + + + Mailiverse.Ext.Apache + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/java/ext/apache/src/org/apache/james/cli/probe/impl/JmxServerProbe.java b/java/ext/apache/src/org/apache/james/cli/probe/impl/JmxServerProbe.java new file mode 100644 index 0000000..18e7d34 --- /dev/null +++ b/java/ext/apache/src/org/apache/james/cli/probe/impl/JmxServerProbe.java @@ -0,0 +1,226 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ +package org.apache.james.cli.probe.impl; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +//import org.apache.james.cli.probe.ServerProbe; +//import org.apache.james.domainlist.api.DomainListManagementMBean; +//import org.apache.james.rrt.api.RecipientRewriteTableManagementMBean; +import org.apache.james.user.api.UsersRepositoryManagementMBean; + +public class JmxServerProbe { //implements ServerProbe { + + // TODO: Move this to somewhere else + private final static String DOMAINLIST_OBJECT_NAME = "org.apache.james:type=component,name=domainlist"; + private final static String VIRTUALUSERTABLE_OBJECT_NAME = "org.apache.james:type=component,name=recipientrewritetable"; + private final static String USERSREPOSITORY_OBJECT_NAME = "org.apache.james:type=component,name=usersrepository"; + + private MBeanServerConnection mbeanServerConn; +// private DomainListManagementMBean domainListProcxy; +// private RecipientRewriteTableManagementMBean virtualUserTableProxy; + private UsersRepositoryManagementMBean usersRepositoryProxy; + + private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi"; + private static final int defaultPort = 9999; + private String host; + private int port; + + /** + * Creates a ServerProbe using the specified JMX host and port. + * + * @param host + * hostname or IP address of the JMX agent + * @param port + * TCP port of the remote JMX agent + * @throws IOException + * on connection failures + */ + public JmxServerProbe(String host, int port) throws IOException, InterruptedException { + this.host = host; + this.port = port; + connect(); + } + + /** + * Creates a NodeProbe using the specified JMX host and default port. + * + * @param host + * hostname or IP address of the JMX agent + * @throws IOException + * on connection failures + */ + public JmxServerProbe(String host) throws IOException, InterruptedException { + this.host = host; + this.port = defaultPort; + connect(); + } + + /* + * Create a connection to the JMX agent and setup the M[X]Bean proxies. + * + * @throws IOException + * on connection failures + */ + private void connect() throws IOException { + JMXServiceURL jmxUrl = new JMXServiceURL(String.format(fmtUrl, host, port)); + JMXConnector jmxc = JMXConnectorFactory.connect(jmxUrl, null); + mbeanServerConn = jmxc.getMBeanServerConnection(); + + try { + ObjectName name; + +// name = new ObjectName(DOMAINLIST_OBJECT_NAME); +// domainListProcxy = (DomainListManagementMBean) MBeanServerInvocationHandler.newProxyInstance(mbeanServerConn, name, DomainListManagementMBean.class, true); +// name = new ObjectName(VIRTUALUSERTABLE_OBJECT_NAME); +// virtualUserTableProxy = (RecipientRewriteTableManagementMBean) MBeanServerInvocationHandler.newProxyInstance(mbeanServerConn, name, RecipientRewriteTableManagementMBean.class, true); + name = new ObjectName(USERSREPOSITORY_OBJECT_NAME); + usersRepositoryProxy = (UsersRepositoryManagementMBean) MBeanServerInvocationHandler.newProxyInstance(mbeanServerConn, name, UsersRepositoryManagementMBean.class, true); + } catch (MalformedObjectNameException e) { + throw new RuntimeException("Invalid ObjectName? Please report this as a bug.", e); + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#addUser(java.lang.String, java.lang.String) + */ + public void addUser(String userName, String password) throws Exception { + usersRepositoryProxy.addUser(userName, password); + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#removeUser(java.lang.String) + */ + public void removeUser(String username) throws Exception { + usersRepositoryProxy.deleteUser(username); + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#listUsers() + */ + public String[] listUsers() throws Exception { + return usersRepositoryProxy.listAllUsers(); + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#setPassword(java.lang.String, java.lang.String) + */ + public void setPassword(String userName, String password) throws Exception { + usersRepositoryProxy.setPassword(userName, password); + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#addDomain(java.lang.String) + */ +// public void addDomain(String domain) throws Exception { +// domainListProcxy.addDomain(domain); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#removeDomain(java.lang.String) + */ +// public void removeDomain(String domain) throws Exception { +// domainListProcxy.removeDomain(domain); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#listDomains() + */ +// public String[] listDomains() throws Exception { +// return domainListProcxy.getDomains(); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#listMappings() + */ +// public Map> listMappings() throws Exception { +// return virtualUserTableProxy.getAllMappings(); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#addAddressMapping(java.lang.String, java.lang.String, java.lang.String) + */ +// public void addAddressMapping(String user, String domain, String toAddress) throws Exception { +// virtualUserTableProxy.addAddressMapping(user, domain, toAddress); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#removeAddressMapping(java.lang.String, java.lang.String, java.lang.String) + */ +// public void removeAddressMapping(String user, String domain, String fromAddress) throws Exception { +// virtualUserTableProxy.removeAddressMapping(user, domain, fromAddress); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#listUserDomainMappings(java.lang.String, java.lang.String) + */ +// public Collection listUserDomainMappings(String user, String domain) throws Exception { +// return virtualUserTableProxy.getUserDomainMappings(user, domain); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#addRegexMapping(java.lang.String, java.lang.String, java.lang.String) + */ +// public void addRegexMapping(String user, String domain, String regex) throws Exception { +// virtualUserTableProxy.addRegexMapping(user, domain, regex); +// } + + /* + * (non-Javadoc) + * + * @see org.apache.james.cli.probe.ServerProbe#removeRegexMapping(java.lang.String, java.lang.String, java.lang.String) + */ +// public void removeRegexMapping(String user, String domain, String regex) throws Exception { +// virtualUserTableProxy.removeRegexMapping(user, domain, regex); +// } +} diff --git a/java/ext/apache/src/org/apache/james/user/api/UsersRepositoryManagementMBean.java b/java/ext/apache/src/org/apache/james/user/api/UsersRepositoryManagementMBean.java new file mode 100644 index 0000000..ee74984 --- /dev/null +++ b/java/ext/apache/src/org/apache/james/user/api/UsersRepositoryManagementMBean.java @@ -0,0 +1,149 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.user.api; + +/** + * Expose user account management functionality through JMX. + */ +public interface UsersRepositoryManagementMBean { + + /** + * Adds a user to this mail server. + * + * + * @param userName + * The name of the user being added + * @param password + * The password of the user being added + */ + void addUser(String userName, String password) throws Exception; + + /** + * Deletes a user from this mail server. + * + * + * @param userName + * The name of the user being deleted + * @throws UsersRepositoryException + * if error + */ + void deleteUser(String userName) throws Exception; + + /** + * Check if a user exists with the given name. + * + * + * @param userName + * The name of the user + * @return TRUE, if the user exists + * @throws UsersRepositoryException + * if error + */ + boolean verifyExists(String userName) throws Exception; + + /** + * Total count of existing users + * + * + * @return Total count of existing users + */ + long countUsers() throws Exception; + + /** + * List the names of all users + * + * + * @return List of all user names + * @throws UsersRepositoryException + * if error + */ + String[] listAllUsers() throws Exception; + + /** + * Set a user's password + * + * + * @param userName + * The name of the user whose password will be changed + * @param password + * The new password + * @throws UsersRepositoryException + * if error + */ + void setPassword(String userName, String password) throws Exception; + + /** + * Removes a user's alias which terminates local mail forwarding + * + * + * @param userName + * The name of the user whose alias is unset + * @throws UsersRepositoryException + * if error + */ + @Deprecated + void unsetAlias(String userName) throws Exception; + + /** + * Retrieves the user's alias, if set + * + * + * @return User's alias, or NULL, if no alias is set + * @throws UsersRepositoryException + * if error + */ + @Deprecated + String getAlias(String userName) throws Exception; + + /** + * Removes a user's forward email address which terminates remote mail + * forwarding + * + * + * @param userName + * The name of the user whose forward is unset + * @throws UsersRepositoryException + * if error + */ + @Deprecated + void unsetForwardAddress(String userName) throws Exception; + + /** + * Retrieves the user's forward, if set + * + * + * @param userName + * The name of the user whose forward is set + * @return User's forward email address, or NULL, if no forward is set + * @throws UsersRepositoryException + * if error + */ + @Deprecated + String getForwardAddress(String userName) throws Exception; + + /** + * Return true if the UserRepository has VirtualHosting enabled + * + * @return virtual + * @throws UsersRepositoryException + */ + boolean getVirtualHostingEnabled() throws Exception; + +} diff --git a/java/ext/bouncycastle b/java/ext/bouncycastle new file mode 120000 index 0000000..2c2238d --- /dev/null +++ b/java/ext/bouncycastle @@ -0,0 +1 @@ +lcrypto-jdk15on-148 \ No newline at end of file diff --git a/java/ext/jordanzimmerman/.classpath b/java/ext/jordanzimmerman/.classpath new file mode 100644 index 0000000..8b25810 --- /dev/null +++ b/java/ext/jordanzimmerman/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/ext/jordanzimmerman/.project b/java/ext/jordanzimmerman/.project new file mode 100644 index 0000000..26b4b0a --- /dev/null +++ b/java/ext/jordanzimmerman/.project @@ -0,0 +1,17 @@ + + + Mailiverse.Ext.JordanZimmerman + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/Hash256i.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/Hash256i.java new file mode 100644 index 0000000..35e43d3 --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/Hash256i.java @@ -0,0 +1,18 @@ +package com.jordanzimmerman; + +import org.bc.crypto.digests.SHA256Digest; + +public class Hash256i { + + byte[] hash(byte[] bytes) + { + SHA256Digest hash = new SHA256Digest(); + hash.update(bytes, 0, bytes.length); + + byte output[] = new byte[hash.getDigestSize()]; + hash.doFinal(output, 0); + + return output; + } + +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPAuthenticationFailedException.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPAuthenticationFailedException.java new file mode 100755 index 0000000..080e02e --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPAuthenticationFailedException.java @@ -0,0 +1,25 @@ +package com.jordanzimmerman; + +import java.io.IOException; + +/** + * Exception thrown when authentication fails + *

+ * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.1 + */ +public class SRPAuthenticationFailedException extends IOException +{ + public SRPAuthenticationFailedException() + { + super(); + } + + public SRPAuthenticationFailedException(String message) + { + super(message); + } +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPClientSession.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPClientSession.java new file mode 100755 index 0000000..531c7cb --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPClientSession.java @@ -0,0 +1,179 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; + +/** + * Manages a client SRP session + *

+ * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.4 Make sure safeguards are checked: abort if B == 0 (mod N) or u == 0 - 2/27/07 + * @version 1.3 Updated to use the SRP-6 spec - 2/21/07 + * @version 1.2 + */ +public class SRPClientSession +{ + protected SRPClientSession () + { + + } + + /** + * @param constants constants to use + * @param password password as passed to {@link SRPFactory#makeVerifier(byte[])} + */ + public SRPClientSession(SRPConstants constants, byte[] password) + { + fConstants = constants; + fPassword = password; + } + + /** + * Once the server sends the salt (value s in the docs), call this method to save the value + * + * @param salt salt from the server + */ + public void setSalt_s(BigInteger salt) + { + fPrivateKey_x = SRPUtils.makePrivateKey(fPassword, salt); + fRandom_a = SRPUtils.random(fConstants); + fCommonValue_S = null; + fEvidenceValue_M1 = null; + fSessionKey_K = null; + + // A = g^a + fPublicKey_A = fConstants.primitiveRoot_g.modPow(fRandom_a, fConstants.largePrime_N); + } + + public void setSalt_s(byte[] v) + { setSalt_s(new BigInteger(v)); } + + /** + * Returns the public key (value A in the docs). This should be passed to the server + * + * @return A + */ + public BigInteger getPublicKey_A() + { + return fPublicKey_A; + } + + public byte[] getPublicKey_A_() + { return getPublicKey_A().toByteArray(); } + + + /** + * Call to save the public key (value B in the docs) when received from the server + * + * @param publicKey_B B + * @throws SRPAuthenticationFailedException if B is invalid + */ + public void setServerPublicKey_B(BigInteger publicKey_B) throws SRPAuthenticationFailedException + { + if ( fPublicKey_A == null ) + { + throw new IllegalStateException("setSalt_s() has not been called yet."); + } + + if ( publicKey_B.mod(fConstants.largePrime_N).equals(BigInteger.ZERO) ) + { + throw new SRPAuthenticationFailedException("B%N == 0"); + } + + BigInteger SRP6_u = SRPUtils.calc_u(fPublicKey_A, publicKey_B); + if ( SRP6_u.mod(fConstants.largePrime_N).equals(BigInteger.ZERO) ) + { + throw new SRPAuthenticationFailedException("u%N == 0"); + } + + // S = (B - 3(g^x))^(a + ux) + BigInteger three_g_pow_x = fConstants.srp6Multiplier_k.multiply(fConstants.primitiveRoot_g.modPow(fPrivateKey_x, fConstants.largePrime_N)); + BigInteger B_minus_g_pow_x = publicKey_B.subtract(three_g_pow_x); + BigInteger ux = SRP6_u.multiply(fPrivateKey_x); + fCommonValue_S = B_minus_g_pow_x.modPow(fRandom_a.add(ux), fConstants.largePrime_N).mod(fConstants.largePrime_N); + fEvidenceValue_M1 = SRPUtils.calcM1(fPublicKey_A, publicKey_B, fCommonValue_S); + + // the MD5 output is the same as the AES key length + fSessionKey_K = SRPUtils.hashToBytesMD5(fCommonValue_S); + } + + public void setServerPublicKey_B(byte[] v) throws SRPAuthenticationFailedException + { setServerPublicKey_B(new BigInteger(v)); } + + /** + * After the session key has been computed, use this method to return the evidence value to send to the server (value M[1] in the docs). + * + * @return M(1) + */ + public BigInteger getEvidenceValue_M1() + { + if ( fEvidenceValue_M1 == null ) + { + throw new IllegalStateException("computeCommonValue_S() has not been called yet."); + } + + return fEvidenceValue_M1; + } + + public byte[] getEvidenceValue_M1_() + { return getEvidenceValue_M1().toByteArray(); } + + /** + * When the server sends M(2), call this method to validate the number. + * + * @param evidenceValueFromServer_M2 M(2) from the server. + * @throws SRPAuthenticationFailedException if M(2) is incorrect + */ + public void validateServerEvidenceValue_M2(BigInteger evidenceValueFromServer_M2) throws SRPAuthenticationFailedException + { + if ( fEvidenceValue_M1 == null ) + { + throw new IllegalStateException("computeCommonValue_S() has not been called yet."); + } + + BigInteger M2 = SRPUtils.calcM2(fPublicKey_A, fEvidenceValue_M1, fCommonValue_S); + if ( !evidenceValueFromServer_M2.equals(M2) ) + { + throw new SRPAuthenticationFailedException("M(2) is incorrect"); + } + } + + public void validateServerEvidenceValue_M2(byte[] v) throws SRPAuthenticationFailedException + { validateServerEvidenceValue_M2(new BigInteger(v)); } + + /** + * Returns the session common value which is the pre-hashed version of K + * + * @return common value + */ + public BigInteger getSessionCommonValue() + { + return fCommonValue_S; + } + + /** + * The 16 byte session key suitable for encryption + * + * @return session key - K + */ + public byte[] getSessionKey_K() + { + return fSessionKey_K; + } + + SRPConstants getConstants() + { + return fConstants; + } + + protected SRPConstants fConstants; + protected byte[] fPassword; + protected BigInteger fPrivateKey_x; + protected BigInteger fRandom_a; + protected BigInteger fPublicKey_A; + protected BigInteger fCommonValue_S; + protected byte[] fSessionKey_K; + protected BigInteger fEvidenceValue_M1; +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPConstants.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPConstants.java new file mode 100755 index 0000000..4bbadaf --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPConstants.java @@ -0,0 +1,48 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; +import java.io.Serializable; + +/** + * POJO for holding the prime number and primitve root.
+ * + * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.3 Updated to use the SRP-6a spec - k = H(N, g) 2/27/07 + * @version 1.2 Updated to use the SRP-6 spec 2/21/07 + * @version 1.1 + */ +public class SRPConstants implements Serializable +{ + /** + * NOTE: this constructor validates the values passed via {@link SRPUtils#validateConstants(java.math.BigInteger,java.math.BigInteger)} + * + * @param largePrime a very large prime number + * @param primitiveRoot a primitive root that relates to the prime number. + */ + public SRPConstants(BigInteger largePrime, BigInteger primitiveRoot) + { + SRPUtils.validateConstants(largePrime, primitiveRoot); + + this.largePrime_N = largePrime; + this.primitiveRoot_g = primitiveRoot; + this.srp6Multiplier_k = SRPUtils.hash(SRPUtils.combine(this.largePrime_N, this.primitiveRoot_g)); + } + + /** + * N + */ + public final BigInteger largePrime_N; + + /** + * g + */ + public final BigInteger primitiveRoot_g; + + /** + * k from SRP-6 + */ + public final BigInteger srp6Multiplier_k; +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPFactory.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPFactory.java new file mode 100755 index 0000000..22c115e --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPFactory.java @@ -0,0 +1,164 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; + +/** + * An implementation of SRP-6a - Secure Remote Password Protocol. See http://srp.stanford.edu + * and http://srp.stanford.edu/ndss.html. The improvements described in + * SRP-6: Improvements and Refinements to the Secure Remote Password Protocol have been incorporated. + *

+ * + * SRP attempts to eliminate many of the security problems involved in a client/server user authentication. + * I don't understand the math, but the ideas are farily simple. On the server, store a mathematically generated number that is + * based on a user chosen password and a randomly generated "salt". Both the client and server maintain a predetermined prime + * number "N" and a "primitive root" based on N called "g". The nature of all these numbers allows an authentication without + * the server needing to save the password. The client asks for the salt that was created, then a series of calculations + * are performed with the client and server exchanging the calculated values. At the end of this, both the client and server + * can safely know that authentication has occurred. + *

+ * + * From the SRP website, SRP assures: + *

    + *
  1. No useful information about the password P or its associated private key x is revealed during a successful run. Specifically, we wish to prevent an attacker from being able to guess and verify passwords based on exchanged messages.
  2. + *
  3. No useful information about the session key K is revealed to an eavesdropper during a successful run. Since K is a cryptographically strong key instead of a limited-entropy password, we are not concerned about guessing attacks on K, as long as K cannot be computed directly by an intruder.
  4. + *
  5. Even if an intruder has the ability to alter or create his own messages and make them appear to originate from Carol or Steve, the protocol should prevent the intruder from gaining access to the host or learning any information about passwords or session keys. At worst, an intruder should only be able to cause authentication to fail between the two parties (often termed a denial-of-service attack).
  6. + *
  7. If the host's password file is captured and the intruder learns the value of v, it should still not allow the intruder to impersonate the user without an expensive dictionary search.
  8. + *
  9. If the session key of any past session is compromised, it should not help the intruder guess at or otherwise deduce the user's password.
  10. + *
  11. If the user's password itself is compromised, it should not allow the intruder to determine the session key K for past sessions and decrypt them. Even present sessions should at least be protected from passive eavesdropping.
  12. + *
+ *

+ * + * Using this library:
+ * For general use, you should only need to directly use these three classes: {@link SRPFactory}, {@link SRPInputStream} + * and {@link SRPOutputStream}. Besides these three, you will use two POJOs: {@link SRPConstants} and {@link SRPVerifier} + *

+ * For all interactions, you obtain an {@link SRPFactory} via one of the static getInstance() methods. The no-args version uses + * default values for the prime number and primitive root. The other version allows you to specify values for these. + *

+ * The first activity is to generate a "verifier" for a password. Given a password P, this is accomplished via the {@link SRPFactory#makeVerifier(byte[])} + * method. E.g. +

+        SRPFactory.getInstance().makeVerifier(P);
+
+ * This value should be stored away referenced via a username. + *

+ * + * The second activity is a client/server session. On the server, allocate a {@link SRPServerSessionRunner} loaded with + * a session from {@link SRPFactory#newServerSession(SRPVerifier)}. On the client, allocate a {@link SRPClientSessionRunner} + * loaded with a session from {@link SRPFactory#newClientSession(byte[])}. Once you have a Session Runner, you can pass it to an + * {@link SRPInputStream} and an {@link SRPOutputStream}. For each of these streams, call both {@link SRPInputStream#authenticate(SRPRunner, SRPOutputStream)} + * and {@link SRPOutputStream#authenticate(SRPRunner, SRPInputStream)}. Once authenticated, use them as you would any I/O stream. All I/O + * on these streams are encrypted using AES with the SRP session key as the encryption key. + *

+ * + * Stream Protocol
+ * The SRPInputStream/SRPOutStream authenticate using, essentially, the protocol as specified here: http://srp.stanford.edu/design.html. + * All values are sent as {@link java.math.BigInteger#toString(int)} with a radix of 16. The only difference is that the method to combine values is + * unique to this library (see {@link SRPUtils#combine(java.math.BigInteger, java.math.BigInteger)}). If at any point authentication + * fails, the stream is closed. + *

+ * Once authentication is complete, the streams use the following protocol to send data:
+

+	[data size][newline]
+	[data]
+
+ *
+ * "data size" is the number of bytes in the data block. The size is specified as radix 16 BigInteger. The data block is + * encrypted via AES using K as the key. K is an MD5 hash of S. A new data block is sent each time flush() is called on the + * output stream. + *

+ * + * IMPORTANT: This library relies on JCE + *

+ * + * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @version 1.4 a) Updated to use the SRP-6a spec. b) Updated Javadoc. 2/27/07 + * @version 1.3 Updated to use the SRP-6 spec 2/21/07 + * @version 1.2 + */ +public class SRPFactory +{ + /** + * Utility for generating a verifier (using default constants) + * + * @param args args[0] is the Password + */ + public static void main(String[] args) + { + SRPVerifier verifier = SRPFactory.getInstance().makeVerifier(args[0].getBytes()); + System.out.println("v: " + verifier.verifier_v.toString(16)); + System.out.println("s: " + verifier.salt_s.toString(16)); + } + + /** + * Return a factory that uses default constants + * + * @return the factory + */ + public static SRPFactory getInstance() + { + return new SRPFactory(DEFAULT_CONSTANTS); + } + + /** + * Return a factory that uses the given constants + * + * @param constants prime number constants + * @return the factory + */ + public static SRPFactory getInstance(SRPConstants constants) + { + return new SRPFactory(constants); + } + + /** + * Create a new "verifier" (v in the SRP docs). A random salt value is created. + * + * @param password bytes of the password. Client will need this same password later on for a client session. + * + * @return the verifier + */ + public SRPVerifier makeVerifier(byte[] password) + { + return SRPUtils.makeVerifier(fConstants, password); + } + + /** + * Start a new client session. + * + * @param password The same password that was passed to {@link #makeVerifier(byte[])} + * + * @return the client session. Normally, this is passed directly to a new {@link SRPClientSessionRunner} + */ + public SRPClientSession newClientSession(byte[] password) + { + return new SRPClientSession(fConstants, password); + } + + /** + * Start a new server session. + * + * @param verifier The same verifier that was returned by {@link #makeVerifier(byte[])} + * + * @return the server session. Normally, this is passed directly to a new {@link SRPServerSessionRunner} + */ + public SRPServerSession newServerSession(SRPVerifier verifier) + { + return new SRPServerSession(fConstants, verifier); + } + + private SRPFactory(SRPConstants constants) + { + fConstants = constants; + } + + public static final SRPConstants DEFAULT_CONSTANTS = new SRPConstants + ( + new BigInteger("115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16), + new BigInteger("2") + ); + + private SRPConstants fConstants; +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPServerSession.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPServerSession.java new file mode 100755 index 0000000..25d82f7 --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPServerSession.java @@ -0,0 +1,166 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; + +/** + * Manages a server SRP session + * + *

+ * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.4 Make sure safeguards are checked: abort if A == 0 (mod N) or u == 0 - 2/27/07 + * @version 1.3 Updated to use the SRP-6 spec 2/21/07 + * @version 1.2 + */ +public class SRPServerSession +{ + /** + * @param constants constants to use + * @param verifier the verifier as returned from {@link SRPFactory#makeVerifier(byte[])} + */ + public SRPServerSession(SRPConstants constants, SRPVerifier verifier) + { + fConstants = constants; + fVerifier = verifier; + fRandom_b = SRPUtils.random(fConstants); + fSRP6_u = null; + fPublicKey_A = null; + fCommonValue_S = null; + fEvidenceValue_M1 = null; + fSessionKey_K = null; + + // B = 3v + g^b + fPublicKey_B = fVerifier.verifier_v.multiply(constants.srp6Multiplier_k).add(fConstants.primitiveRoot_g.modPow(fRandom_b, fConstants.largePrime_N)); + } + + /** + * When the client sends the public key (value A in the docs) call this method to store the value + * + * @param publicKey_A A + * @throws SRPAuthenticationFailedException if A is invalid + */ + public void setClientPublicKey_A(BigInteger publicKey_A) throws SRPAuthenticationFailedException + { + if ( publicKey_A.mod(fConstants.largePrime_N).equals(BigInteger.ZERO) ) + { + throw new SRPAuthenticationFailedException("A%N == 0"); + } + + fPublicKey_A = publicKey_A; + fSRP6_u = SRPUtils.calc_u(fPublicKey_A, fPublicKey_B); + if ( fSRP6_u.mod(fConstants.largePrime_N).equals(BigInteger.ZERO) ) + { + throw new SRPAuthenticationFailedException("u%N == 0"); + } + } + + public void setClientPublicKey_A(byte[] publicKey) throws SRPAuthenticationFailedException + { setClientPublicKey_A(new BigInteger(publicKey)); } + + /** + * Returns the public key that should be sent to the client (value B in the docs). + * + * @return B + */ + public BigInteger getPublicKey_B() + { + return fPublicKey_B; + } + + /** + * Call to calculate the common session key (S/K in the docs) + */ + public void computeCommonValue_S() + { + if ( fPublicKey_A == null ) + { + throw new IllegalStateException("setClientPublicKey_A() has not been called yet."); + } + + // S = (A · v^u)^b + fCommonValue_S = fPublicKey_A.multiply(fVerifier.verifier_v.modPow(fSRP6_u, fConstants.largePrime_N)).modPow(fRandom_b, fConstants.largePrime_N); + fEvidenceValue_M1 = SRPUtils.calcM1(fPublicKey_A, fPublicKey_B, fCommonValue_S); + + // the MD5 output is the same as the AES key length + fSessionKey_K = SRPUtils.hashToBytesMD5(fCommonValue_S); + } + + /** + * When M(1) is received from the client, call this method to validate it + * + * @param evidenceValueFromClient_M1 M(1) as recevied from the client + * @throws SRPAuthenticationFailedException if M(1) is incorrect + */ + public void validateClientEvidenceValue_M1(BigInteger evidenceValueFromClient_M1) throws SRPAuthenticationFailedException + { + if ( fEvidenceValue_M1 == null ) + { + throw new IllegalStateException("computeCommonValue_S() has not been called yet."); + } + + if ( !fEvidenceValue_M1.equals(evidenceValueFromClient_M1) ) + { + throw new SRPAuthenticationFailedException("M(1) incorrect"); + } + } + + public void validateClientEvidenceValue_M1(byte[] evidenceValueFromClient_M1) throws SRPAuthenticationFailedException + { validateClientEvidenceValue_M1(new BigInteger(evidenceValueFromClient_M1)); } + + /** + * Return the value M(2) that should be sent to the client + * + * @return M(2) + */ + public BigInteger getEvidenceValue_M2() + { + if ( fEvidenceValue_M1 == null ) + { + throw new IllegalStateException("computeCommonValue_S() has not been called yet."); + } + + return SRPUtils.calcM2(fPublicKey_A, fEvidenceValue_M1, fCommonValue_S); + } + + /** + * Returns the session common value which is the pre-hashed version of K + * + * @return common value + */ + public BigInteger getSessionCommonValue() + { + return fCommonValue_S; + } + + /** + * The 16 byte session key suitable for encryption + * + * @return session key - K + */ + public byte[] getSessionKey_K() + { + return fSessionKey_K; + } + + SRPConstants getConstants() + { + return fConstants; + } + + public SRPVerifier getVerifier() + { + return fVerifier; + } + + private SRPConstants fConstants; + private SRPVerifier fVerifier; + private BigInteger fRandom_b; + private BigInteger fSRP6_u; + private BigInteger fPublicKey_A; + private BigInteger fPublicKey_B; + private BigInteger fCommonValue_S; + private byte[] fSessionKey_K; + private BigInteger fEvidenceValue_M1; +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPUtils.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPUtils.java new file mode 100755 index 0000000..6744f01 --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPUtils.java @@ -0,0 +1,286 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.nio.ByteBuffer; + +/** + * Various utilities
+ * + * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.2 Updated to use the SRP-6 spec 2/21/07 + * @version 1.1 + */ +class SRPUtils +{ + /** + * Validates the given constants. Throws {@link IllegalArgumentException} if the values are not valid. + * NOTE: due to prime number calculations, this method can be slow. + * + * @param N large prime + * @param g primitve root of N + */ + static void validateConstants(BigInteger N, BigInteger g) + { + // Developed from "SRP JavaScript Demo" from http://srp.stanford.edu

+ + if ( !N.isProbablePrime(10) ) + { + throw new IllegalArgumentException("isProbablePrime(10) failed for " + N.toString(16)); + } + + BigInteger n_minus_one_div_2 = N.subtract(BigInteger.ONE).divide(TWO); + + if ( !n_minus_one_div_2.isProbablePrime(10) ) + { + throw new IllegalArgumentException("(N-1)/2 is not prime for " + N.toString(16)); + } + + if( g.modPow(n_minus_one_div_2, N).add(BigInteger.ONE).compareTo(N) != 0) + { + throw new IllegalArgumentException("Not a primitive root: " + g.toString(16)); + } + } + + /** + * Make a verifier. First, x is generated via x = H(s, P) where H is a hash() function, s is random salt, and P is the password. + * The verifier is then v = g^x + * + * @param constants the constants to use + * @param password the password to process + * @return the verifier + */ + static SRPVerifier makeVerifier(SRPConstants constants, byte[] password) + { + BigInteger salt = random(constants); + BigInteger x = makePrivateKey(password, salt); + BigInteger v = constants.primitiveRoot_g.modPow(x, constants.largePrime_N); + + return new SRPVerifier(v, salt); + } + + /** + * Make a new private key via x = H(s, P) where H is a hash() function, s is random salt, and P is the password. + * + * @param password the password + * @param salt random salt + * @return the private key + */ + static BigInteger makePrivateKey(byte[] password, BigInteger salt) + { + BigInteger passwordInt = new BigInteger(password); + return hash(combine(passwordInt, salt)); + } + + /** + * Combine two integers into one. This method uses a novel combining method rather than simple concatenation. My assumption is + * that it will add an additional level of security as a malicious party would not be able to guess this method. The bytes from + * each value are interleaved in pairs. If the first value of the pair is odd, two bytes are taken from the second value. Any + * remaining bytes are appended at the end. + * + * @param a first value to combine + * @param b second value to combine + * @return combined value + */ + /* + static BigInteger combine(BigInteger a, BigInteger b) + { + ByteBuffer abuf = ByteBuffer.wrap(a.toByteArray()); + ByteBuffer bbuf = ByteBuffer.wrap(b.toByteArray()); + byte[] combined = new byte[abuf.capacity() + bbuf.capacity()]; + ByteBuffer combinedbuf = ByteBuffer.wrap(combined); + + abuf.rewind(); + bbuf.rewind(); + combinedbuf.clear(); + + while ( abuf.hasRemaining() && bbuf.hasRemaining() ) + { + byte abyte = abuf.get(); + combinedbuf.put(abyte); + byte bbyte = bbuf.get(); + combinedbuf.put(bbyte); + if ( ((abyte & 1) == 0) && bbuf.hasRemaining() ) + { + bbyte = bbuf.get(); + combinedbuf.put(bbyte); + } + } + + while ( abuf.hasRemaining() ) + { + byte x = abuf.get(); + combinedbuf.put(x); + } + + while ( bbuf.hasRemaining() ) + { + byte x = bbuf.get(); + combinedbuf.put(x); + } + + return new BigInteger(combined); + } + */ + static BigInteger combine(BigInteger a, BigInteger b) + { + byte[] abuf = a.toByteArray(); + byte[] bbuf = b.toByteArray(); + byte[] cbuf = new byte[abuf.length + bbuf.length]; + + int ai = 0; + int bi = 0; + int ci = 0; + int al = abuf.length; + int bl = bbuf.length; + + while ( ai < al && bi < bl ) + { + byte abyte = abuf[ai++]; + cbuf[ci++] = abyte; + byte bbyte = bbuf[bi++]; + cbuf[ci++] = bbyte; + + if ( ((abyte & 1) == 0) && bi < bl ) + { + cbuf[ci++] = bbuf[bi++]; + } + } + + while ( ai < al ) + { + cbuf[ci++] = abuf[ai++]; + } + + while ( bi < bl ) + { + cbuf[ci++] = bbuf[bi++]; + } + + return new BigInteger(cbuf); + } + + + /** + * hash a big int. Use SHA 256. + * + * @param i int to hash + * @return the hash + */ + static BigInteger hash(BigInteger i) + { + return new BigInteger(hashToBytes(i)); + } + + /** + * hash a big int. Use SHA 256. + * + * @param i int to hash + * @return the hash + */ + static byte[] hashToBytes(BigInteger i) + { + Hash256i d = new Hash256i(); + byte[] b = i.toByteArray(); + return d.hash(b); + } + + /** + * hash a big int. Use MD5. + * + * @param i int to hash + * @return the hash + */ + static byte[] hashToBytesMD5(BigInteger i) + { + /* + try + { + MessageDigest sha = MessageDigest.getInstance("MD5"); + byte[] b = i.toByteArray(); + sha.update(b, 0, b.length); + return sha.digest(); + } + catch ( NoSuchAlgorithmException e ) + { + throw new UnsupportedOperationException(e); + } + */ + return hashToBytesForAES(i); + } + + static byte[] hashToBytesForAES(BigInteger i) + { + return hashToBytes(i); + } + + + /** + * Return a random number that satsifies: 1 < r < n + * + * @param constants constants to use + * @return the random number + */ + static BigInteger random(SRPConstants constants) + { + int numberOfBytes = (constants.largePrime_N.bitLength() + (constants.largePrime_N.bitLength() - 1)) / 8; + byte[] b = new byte[numberOfBytes]; + fRandom.nextBytes(b); + BigInteger i = new BigInteger(b); + + // random numbers must be: 1 < r < n + BigInteger max = constants.largePrime_N.subtract(TWO); + return i.mod(max).add(TWO); + } + + /** + * Calculate M(1) - H(A, B, K) + * + * @param publicKey_A generated public key - A + * @param publicKey_B generated public key - B + * @param commonValue_S the session common value - S + * @return M(1) + */ + static BigInteger calcM1(BigInteger publicKey_A, BigInteger publicKey_B, BigInteger commonValue_S) + { + return hash(combine(combine(publicKey_A, publicKey_B), commonValue_S)); + } + + /** + * Calculate M(1) - H(A, M[1], K) + * + * @param publicKey_A generated public key - A + * @param evidenceValue_M1 generated hash - M(1) + * @param commonValue_S the session common value - S + * @return M(1) + */ + static BigInteger calcM2(BigInteger publicKey_A, BigInteger evidenceValue_M1, BigInteger commonValue_S) + { + return hash(combine(combine(publicKey_A, evidenceValue_M1), commonValue_S)); + } + + /** + * Return the SRP-6 version of u - H(A, B) + * + * @param A Public Key A + * @param B Public Key B + * @return u + */ + static BigInteger calc_u(BigInteger A, BigInteger B) + { + return hash(combine(A, B)); + } + + private SRPUtils() + { + } + + private static final BigInteger TWO = BigInteger.valueOf(2); + + private static final SecureRandom fRandom = new SecureRandom(); +} diff --git a/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPVerifier.java b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPVerifier.java new file mode 100755 index 0000000..05a7abe --- /dev/null +++ b/java/ext/jordanzimmerman/src/com/jordanzimmerman/SRPVerifier.java @@ -0,0 +1,32 @@ +package com.jordanzimmerman; + +import java.math.BigInteger; +import java.io.Serializable; + +/** + * POJO for holding the random salt and verifier
+ * + * Released into the public domain + * + * @author Jordan Zimmerman - jordan@jordanzimmerman.com + * @see SRPFactory Full Documentation + * @version 1.1 + */ +public class SRPVerifier implements Serializable +{ + public SRPVerifier(BigInteger verifier, BigInteger salt) + { + this.verifier_v = verifier; + this.salt_s = salt; + } + + /** + * v + */ + public final BigInteger verifier_v; + + /** + * s + */ + public final BigInteger salt_s; +} diff --git a/java/ext/json/.classpath b/java/ext/json/.classpath new file mode 100644 index 0000000..e2b1764 --- /dev/null +++ b/java/ext/json/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/java/ext/json/.project b/java/ext/json/.project new file mode 100644 index 0000000..7588e16 --- /dev/null +++ b/java/ext/json/.project @@ -0,0 +1,30 @@ + + + Mailiverse.Ext.Json + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/java/ext/json/.settings/org.eclipse.jdt.core.prefs b/java/ext/json/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..8000cd6 --- /dev/null +++ b/java/ext/json/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/java/ext/json/.settings/org.eclipse.wst.common.component b/java/ext/json/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..b9bb031 --- /dev/null +++ b/java/ext/json/.settings/org.eclipse.wst.common.component @@ -0,0 +1,6 @@ + + + + + + diff --git a/java/ext/json/.settings/org.eclipse.wst.common.project.facet.core.xml b/java/ext/json/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..d6c31ad --- /dev/null +++ b/java/ext/json/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/java/ext/json/bin/org/json/README b/java/ext/json/bin/org/json/README new file mode 100755 index 0000000..b77c71a --- /dev/null +++ b/java/ext/json/bin/org/json/README @@ -0,0 +1,68 @@ +JSON in Java [package org.json] + +Douglas Crockford +douglas@crockford.com + +2011-02-02 + + +JSON is a light-weight, language independent, data interchange format. +See http://www.JSON.org/ + +The files in this package implement JSON encoders/decoders in Java. +It also includes the capability to convert between JSON and XML, HTTP +headers, Cookies, and CDL. + +This is a reference implementation. There is a large number of JSON packages +in Java. Perhaps someday the Java community will standardize on one. Until +then, choose carefully. + +The license includes this restriction: "The software shall be used for good, +not evil." If your conscience cannot live with that, then choose a different +package. + +The package compiles on Java 1.2 thru Java 1.4. + + +JSONObject.java: The JSONObject can parse text from a String or a JSONTokener +to produce a map-like object. The object provides methods for manipulating its +contents, and for producing a JSON compliant object serialization. + +JSONArray.java: The JSONObject can parse text from a String or a JSONTokener +to produce a vector-like object. The object provides methods for manipulating +its contents, and for producing a JSON compliant array serialization. + +JSONTokener.java: The JSONTokener breaks a text into a sequence of individual +tokens. It can be constructed from a String, Reader, or InputStream. + +JSONException.java: The JSONException is the standard exception type thrown +by this package. + + +JSONString.java: The JSONString interface requires a toJSONString method, +allowing an object to provide its own serialization. + +JSONStringer.java: The JSONStringer provides a convenient facility for +building JSON strings. + +JSONWriter.java: The JSONWriter provides a convenient facility for building +JSON text through a writer. + + +CDL.java: CDL provides support for converting between JSON and comma +delimited lists. + +Cookie.java: Cookie provides support for converting between JSON and cookies. + +CookieList.java: CookieList provides support for converting between JSON and +cookie lists. + +HTTP.java: HTTP provides support for converting between JSON and HTTP headers. + +HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers. + +XML.java: XML provides support for converting between JSON and XML. + +JSONML.java: JSONML provides support for converting between JSONML and XML. + +XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text. diff --git a/java/ext/json/src/org/json/JSONArray.java b/java/ext/json/src/org/json/JSONArray.java new file mode 100755 index 0000000..ebedf83 --- /dev/null +++ b/java/ext/json/src/org/json/JSONArray.java @@ -0,0 +1,907 @@ +package org.json; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

+ * + * @author JSON.org + * @version 2012-04-20 + */ +public class JSONArray { + + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * @param x A JSONTokener + * @throws JSONException If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() != ']') { + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + + /** + * Construct a JSONArray from a source JSON text. + * @param source A string that begins with + * [ (left bracket) + * and ends with ] (right bracket). + * @throws JSONException If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + + /** + * Construct a JSONArray from a Collection. + * @param collection A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = new ArrayList(); + if (collection != null) { + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + this.myArrayList.add(JSONObject.wrap(iter.next())); + } + } + } + + + /** + * Construct a JSONArray from an array + * @throws JSONException If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + throw new JSONException("not supported"); +/* + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + throw new JSONException( +"JSONArray initial value should be a string or collection or array."); + } + */ + } + + /** + * Get the object value associated with an index. + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + + /** + * Get the boolean value associated with an index. + * The string values "true" and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException If there is no value for the index or if the + * value is not convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) || + (object instanceof String && + ((String)object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) || + (object instanceof String && + ((String)object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a boolean."); + } + + + /** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public double getDouble(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number + ? ((Number)object).doubleValue() + : Double.parseDouble((String)object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + + "] is not a number."); + } + } + + + /** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number + ? ((Number)object).intValue() + : Integer.parseInt((String)object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + + "] is not a number."); + } + } + + + /** + * Get the JSONArray associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException If there is no value for the index. or if the + * value is not a JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray)object; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject associated with an index. + * @param index subscript + * @return A JSONObject value. + * @throws JSONException If there is no value for the index or if the + * value is not a JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject)object; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public long getLong(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number + ? ((Number)object).longValue() + : Long.parseLong((String)object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + + "] is not a number."); + } + } + + + /** + * Get the string associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String)object; + } + throw new JSONException("JSONArray[" + index + "] not a string."); + } + + + /** + * Determine if the value is null. + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. + * Warning: This method assumes that the data structure is acyclical. + * @param separator A string that will be inserted between the elements. + * @return a string. + * @throws JSONException If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + + /** + * Get the optional object value associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no + * object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) + ? null + : this.myArrayList.get(index); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns false if there is no value at that index, + * or if the value is not Boolean.TRUE or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if + * it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional double value associated with an index. + * NaN is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + + /** + * Get the optional double value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return this.getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional int value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + + /** + * Get the optional int value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return this.getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional JSONArray associated with an index. + * @param index subscript + * @return A JSONArray value, or null if the index has no value, + * or if the value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = this.opt(index); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get the optional JSONObject associated with an index. + * Null is returned if the key is not found, or null if the index has + * no value, or if the value is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = this.opt(index); + return o instanceof JSONObject ? (JSONObject)o : null; + } + + + /** + * Get the optional long value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + + /** + * Get the optional long value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return this.getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value + * is not a string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + + /** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) + ? defaultValue : object + .toString(); + } + + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + this.put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param value A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + this.put(new JSONArray(value)); + return this; + } + + + /** + * Append a double value. This increases the array's length by one. + * + * @param value A double value. + * @throws JSONException if the value is not finite. + * @return this. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + this.put(d); + return this; + } + + + /** + * Append an int value. This increases the array's length by one. + * + * @param value An int value. + * @return this. + */ + public JSONArray put(int value) { + this.put(new Integer(value)); + return this; + } + + + /** + * Append an long value. This increases the array's length by one. + * + * @param value A long value. + * @return this. + */ + public JSONArray put(long value) { + this.put(new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject which is produced from a Map. + * @param value A Map value. + * @return this. + */ + public JSONArray put(Map value) { + this.put(new JSONObject(value)); + return this; + } + + + /** + * Append an object value. This increases the array's length by one. + * @param value An object value. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value A boolean value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param index The subscript. + * @param value A Collection value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + this.put(index, new JSONArray(value)); + return this; + } + + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A double value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + this.put(index, new Double(value)); + return this; + } + + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value An int value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + this.put(index, new Integer(value)); + return this; + } + + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A long value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + this.put(index, new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject that is produced from a Map. + * @param index The subscript. + * @param value The Map value. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value)); + return this; + } + + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + this.myArrayList.set(index, value); + } else { + while (index != this.length()) { + this.put(JSONObject.NULL); + } + this.put(value); + } + return this; + } + + + /** + * Remove an index and close the hole. + * @param index The index of the element to be removed. + * @return The value that was associated with the index, + * or null if there was no value. + */ + public Object remove(int index) { + Object o = this.opt(index); + this.myArrayList.remove(index); + return o; + } + + + /** + * Produce a JSONObject by combining a JSONArray of names with the values + * of this JSONArray. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || this.length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + + /** + * Make a JSON text of this JSONArray. For compactness, no + * unnecessary whitespace is added. If it is not possible to produce a + * syntactically correct JSON text then null will be returned instead. This + * could occur if the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable + * representation of the array. + */ + public String toString() { + try { + return '[' + this.join(",") + ']'; + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONArray. + * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with [ (left bracket) and ending + * with ] (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0).toString(); + } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indention of the top level. + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean commanate = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + JSONObject.writeValue(writer, this.myArrayList.get(0), + indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newindent); + JSONObject.writeValue(writer, this.myArrayList.get(i), + indentFactor, newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } +} diff --git a/java/ext/json/src/org/json/JSONException.java b/java/ext/json/src/org/json/JSONException.java new file mode 100755 index 0000000..289b602 --- /dev/null +++ b/java/ext/json/src/org/json/JSONException.java @@ -0,0 +1,28 @@ +package org.json; + +/** + * The JSONException is thrown by the JSON.org classes when things are amiss. + * @author JSON.org + * @version 2010-12-24 + */ +public class JSONException extends Exception { + private static final long serialVersionUID = 0; + private Throwable cause; + + /** + * Constructs a JSONException with an explanatory message. + * @param message Detail about the reason for the exception. + */ + public JSONException(String message) { + super(message); + } + + public JSONException(Throwable cause) { + super(cause.getMessage()); + this.cause = cause; + } + + public Throwable getCause() { + return this.cause; + } +} diff --git a/java/ext/json/src/org/json/JSONObject.java b/java/ext/json/src/org/json/JSONObject.java new file mode 100755 index 0000000..637a807 --- /dev/null +++ b/java/ext/json/src/org/json/JSONObject.java @@ -0,0 +1,1606 @@ +package org.json; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +/* +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +*/ +import java.util.Collection; +//import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +//import java.util.Locale; +import java.util.Map; +//import java.util.ResourceBundle; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its external + * form is a string wrapped in curly braces with colons between the names and + * values, and commas between the values and names. The internal form is an + * object having get and opt methods for accessing the + * values by name, and put methods for adding or replacing values + * by name. The values can be any of these types: Boolean, + * JSONArray, JSONObject, Number, + * String, or the JSONObject.NULL object. A JSONObject + * constructor can be used to convert an external form JSON text into an + * internal form whose values can be retrieved with the get and + * opt methods, or to convert values into a JSON text using the + * put and toString methods. A get method + * returns a value if one can be found, and throws an exception if one cannot be + * found. An opt method returns a default value instead of throwing + * an exception, and so is useful for obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. The opt methods differ from the get methods in that they do + * not throw. Instead, they return a specified value, such as null. + *

+ * The put methods add or replace values in an object. For example, + * + *

+ * myString = new JSONObject().put("JSON", "Hello, World!").toString();
+ * 
+ * + * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON syntax rules. The constructors are more forgiving in the texts they + * will accept: + *

+ * + * @author JSON.org + * @version 2012-07-02 + */ +public class JSONObject { + + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * @return NULL. + */ + protected final Object clone() { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * @param object An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object + * or null. + */ + public boolean equals(Object object) { + return object == null || object == this; + } + + /** + * Get the "null" string value. + * @return The string "null". + */ + public String toString() { + return "null"; + } + } + + + /** + * The map where the JSONObject's properties are kept. + */ + private final Map map; + + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + this.map = new HashMap(); + } + + + /** + * Construct a JSONObject from a subset of another JSONObject. + * An array of strings is used to identify the keys that should be copied. + * Missing keys are ignored. + * @param jo A JSONObject. + * @param names An array of strings. + * @throws JSONException + * @exception JSONException If a value is a non-finite number or if a name is duplicated. + */ + public JSONObject(JSONObject jo, String[] names) { + this(); + for (int i = 0; i < names.length; i += 1) { + try { + this.putOnce(names[i], jo.opt(names[i])); + } catch (Exception ignore) { + } + } + } + + + /** + * Construct a JSONObject from a JSONTokener. + * @param x A JSONTokener object containing the source string. + * @throws JSONException If there is a syntax error in the source string + * or a duplicated key. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); + } + +// The key is followed by ':'. We will also tolerate '=' or '=>'. + + c = x.nextClean(); + if (c == '=') { + if (x.next() != '>') { + x.back(); + } + } else if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + this.putOnce(key, x.nextValue()); + +// Pairs are separated by ','. We will also tolerate ';'. + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + + /** + * Construct a JSONObject from a Map. + * + * @param map A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException + */ + public JSONObject(Map map) { + this.map = new HashMap(); + if (map != null) { + Iterator i = map.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e = (Map.Entry)i.next(); + Object value = e.getValue(); + if (value != null) { + this.map.put(e.getKey(), wrap(value)); + } + } + } + } + + + /** + * Construct a JSONObject from an Object using bean getters. + * It reflects on all of the public methods of the object. + * For each of the methods with no parameters and a name starting + * with "get" or "is" followed by an uppercase letter, + * the method is invoked, and a key and the value returned from the getter method + * are put into the new JSONObject. + * + * The key is formed by removing the "get" or "is" prefix. + * If the second remaining character is not upper case, then the first + * character is converted to lower case. + * + * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is "Larry Fine", + * then the JSONObject will contain "name": "Larry Fine". + * + * @param bean An object that has getter methods that should be used + * to make a JSONObject. + */ + /* + public JSONObject(Object bean) { + this(); + this.populateMap(bean); + } + */ + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings + * from the names array, and the values will be the field values associated + * with those keys in the object. If a key is not found or not visible, + * then it will not be copied into the new JSONObject. + * @param object An object that has fields that should be used to make a + * JSONObject. + * @param names An array of strings, the names of the fields to be obtained + * from the object. + */ + /* + public JSONObject(Object object, String names[]) { + this(); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + String name = names[i]; + try { + this.putOpt(name, c.getField(name).get(object)); + } catch (Exception ignore) { + } + } + } + */ + + + /** + * Construct a JSONObject from a source JSON text string. + * This is the most commonly used JSONObject constructor. + * @param source A string beginning + * with { (left brace) and ending + * with } (right brace). + * @exception JSONException If there is a syntax error in the source + * string or a duplicated key. + */ + public JSONObject(String source) throws JSONException { + this(new JSONTokener(source)); + } + + + /** + * Construct a JSONObject from a ResourceBundle. + * @param baseName The ResourceBundle base name. + * @param locale The Locale to load the ResourceBundle for. + * @throws JSONException If any JSONExceptions are detected. + */ + /* + public JSONObject(String baseName, Locale locale) throws JSONException { + this(); + ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, + Thread.currentThread().getContextClassLoader()); + +// Iterate through the keys in the bundle. + + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (key instanceof String) { + +// Go through the path, ensuring that there is a nested JSONObject for each +// segment except the last. Add the value using the last segment's name into +// the deepest nested JSONObject. + + String[] path = ((String)key).split("\\."); + int last = path.length - 1; + JSONObject target = this; + for (int i = 0; i < last; i += 1) { + String segment = path[i]; + JSONObject nextTarget = target.optJSONObject(segment); + if (nextTarget == null) { + nextTarget = new JSONObject(); + target.put(segment, nextTarget); + } + target = nextTarget; + } + target.put(path[last], bundle.getString((String)key)); + } + } + } + +*/ + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a + * JSONArray is stored under the key to hold all of the accumulated values. + * If there is already a JSONArray, then the new value is appended to it. + * In contrast, the put method replaces the previous value. + * + * If only one value is accumulated that is not a JSONArray, then the + * result will be the same as using put. But if multiple values are + * accumulated, then the result will be like append. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the value is an invalid number + * or if the key is null. + */ + public JSONObject accumulate( + String key, + Object value + ) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, value instanceof JSONArray + ? new JSONArray().put(value) + : value); + } else if (object instanceof JSONArray) { + ((JSONArray)object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the key is null or if the current value + * associated with the key is not a JSONArray. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray)object).put(value)); + } else { + throw new JSONException("JSONObject[" + key + + "] is not a JSONArray."); + } + return this; + } + + + /** + * Produce a string from a double. The string "null" will be returned if + * the number is not finite. + * @param d A double. + * @return A String. + */ + public static String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String string = Double.toString(d); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && + string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + + /** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @throws JSONException if the key is not found. + */ + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + quote(key) + + "] not found."); + } + return object; + } + + + /** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (object.equals(Boolean.FALSE) || + (object instanceof String && + ((String)object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) || + (object instanceof String && + ((String)object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a Boolean."); + } + + + /** + * Get the double value associated with a key. + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or + * if the value is not a Number object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number + ? ((Number)object).doubleValue() + : Double.parseDouble((String)object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a number."); + } + } + + + /** + * Get the int value associated with a key. + * + * @param key A key string. + * @return The integer value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to an integer. + */ + public int getInt(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number + ? ((Number)object).intValue() + : Integer.parseInt((String)object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not an int."); + } + } + + + /** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray)object; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject)object; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with a key. + * + * @param key A key string. + * @return The long value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to a long. + */ + public long getLong(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number + ? ((Number)object).longValue() + : Long.parseLong((String)object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a long."); + } + } + + + /** + * Get an array of field names from a JSONObject. + * + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(JSONObject jo) { + int length = jo.length(); + if (length == 0) { + return null; + } + Iterator iterator = jo.keys(); + String[] names = new String[length]; + int i = 0; + while (iterator.hasNext()) { + names[i] = (String)iterator.next(); + i += 1; + } + return names; + } + + + /** + * Get an array of field names from an Object. + * + * @return An array of field names, or null if there are no names. + */ + /* + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; i += 1) { + names[i] = fields[i].getName(); + } + return names; + } + */ + + /** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @throws JSONException if there is no string value for the key. + */ + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String)object; + } + throw new JSONException("JSONObject[" + quote(key) + + "] not a string."); + } + + + /** + * Determine if the JSONObject contains a specific key. + * @param key A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1. If there is such a property, and if + * it is an Integer, Long, Double, or Float, then add one to it. + * @param key A key string. + * @return this. + * @throws JSONException If there is already a property with this name + * that is not an Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, ((Integer)value).intValue() + 1); + } else if (value instanceof Long) { + this.put(key, ((Long)value).longValue() + 1); + } else if (value instanceof Double) { + this.put(key, ((Double)value).doubleValue() + 1); + } else if (value instanceof Float) { + this.put(key, ((Float)value).floatValue() + 1); + } else { + throw new JSONException("Unable to increment [" + quote(key) + "]."); + } + return this; + } + + + /** + * Determine if the value associated with the key is null or if there is + * no value. + * @param key A key string. + * @return true if there is no value associated with the key or if + * the value is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(this.opt(key)); + } + + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.map.keySet().iterator(); + } + + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + JSONArray ja = new JSONArray(); + Iterator keys = this.keys(); + while (keys.hasNext()) { + ja.put(keys.next()); + } + return ja.length() == 0 ? null : ja; + } + + /** + * Produce a string from a Number. + * @param number A Number + * @return A String. + * @throws JSONException If n is a non-finite number. + */ + public static String numberToString(Number number) + throws JSONException { + if (number == null) { + throw new JSONException("Null pointer"); + } + testValidity(number); + +// Shave off trailing zeros and decimal point, if possible. + + String string = number.toString(); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && + string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + + /** + * Get an optional value associated with a key. + * @param key A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + + /** + * Get an optional boolean associated with a key. + * It returns false if there is no such key, or if the value is not + * Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + + /** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + try { + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional double associated with a key, + * or NaN if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + + /** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + try { + return this.getDouble(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional int value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return this.optInt(key, 0); + } + + + /** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + try { + return this.getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional JSONArray associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = this.opt(key); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get an optional JSONObject associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject)object : null; + } + + + /** + * Get an optional long value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return this.optLong(key, 0); + } + + + /** + * Get an optional long value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + try { + return this.getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional string associated with a key. + * It returns an empty string if there is no such key. If the value is not + * a string and is not null, then it is converted to a string. + * + * @param key A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return this.optString(key, ""); + } + + + /** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + + /* + private void populateMap(Object bean) { + Class klass = bean.getClass(); + +// If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass + ? klass.getMethods() + : klass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + if (Modifier.isPublic(method.getModifiers())) { + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + if ("getClass".equals(name) || + "getDeclaringClass".equals(name)) { + key = ""; + } else { + key = name.substring(3); + } + } else if (name.startsWith("is")) { + key = name.substring(2); + } + if (key.length() > 0 && + Character.isUpperCase(key.charAt(0)) && + method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + + key.substring(1); + } + + Object result = method.invoke(bean, (Object[])null); + if (result != null) { + this.map.put(key, wrap(result)); + } + } + } + } catch (Exception ignore) { + } + } + } + */ + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * @param key A key string. + * @param value A Collection value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Collection value) throws JSONException { + this.put(key, new JSONArray(value)); + return this; + } + + + /** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + * @throws JSONException If the key is null or if the number is invalid. + */ + public JSONObject put(String key, double value) throws JSONException { + this.put(key, new Double(value)); + return this; + } + + + /** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + this.put(key, new Integer(value)); + return this; + } + + + /** + * Put a key/long pair in the JSONObject. + * + * @param key A key string. + * @param value A long which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + this.put(key, new Long(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * @param key A key string. + * @param value A Map value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Map value) throws JSONException { + this.put(key, new JSONObject(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject. If the value is null, + * then the key will be removed from the JSONObject if it is present. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is non-finite number + * or if the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + + /** + * Put a key/value pair in the JSONObject, but only if the key and the + * value are both non-null, and only if there is not already a member + * with that name. + * @param key + * @param value + * @return his. + * @throws JSONException if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + this.put(key, value); + } + return this; + } + + + /** + * Put a key/value pair in the JSONObject, but only if the + * key and the value are both non-null. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + this.put(key, value); + } + return this; + } + + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within = '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + hhhh = "000" + Integer.toHexString(c); + w.write("\\u" + hhhh.substring(hhhh.length() - 4)); + } else { + w.write(c); + } + } + } + w.write('"'); + return w; + } + + /** + * Remove a name and its value, if present. + * @param key The name to be removed. + * @return The value that was associated with the name, + * or null if there was no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. + * @param string A String. + * @return A simple JSON value. + */ + public static Object stringToValue(String string) { + Double d; + if (string.equals("")) { + return string; + } + if (string.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (string.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (string.equalsIgnoreCase("null")) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. + * If a number cannot be produced, then the value will just + * be a string. Note that the plus and implied string + * conventions are non-standard. A JSON parser may accept + * non-JSON forms as long as it accepts all correct JSON forms. + */ + + char b = string.charAt(0); + if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { + try { + if (string.indexOf('.') > -1 || + string.indexOf('e') > -1 || string.indexOf('E') > -1) { + d = Double.valueOf(string); + if (!d.isInfinite() && !d.isNaN()) { + return d; + } + } else { + Long myLong = new Long(string); + if (myLong.longValue() == myLong.intValue()) { + return new Integer(myLong.intValue()); + } else { + return myLong; + } + } + } catch (Exception ignore) { + } + } + return string; + } + + + /** + * Throw an exception if the object is a NaN or infinite number. + * @param o The object to test. + * @throws JSONException If o is a non-finite number. + */ + public static void testValidity(Object o) throws JSONException { + if (o != null) { + if (o instanceof Double) { + if (((Double)o).isInfinite() || ((Double)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } else if (o instanceof Float) { + if (((Float)o).isInfinite() || ((Float)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } + } + } + + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * @param names A JSONArray containing a list of key strings. This + * determines the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace + * is added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + */ + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + public String toString(int indentFactor) throws JSONException { + StringWriter w = new StringWriter(); +// synchronized (w.getBuffer()) { + return this.write(w, indentFactor, 0).toString(); +// } + } + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce + * the JSON text. The method is required to produce a strictly + * conforming text. If the object does not contain a toJSONString + * method (which is the most common case), then a text will be + * produced by other means. If the value is an array or Collection, + * then a JSONArray will be made from it and its toJSONString method + * will be called. If the value is a MAP, then a JSONObject will be made + * from it and its toJSONString method will be called. Otherwise, the + * value's toString method will be called, and the result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + Object object; + try { + object = ((JSONString)value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (object instanceof String) { + return (String)object; + } + throw new JSONException("Bad value from toJSONString: " + object); + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean || value instanceof JSONObject || + value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + return new JSONObject((Map)value).toString(); + } + if (value instanceof Collection) { + return new JSONArray((Collection)value).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + return quote(value.toString()); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If + * it is a map, wrap it in a JSONObject. If it is a standard property + * (Double, String, et al) then it is already wrapped. Otherwise, if it + * comes from one of the java packages, turn it into a string. And if + * it doesn't, try to wrap it in a JSONObject. If the wrapping fails, + * then null is returned. + * + * @param object The object to wrap + * @return The wrapped value + */ + public static Object wrap(Object object) { + try { + if (object == null) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray || + NULL.equals(object) || object instanceof JSONString || + object instanceof Byte || object instanceof Character || + object instanceof Short || object instanceof Integer || + object instanceof Long || object instanceof Boolean || + object instanceof Float || object instanceof Double || + object instanceof String || object instanceof Enum) { + return object; + } + + if (object instanceof Collection) { + return new JSONArray((Collection)object); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + return new JSONObject((Map)object); + } + + /* + Package objectPackage = object.getClass().getPackage(); + String objectPackageName = objectPackage != null + ? objectPackage.getName() + : ""; + if ( + objectPackageName.startsWith("java.") || + objectPackageName.startsWith("javax.") || + object.getClass().getClassLoader() == null + ) { + return object.toString(); + } + */ + + return null; +// return new JSONObject(object); + } catch(Exception exception) { + return null; + } + } + + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * For compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + + static final Writer writeValue(Writer writer, Object value, + int indentFactor, int indent) throws JSONException, IOException { + if (value == null || value.equals(null)) { + writer.write("null"); + } else if (value instanceof JSONObject) { + ((JSONObject) value).write(writer, indentFactor, indent); + } else if (value instanceof JSONArray) { + ((JSONArray) value).write(writer, indentFactor, indent); + } else if (value instanceof Map) { + new JSONObject((Map) value).write(writer, indentFactor, indent); + } else if (value instanceof Collection) { + new JSONArray((Collection) value).write(writer, indentFactor, + indent); + } else if (value.getClass().isArray()) { + new JSONArray(value).write(writer, indentFactor, indent); + } else if (value instanceof Number) { + writer.write(numberToString((Number) value)); + } else if (value instanceof Boolean) { + writer.write(value.toString()); + } else if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + writer.write(o != null ? o.toString() : quote(value.toString())); + } else { + quote(value.toString(), writer); + } + return writer; + } + + static final void indent(Writer writer, int indent) throws IOException { + for (int i = 0; i < indent; i += 1) { + writer.write(' '); + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean commanate = false; + final int length = this.length(); + Iterator keys = this.keys(); + writer.write('{'); + + if (length == 1) { + Object key = keys.next(); + writer.write(quote(key.toString())); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + writeValue(writer, this.map.get(key), indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + while (keys.hasNext()) { + Object key = keys.next(); + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, newindent); + writer.write(quote(key.toString())); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + writeValue(writer, this.map.get(key), indentFactor, + newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, indent); + } + writer.write('}'); + return writer; + } catch (IOException exception) { + throw new JSONException(exception); + } + } +} diff --git a/java/ext/json/src/org/json/JSONString.java b/java/ext/json/src/org/json/JSONString.java new file mode 100755 index 0000000..1f2d77d --- /dev/null +++ b/java/ext/json/src/org/json/JSONString.java @@ -0,0 +1,18 @@ +package org.json; +/** + * The JSONString interface allows a toJSONString() + * method so that a class can change the behavior of + * JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The + * toJSONString method will be used instead of the default behavior + * of using the Object's toString() method and quoting the result. + */ +public interface JSONString { + /** + * The toJSONString method allows a class to produce its own JSON + * serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); +} diff --git a/java/ext/json/src/org/json/JSONStringer.java b/java/ext/json/src/org/json/JSONStringer.java new file mode 100755 index 0000000..2ae7d88 --- /dev/null +++ b/java/ext/json/src/org/json/JSONStringer.java @@ -0,0 +1,78 @@ +package org.json; + +import java.io.StringWriter; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * JSONStringer provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONStringer can produce one JSON text. + *

+ * A JSONStringer instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting cascade style. For example,

+ * myString = new JSONStringer()
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject()
+ *     .toString();
which produces the string
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONStringer adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2008-09-18 + */ +public class JSONStringer extends JSONWriter { + /** + * Make a fresh JSONStringer. It can be used to build one JSON text. + */ + public JSONStringer() { + super(new StringWriter()); + } + + /** + * Return the JSON text. This method is used to obtain the product of the + * JSONStringer instance. It will return null if there was a + * problem in the construction of the JSON text (such as the calls to + * array were not properly balanced with calls to + * endArray). + * @return The JSON text. + */ + public String toString() { + return this.mode == 'd' ? this.writer.toString() : null; + } +} diff --git a/java/ext/json/src/org/json/JSONTokener.java b/java/ext/json/src/org/json/JSONTokener.java new file mode 100755 index 0000000..fdd261e --- /dev/null +++ b/java/ext/json/src/org/json/JSONTokener.java @@ -0,0 +1,433 @@ +package org.json; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2012-02-16 + */ +public class JSONTokener { + + private long character; + private boolean eof; + private long index; + private long line; + private char previous; + private Reader reader; + private boolean usePrevious; + + + /** + * Construct a JSONTokener from a Reader. + * + * @param reader A reader. + */ + public JSONTokener(Reader reader) { + this.reader = reader; + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.line = 1; + } + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this(new StringReader(s)); + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + */ + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.index -= 1; + this.character -= 1; + this.usePrevious = true; + this.eof = false; + } + + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + public boolean end() { + return this.eof && !this.usePrevious; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + */ + public boolean more() throws JSONException { + this.next(); + if (this.end()) { + return false; + } + this.back(); + return true; + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + */ + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + + if (c <= 0) { // End of stream + this.eof = true; + c = 0; + } + } + this.index += 1; + if (this.previous == '\r') { + this.line += 1; + this.character = c == '\n' ? 0 : 1; + } else if (c == '\n') { + this.line += 1; + this.character = 0; + } else { + this.character += 1; + } + this.previous = (char) c; + return this.previous; + } + + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = this.next(); + if (n != c) { + throw this.syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + + char[] chars = new char[n]; + int pos = 0; + + while (pos < n) { + chars[pos] = this.next(); + if (this.end()) { + throw this.syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } + + + /** + * Get the next char in the string, skipping whitespace. + * @throws JSONException + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = this.next(); + if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = this.next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); + case '\\': + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char)Integer.parseInt(this.next(4), 16)); + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param delimiter A delimiter character. + * @return A string. + */ + public String nextTo(char delimiter) throws JSONException { + StringBuffer sb = new StringBuffer(); + for (;;) { + char c = this.next(); + if (c == delimiter || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimiter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + */ + public String nextTo(String delimiters) throws JSONException { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = this.next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = this.nextClean(); + String string; + + switch (c) { + case '"': + case '\'': + return this.nextString(c); + case '{': + this.back(); + return new JSONObject(this); + case '[': + this.back(); + return new JSONArray(this); + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuffer sb = new StringBuffer(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = this.next(); + } + this.back(); + + string = sb.toString().trim(); + if ("".equals(string)) { + throw this.syntaxError("Missing value"); + } + return JSONObject.stringToValue(string); + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + */ + public char skipTo(char to) throws JSONException { + char c; + try { + long startIndex = this.index; + long startCharacter = this.character; + long startLine = this.line; + this.reader.mark(1000000); + do { + c = this.next(); + if (c == 0) { + this.reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return c; + } + } while (c != to); + } catch (IOException exc) { + throw new JSONException(exc); + } + + this.back(); + return c; + } + + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + this.toString()); + } + + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + public String toString() { + return " at " + this.index + " [character " + this.character + " line " + + this.line + "]"; + } +} diff --git a/java/ext/json/src/org/json/JSONWriter.java b/java/ext/json/src/org/json/JSONWriter.java new file mode 100755 index 0000000..855b2bd --- /dev/null +++ b/java/ext/json/src/org/json/JSONWriter.java @@ -0,0 +1,327 @@ +package org.json; + +import java.io.IOException; +import java.io.Writer; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * JSONWriter provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONWriter can produce one JSON text. + *

+ * A JSONWriter instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting a cascade style. For example,

+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject();
which writes
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONWriter adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2011-11-24 + */ +public class JSONWriter { + private static final int maxdepth = 200; + + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; + + /** + * The current mode. Values: + * 'a' (array), + * 'd' (done), + * 'i' (initial), + * 'k' (key), + * 'o' (object). + */ + protected char mode; + + /** + * The object/array stack. + */ + private final JSONObject stack[]; + + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; + + /** + * The writer that will receive the output. + */ + protected Writer writer; + + /** + * Make a fresh JSONWriter. It can be used to build one JSON text. + */ + public JSONWriter(Writer w) { + this.comma = false; + this.mode = 'i'; + this.stack = new JSONObject[maxdepth]; + this.top = 0; + this.writer = w; + } + + /** + * Append a value. + * @param string A string value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter append(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.write(','); + } + this.writer.write(string); + } catch (IOException e) { + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = 'k'; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push(null); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + /** + * End something. + * @param mode Mode + * @param c Closing character + * @return this + * @throws JSONException If unbalanced. + */ + private JSONWriter end(char mode, char c) throws JSONException { + if (this.mode != mode) { + throw new JSONException(mode == 'a' + ? "Misplaced endArray." + : "Misplaced endObject."); + } + this.pop(mode); + try { + this.writer.write(c); + } catch (IOException e) { + throw new JSONException(e); + } + this.comma = true; + return this; + } + + /** + * End an array. This method most be called to balance calls to + * array. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + /** + * End an object. This method most be called to balance calls to + * object. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + /** + * Append a key. The key will be associated with the next value. In an + * object, every value must be preceded by a key. + * @param string A key string. + * @return this + * @throws JSONException If the key is out of place. For example, keys + * do not belong in arrays or if the key is null. + */ + public JSONWriter key(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + this.stack[this.top - 1].putOnce(string, Boolean.TRUE); + if (this.comma) { + this.writer.write(','); + } + this.writer.write(JSONObject.quote(string)); + this.writer.write(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) { + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + + } + + + /** + * Pop an array or object scope. + * @param c The scope to close. + * @throws JSONException If nesting is wrong. + */ + private void pop(char c) throws JSONException { + if (this.top <= 0) { + throw new JSONException("Nesting error."); + } + char m = this.stack[this.top - 1] == null ? 'a' : 'k'; + if (m != c) { + throw new JSONException("Nesting error."); + } + this.top -= 1; + this.mode = this.top == 0 + ? 'd' + : this.stack[this.top - 1] == null + ? 'a' + : 'k'; + } + + /** + * Push an array or object scope. + * @param c The scope to open. + * @throws JSONException If nesting is too deep. + */ + private void push(JSONObject jo) throws JSONException { + if (this.top >= maxdepth) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = jo; + this.mode = jo == null ? 'a' : 'k'; + this.top += 1; + } + + + /** + * Append either the value true or the value + * false. + * @param b A boolean. + * @return this + * @throws JSONException + */ + public JSONWriter value(boolean b) throws JSONException { + return this.append(b ? "true" : "false"); + } + + /** + * Append a double value. + * @param d A double. + * @return this + * @throws JSONException If the number is not finite. + */ + public JSONWriter value(double d) throws JSONException { + return this.value(new Double(d)); + } + + /** + * Append a long value. + * @param l A long. + * @return this + * @throws JSONException + */ + public JSONWriter value(long l) throws JSONException { + return this.append(Long.toString(l)); + } + + + /** + * Append an object value. + * @param object The object to append. It can be null, or a Boolean, Number, + * String, JSONObject, or JSONArray, or an object that implements JSONString. + * @return this + * @throws JSONException If the value is out of sequence. + */ + public JSONWriter value(Object object) throws JSONException { + return this.append(JSONObject.valueToString(object)); + } +} diff --git a/java/ext/json/src/org/json/README b/java/ext/json/src/org/json/README new file mode 100755 index 0000000..b77c71a --- /dev/null +++ b/java/ext/json/src/org/json/README @@ -0,0 +1,68 @@ +JSON in Java [package org.json] + +Douglas Crockford +douglas@crockford.com + +2011-02-02 + + +JSON is a light-weight, language independent, data interchange format. +See http://www.JSON.org/ + +The files in this package implement JSON encoders/decoders in Java. +It also includes the capability to convert between JSON and XML, HTTP +headers, Cookies, and CDL. + +This is a reference implementation. There is a large number of JSON packages +in Java. Perhaps someday the Java community will standardize on one. Until +then, choose carefully. + +The license includes this restriction: "The software shall be used for good, +not evil." If your conscience cannot live with that, then choose a different +package. + +The package compiles on Java 1.2 thru Java 1.4. + + +JSONObject.java: The JSONObject can parse text from a String or a JSONTokener +to produce a map-like object. The object provides methods for manipulating its +contents, and for producing a JSON compliant object serialization. + +JSONArray.java: The JSONObject can parse text from a String or a JSONTokener +to produce a vector-like object. The object provides methods for manipulating +its contents, and for producing a JSON compliant array serialization. + +JSONTokener.java: The JSONTokener breaks a text into a sequence of individual +tokens. It can be constructed from a String, Reader, or InputStream. + +JSONException.java: The JSONException is the standard exception type thrown +by this package. + + +JSONString.java: The JSONString interface requires a toJSONString method, +allowing an object to provide its own serialization. + +JSONStringer.java: The JSONStringer provides a convenient facility for +building JSON strings. + +JSONWriter.java: The JSONWriter provides a convenient facility for building +JSON text through a writer. + + +CDL.java: CDL provides support for converting between JSON and comma +delimited lists. + +Cookie.java: Cookie provides support for converting between JSON and cookies. + +CookieList.java: CookieList provides support for converting between JSON and +cookie lists. + +HTTP.java: HTTP provides support for converting between JSON and HTTP headers. + +HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers. + +XML.java: XML provides support for converting between JSON and XML. + +JSONML.java: JSONML provides support for converting between JSONML and XML. + +XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text. diff --git a/java/ext/lcrypto-jdk15on-148/.classpath b/java/ext/lcrypto-jdk15on-148/.classpath new file mode 100644 index 0000000..e2b1764 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/java/ext/lcrypto-jdk15on-148/.project b/java/ext/lcrypto-jdk15on-148/.project new file mode 100644 index 0000000..ad6f343 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/.project @@ -0,0 +1,30 @@ + + + Mailiverse.Ext.BouncyCastle + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.jdt.core.prefs b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..c537b63 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.component b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..c82f9b8 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.component @@ -0,0 +1,6 @@ + + + + + + diff --git a/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.project.facet.core.xml b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..37355cb --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/java/ext/lcrypto-jdk15on-148/CONTRIBUTORS.html b/java/ext/lcrypto-jdk15on-148/CONTRIBUTORS.html new file mode 100644 index 0000000..01541ef --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/CONTRIBUTORS.html @@ -0,0 +1,324 @@ + + +The following organisations and people have contributed to the Bouncy Castle Cryptography Package. +

+Thanks, may your castles never deflate! +

+Organisations +

+People + + + diff --git a/java/ext/lcrypto-jdk15on-148/LICENSE.html b/java/ext/lcrypto-jdk15on-148/LICENSE.html new file mode 100644 index 0000000..c317e74 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/LICENSE.html @@ -0,0 +1,22 @@ + + + +Copyright (c) 2000-2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) +

+Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +

+The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. +

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + diff --git a/java/ext/lcrypto-jdk15on-148/bin/META-INF/MANIFEST.MF b/java/ext/lcrypto-jdk15on-148/bin/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5e94951 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/bin/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/java/ext/lcrypto-jdk15on-148/index.html b/java/ext/lcrypto-jdk15on-148/index.html new file mode 100644 index 0000000..6899828 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/index.html @@ -0,0 +1,121 @@ + + +The Bouncy Castle Crypto Package + + + + +

+

The Bouncy Castle Crypto Package

+ +
+
+
+
+The Bouncy Castle Crypto package is a Java implementation of +cryptographic algorithms, it was developed by the Legion of the +Bouncy Castle - with a little help! The Legion, and the latest +goings on with this package, can be found at +http://www.bouncycastle.org. +

+The Legion also gratefully acknowledges the contributions made to this +package by others (see here +for the current list). +

+The package is organised so that it +contains a light-weight API suitable for use in any environment +(including the newly released J2ME) with the additional infrastructure +to conform the algorithms to the JCE framework. +

+Except where otherwise stated, this software is distributed under a license based on the MIT X Consortium license. To view the license, see here. The OpenPGP library also includes a modified BZIP2 library which is licensed under the Apache Software License, Version 2.0. +

+The current release notes for this package are +here. +

+The current specifications for this package are +here. +

+The current api documentation for this package is +here. +

+

+

Examples and Tests

+

+

To view some examples, look at the test programs in the packages: +

+

There are also some specific example programs for dealing with Attribute Certificates, PKCS12, SMIME and OpenPGP. They can be found in: +

+ +

Finally there are also code examples from Beginning Cryptography with Java which demonstrate both the use of the JCE/JCA and also some of the Bouncy Castle APIs.

+

+Note 1:The JCE classes are only distributed with the JDK 1.1, JDK 1.2, and JDK 1.3 JCE releases. The +JDK 1.0, J2ME, and the JDK 1.1, JDK 1.2, JDK 1.3, JDK 1.4, and JDK 1.5 lightweight releases only include the +Bouncy Castle lightweight cryptography API.
+Note 2:The regression test for Diffie-Hellman is quite slow. +

Building From CVS

+

+The src and test/src directory are for JDK 1.5. +

+

+Compatibility classes for other VMs are as follows: +

+

+

+The clean room JCE, which will compile with everything from JDK 1.1 and up is in the jce/src directory. +

+

+The build scripts that come with the full distribution allow creation of the different releases by using the tree under src and test/src, excluding classes that are not appropriate and copying in the required compatibility classes from the directories containing compatibility classes appropriate for the distribution. +

+

+If you want to try create a build for yourself, using your own environment, the best way to do it is to start with the build for the distribution you are interested in, make sure that builds, and then modify your build scripts to do the required exclusions and file copies for your setup, otherwise you are likely to get class not found exceptions. The final caveat to this is that as the j2me distribution includes some compatibility classes starting in the java package, you need to use an obfuscator to change the package names before attempting to import a midlet using the BC API. +

+

Mailing Lists

+

+For those who are interested, there are 2 mailing lists +for participation in this project. To subscribe use the +links below and include the word subscribe in the message body. (To unsubscribe, replace subscribe with +unsubscribe in the message body) +

+NOTE:You need to be subscribed to send mail to the above +mailing list. +

+If you want to provide feedback, offers of jobs (or more importantly +beer) directly to the members of The Legion +then please use +feedback-crypto@bouncycastle.org +

+Enjoy! + + diff --git a/java/ext/lcrypto-jdk15on-148/releasenotes.html b/java/ext/lcrypto-jdk15on-148/releasenotes.html new file mode 100644 index 0000000..1af7401 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/releasenotes.html @@ -0,0 +1,1434 @@ + + +Bouncy Castle Crypto Package - Release Notes + + + + +

+

Bouncy Castle Crypto Package - Release Notes

+ +
+
+
+
+

1.0 Introduction

+The Bouncy Castle Crypto package is a Java implementation of +cryptographic algorithms. The package is organised so that it +contains a light-weight API suitable for use in any environment +(including the newly released J2ME) with the additional infrastructure +to conform the algorithms to the JCE framework. +

+

2.0 Release History

+ +

2.1.1 Version

+Release 1.48 +

2.1.2 Defects Fixed

+ + +

2.1.3 Additional Features and Functionality

+ + +

2.2.1 Version

+Release 1.47 +

2.2.2 Defects Fixed

+ + +

2.2.3 Additional Features and Functionality

+ + +

2.2.4 Other notes

+

+Okay, so we have had to do another release. The issue we have run into is that we probably didn't go far enough in 1.46, but we are now confident that moving from this release to 2.0 should be largely just getting rid of deprecated methods. While this release does change a lot it is relatively straight forward to do a port and we have a porting guide which explains the important ones. The area there has been the most change in is the ASN.1 library which was in bad need of a rewrite after 10 years of patching. On the bright side the rewrite did allow us to eliminate a few problems and bugs in the ASN.1 library, so we have some hope anyone porting to it will also have similar benefits. As with 1.46 the other point of emphasis has been making sure interface support is available for operations across the major APIs, so the lightweight API or some local role your own methods can be used instead for doing encryption and signing. +

+ +

2.3.1 Version

+Release 1.46 +

2.3.2 Defects Fixed

+ +

2.3.3 Additional Features and Functionality

+ +

2.3.4 Other notes

+

+Baring security patches we expect 1.46 will be the last of the 1.* releases. The next release of +BC will be version 2.0. For this reason a lot of things in 1.46 that relate to CMS have been deprecated and +new methods have been added to the CMS and certificate handling APIs which provide greater flexibility +in how digest and signature algorithms get used. It is now possible to use the lightweight API or a simple +custom API with CMS and for certificate generation. In addition a lot of methods and some classes that were +deprecated for reasons of been confusing, or in some cases just plan wrong, have been removed. +

+

+So there are four things useful to know about this release: +

+

+

2.4.1 Version

+Release 1.45 +

2.4.2 Defects Fixed

+ +

2.4.3 Additional Features and Functionality

+ +

2.4.4 Security Advisory

+ + +

2.5.1 Version

+Release 1.44 +

2.5.2 Defects Fixed

+ +

2.5.3 Additional Features and Functionality

+ + +

2.6.1 Version

+Release 1.43 +

2.6.2 Defects Fixed

+ +

2.6.3 Security Advisory

+ + +

2.7.1 Version

+Release 1.42 +

2.7.2 Defects Fixed

+ +

2.7.3 Additional Features and Functionality

+ + +

2.8.1 Version

+Release 1.41 +

2.8.2 Defects Fixed

+ +

2.8.3 Additional Features and Functionality

+ + +

2.9.1 Version

+Release 1.40 +

2.9.2 Defects Fixed

+ +

2.9.3 Additional Features and Functionality

+ +

2.9.4 Additional Notes

+ + +

2.10.1 Version

+Release 1.39 +

2.10.2 Defects Fixed

+ +

2.10.3 Additional Features and Functionality

+ +

2.11.1 Version

+Release 1.38 +

2.11.2 Defects Fixed

+ +

2.11.3 Additional Features and Functionality

+ +

2.12.1 Version

+Release 1.37 +

2.12.2 Defects Fixed

+ +

2.12.3 Additional Features and Functionality

+ + +

2.13.1 Version

+Release 1.36 +

2.13.2 Defects Fixed

+ +

2.13.3 Additional Features and Functionality

+ + +

2.14.1 Version

+Release 1.35 +

2.14.2 Defects Fixed

+ +

2.14.3 Additional Features and Functionality

+ +

2.15.1 Version

+Release 1.34 +

2.15.2 Defects Fixed

+ +

2.15.3 Additional Features and Functionality

+ +

2.15.5 Security Advisory

+ +

2.16.1 Version

+Release 1.33 +

2.16.2 Defects Fixed

+ +

2.16.3 Additional Features and Functionality

+ + +

2.17.1 Version

+Release 1.32 +

2.17.2 Defects Fixed

+ + +

2.17.3 Additional Features and Functionality

+ +

2.17.4 Possible compatibility issue

+ + +

2.18.1 Version

+Release 1.31 +

2.18.2 Defects Fixed

+ +

2.18.3 Additional Features and Functionality

+ + +

2.19.1 Version

+Release 1.30 +

2.19.2 Defects Fixed

+ +

2.19.3 Additional Features and Functionality

+ + +

2.20.1 Version

+Release 1.29 +

2.20.2 Defects Fixed

+ +

2.20.3 Additional Features and Functionality

+ +

2.20.4 Notes

+ +

2.21.1 Version

+Release 1.28 +

2.21.2 Defects Fixed

+ +

2.21.3 Additional Features and Functionality

+ +

2.21.4 Notes

+ +

2.22.1 Version

+Release 1.27 +

2.22.2 Defects Fixed

+ +

2.22.3 Additional Features and Functionality

+ +

2.22.4 Changes that may affect compatibility

+ +

2.23.1 Version

+Release 1.26 +

2.23.2 Defects Fixed

+ +

2.23.3 Additional Features and Functionality

+ +

2.23.4 JDK 1.5 Changes

+ + +

2.24.1 Version

+Release 1.25 +

2.24.2 Defects Fixed

+ +

2.24.3 Additional Features and Functionality

+ +

2.25.1 Version

+Release 1.24 +

2.25.2 Defects Fixed

+ +

2.25.3 Additional Features and Functionality

+ +

2.26.1 Version

+Release 1.23 +

2.26.2 Defects Fixed

+ +

2.26.3 Additional Features and Functionality

+ + +

2.27.1 Version

+Release 1.22 +

2.27.2 Defects Fixed

+ +

2.27.3 Additional Features and Functionality

+ + +

2.28.1 Version

+Release 1.21 +

2.28.2 Defects Fixed

+ +

2.28.3 Additional Features and Functionality

+ + +

2.29.1 Version

+Release 1.20 +

2.29.2 Defects Fixed

+ +

2.29.3 Additional Features and Functionality

+ + +

2.30.1 Version

+Release 1.19 +

2.30.2 Defects Fixed

+ +

2.30.3 Additional Features and Functionality

+ + +

2.31.1 Version

+Release 1.18 +

2.31.2 Defects Fixed

+ +

2.31.3 Additional Features and Functionality

+ +

2.32.1 Version

+Release 1.17 +

2.32.2 Defects Fixed

+ +

2.32.2 Additional Functionality and Features

+ + +

2.33.1 Version

+Release 1.16 +

2.33.2 Defects Fixed

+ +

2.33.3 Additional Functionality and Features

+ +

2.34.1 Version

+Release 1.15 +

2.34.2 Defects Fixed

+ +

2.34.3 Additional Functionality and Features

+ +

2.35.1 Version

+Release 1.14 +

2.35.2 Defects Fixed

+ +

2.35.3 Additional Functionality and Features

+ +

2.36.1 Version

+Release 1.13 +

2.36.2 Defects Fixed

+ + +

2.36.3 Additional Functionality and Features

+ + +

2.37.1 Version

+Release 1.12 +

2.37.2 Defects Fixed

+ +

2.37.3 Additional Functionality and Features

+ +

2.38.1 Version

+Release 1.11 +

2.38.2 Defects Fixed

+ +

2.38.3 Additional Functionality and Features

+ +

2.38.4 Other changes

+ +

2.39.1 Version

+Release 1.10 +

2.39.2 Defects Fixed

+ +

2.39.3 Additional Functionality and Features

+ +

2.40.1 Version

+Release 1.09 +

2.40.2 Defects Fixed

+ +

2.40.3 Additional Functionality and Features

+ + +

2.41.1 Version

+Release 1.08 + +

2.41.2 Defects Fixed

+ +

2.41.3 Additional Functionality and Features

+ +

2.42.1 Version

+Release 1.07 + +

2.42.2 Defects Fixed

+ +

2.43.1 Version

+Release 1.06 + +

2.43.2 Defects Fixed

+ + +

2.43.3 Additional Functionality

+ + +

2.44.1 Version

+Release 1.05 + +

2.44.2 Defects Fixed

+ +

2.44.3 Additional Functionality

+ +

2.44.4 Additional Notes

+

+Concerning the PKCS12 fix: in a few cases this may cause some backward +compatibility issues - if this happens to you, drop us a line at +feedback-crypto@bouncycastle.org +and we will help you get it sorted out. + +

2.45.1 Version

+Release 1.04 + +

2.45.2 Defects Fixed

+ + +

2.45.3 Additional Functionality

+ + +

2.46.1 Version

+Release 1.03 + +

2.46.2 Defects Fixed

+ + +

2.47.1 Version

+Release 1.02 + +

2.47.2 Defects Fixed

+ + +

2.48.1 Version

+Release 1.01 + +

2.48.2 Defects Fixed

+ + + +

2.49.1 Version

+Release 1.0 + +

2.49.2 Defects Fixed

+

+

+ +

2.49.3 Additional functionality

+

+

+ +

3.0 Notes

+

+The J2ME is only supported under Windows. +

+If you are trying to use the lightweight provider in a JDK 1.0 applet, you +need to change the package names for java.math.BigInteger, java.lang.IllegalStateException, and java.security.SecureRandom +

+The RSA test under JDK 1.0 and J2ME takes a while to run... + + diff --git a/java/ext/lcrypto-jdk15on-148/specifications.html b/java/ext/lcrypto-jdk15on-148/specifications.html new file mode 100644 index 0000000..a26ad50 --- /dev/null +++ b/java/ext/lcrypto-jdk15on-148/specifications.html @@ -0,0 +1,815 @@ + + +Bouncy Castle Crypto Package + + + + +

+

Bouncy Castle Crypto Package

+ +
+
+
+
+

1.0 Introduction

+The Bouncy Castle Crypto package is a Java implementation of +cryptographic algorithms. The package is organised so that it +contains a light-weight API suitable for use in any environment +(including the newly released J2ME) with the additional infrastructure +to conform the algorithms to the JCE framework. +

+Except where otherwise stated, this software is distributed under a license +based on the MIT X +Consortium license. To view the license, see here. +The OpenPGP library also includes a modified BZIP2 library which +is licensed under the Apache Software License, Version 2.0. + +

+If you have the full package you will have six jar files, bcprov*.jar +which contains the BC provider, jce-*.jar which contains +the JCE provider, clean room API, and bcmail*.jar which contains the +mail API. +

+Note: if you are using JDK 1.0, you will just find a class hierarchy in +the classes directory. +

+To view examples, look at the test programs in the packages: +

+

+To verify the packages, run the following Java programs with the +appropriate classpath: +

+ + +

2.0 Patents

+

+Some of the algorithms in the Bouncy Castle APIs are patented in some +places. It is upon the user of the library to be aware of what the +legal situation is in their own situation, however we have been asked +to specifically mention the patent below, in the following terms, at +the request of the patent holder. Algorithms that appear here are only +distributed in the -ext- versions of the provider. +

+The IDEA encryption algorithm is patented in the USA, Japan, and Europe +including at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, +Switzerland and the United Kingdom. Non-commercial use is free, however +any commercial products that make use of IDEA are liable for royalties. +Please see +www.mediacrypt.com for +further details. + +

3.0 Specifications

+ + + +

4.0 Light-weight API

+ +

+This API has been specifically developed for those circumstances +where the rich API and integration requirements of the JCE are +not required. +

+However as a result, the light-weight API requires more effort +and understanding on the part of a developer to initialise and +utilise the algorithms. + +

4.1 Example

+ +

To utilise the light-weight API in a program, the fundamentals +are as follows; + +

+
+	/*
+	 * This will use a supplied key, and encrypt the data
+	 * This is the equivalent of DES/CBC/PKCS5Padding
+	 */
+	BlockCipher engine = new DESEngine();
+	BufferedBlockCipher cipher = new PaddedBlockCipher(new CBCCipher(engine));
+
+	byte[] key = keyString.getBytes();
+	byte[] input = inputString.getBytes();
+
+	cipher.init(true, new KeyParameter(key));
+
+	byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
+	
+	int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
+	try
+	{
+		cipher.doFinal(cipherText, outputLen);
+	}
+	catch (CryptoException ce)
+	{
+		System.err.println(ce);
+		System.exit(1);
+	}
+
+
+ +

4.2 Algorithms

+ +

The light-weight API has built in support for the following: + +

Symmetric (Block)

+ +

+The base interface is BlockCipher and has the following +implementations which match the modes the block cipher can +be operated in. +

+ + + + + + + + + + + +
NameConstructorNotes
BufferedBlockCipherBlockCipher 
CBCBlockCipherBlockCipher 
CFBBlockCipherBlockCipher, block size (in bits) 
CCMBlockCipherBlockCipherPacket mode - requires all data up front.
EAXBlockCipherBlockCipher 
OFBBlockCipherBlockCipher, block size (in bits) 
SICBlockCipherBlockCipher, block size (in bits)Also known as CTR mode
OpenPGPCFBBlockCipherBlockCipher 
GOFBBlockCipherBlockCipherGOST OFB mode
+ +

+BufferedBlockCipher has a further sub-classes +

+ + + + +
NameConstructorNotes
PaddedBufferedBlockCipherBlockCiphera buffered block cipher that can use padding - default PKCS5/7 padding
CTSBlockCipherBlockCipherCipher Text Stealing
+ +

The following paddings can be used with the PaddedBufferedBlockCipher. +

+ + + + + + + +
NameDescription
PKCS7PaddingPKCS7/PKCS5 padding
ISO10126d2PaddingISO 10126-2 padding
X932PaddingX9.23 padding
ISO7816d4PaddingISO 7816-4 padding (ISO 9797-1 scheme 2)
ZeroBytePaddingPad with Zeros (not recommended)
+ +

The following cipher engines are implemented that can be +used with the above modes. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameKeySizes (in bits) Block SizeNotes
AESEngine0 .. 256 128 bit 
AESWrapEngine0 .. 256 128 bitImplements FIPS AES key wrapping
BlowfishEngine0 .. 448 64 bit 
CamelliaEngine128, 192, 256128 bit 
CamelliaWrapEngine128, 192, 256128 bit 
CAST5Engine0 .. 128 64 bit 
CAST6Engine0 .. 256 128 bit 
DESEngine6464 bit 
DESedeEngine128, 19264 bit 
DESedeWrapEngine128, 19264 bitImplements Draft IETF DESede key wrapping
GOST28147Engine25664 bitHas a range of S-boxes
IDEAEngine12864 bit 
NoekeonEngine128128 bit 
RC2Engine0 .. 1024 64 bit 
RC532Engine0 .. 128 64 bitUses a 32 bit word
RC564Engine0 .. 128 128 bitUses a 64 bit word
RC6Engine0 .. 256 128 bit 
RijndaelEngine0 .. 256 128 bit, 160 bit, 192 bit, 224 bit, 256 bit 
SEEDEngine128128 bit 
SEEDWrapEngine128128 bit 
SerpentEngine128, 192, 256 128 bit 
SkipjackEngine0 .. 128 64 bit 
TEAEngine12864 bit 
TwofishEngine128, 192, 256 128 bit 
XTEAEngine12864 bit 
+ +

Symmetric (Stream)

+ +

+The base interface is StreamCipher and has the following +implementations which match the modes the stream cipher can +be operated in. + +

+ + + +
NameConstructorNotes
BlockStreamCipherBlockCipher 
+

The following cipher engines are implemented that can be +used with the above modes. +

+ + + + + + + + + + + +
NameKeySizes (in bits) Notes
RC4Engine40 .. 2048 
HC128Engine128 
HC256Engine256 
Salsa20Engine128/256 
ISAACEngine32 .. 8192 
VMPCEngine8 .. 6144 
Grainv1Engine8064 bit IV
Grain128Engine12896 bit IV
+ +

Block Asymmetric

+ +

+The base interface is AsymmetricBlockCipher and has the following +implementations which match the modes the cipher can be operated in. + +

+ + + + + + +
NameConstructorNotes
BufferedAsymmetricBlockCipherAsymmetricBlockCipher 
OAEPEncodingAsymmetricBlockCipher 
PKCS1EncodingAsymmetricBlockCipher 
ISO9796d1EncodingAsymmetricBlockCipherISO9796-1
+ +

The following cipher engines are implemented that can be +used with the above modes. +

+ + + + +
NameKeySizes (in bits)Notes
RSAEngineany multiple of 8 large enough for the encoding. 
ElGamalEngineany multiple of 8 large enough for the encoding. 
+ +

Digest

+ +

+The base interface is Digest and has the following +implementations +

+ + + + + + + + + + + + + + + + + + +
NameOutput (in bits)Notes
MD2Digest128 
MD4Digest128 
MD5Digest128 
RipeMD128Digest128basic RipeMD
RipeMD160Digest160enhanced version of RipeMD
RipeMD256Digest256expanded version of RipeMD128
RipeMD320Digest320expanded version of RipeMD160
SHA1Digest160 
SHA224Digest224FIPS 180-2
SHA256Digest256FIPS 180-2
SHA384Digest384FIPS 180-2
SHA512Digest512FIPS 180-2
SHA3Digest224, 256, 288, 384, 512
TigerDigest192The Tiger Digest.
GOST3411Digest256The GOST-3411 Digest.
WhirlpoolDigest512The Whirlpool Digest.
+ +

MAC

+ +

+The base interface is Mac and has the following +implementations +

+ + + + + +
NameOutput (in bits)Notes
CBCBlockCipherMacblocksize/2 unless specified 
CFBBlockCipherMacblocksize/2, in CFB 8 mode, unless specified 
HMacdigest length 
+ +

PBE

+ +

+The base class is PBEParametersGenerator and has the following +sub-classes +

+ + + + + + +
NameConstructorNotes
PKCS5S1ParametersGeneratorDigest 
PKCS5S2ParametersGenerator Uses SHA1/Hmac as defined
PKCS12ParametersGeneratorDigest 
OpenSSLPBEParametersGenerator Uses MD5 as defined
+ +

Key Agreement

+

+Two versions of Diffie-Hellman key agreement are supported, the basic +version, and one for use with long term public keys. Two versions of +key agreement using Elliptic Curve cryptography are also supported, +standard Diffie-Hellman key agreement and standard key agreement with +co-factors. +

+The agreement APIs are in the org.bouncycastle.crypto.agreement package. +Classes for generating Diffie-Hellman parameters can be found in the +org.bouncycastle.crypto.params and org.bouncycastle.crypto.generators packages. +

+ +

IESCipher

+

+The IES cipher is based on the one described in IEEE P1363a (draft 10), for +use with either traditional Diffie-Hellman or Elliptic Curve Diffie-Hellman. +

+Note: At the moment this is still a draft, don't use it for anything +that may be subject to long term storage, the key values produced may well +change as the draft is finalised. +

+

Signers

+

+DSA, ECDSA, ISO-9796-2, GOST-3410-94, GOST-3410-2001, DSTU-4145-2002, and RSA-PSS are supported by the org.bouncycastle.crypto.signers +package. Note: as these are light weight classes, if you need to use SHA1 or GOST-3411 +(as defined in the relevant standards) you'll also need to make use of the appropriate +digest class in conjunction with these. +Classes for generating DSA and ECDSA parameters can be found in the +org.bouncycastle.crypto.params and org.bouncycastle.crypto.generators packages. +

+ +

4.3 ASN.1 package

+ +

The light-weight API has direct interfaces into a package capable of +reading and writing DER-encoded ASN.1 objects and for the generation +of X.509 V3 certificate objects and PKCS12 files. BER InputStream and +OutputStream classes are provided as well. + +

5.0 Bouncy Castle Provider

+ +

The Bouncy Castle provider is a JCE compliant provider that +is a wrapper built on top of the light-weight API. + +

+The advantage for writing application code that uses the +provider interface to cryptographic algorithms is that the +actual provider used can be selected at run time. This +is extremely valuable for applications that may wish to +make use of a provider that has underlying hardware for +cryptographic computation, or where an application may have +been developed in an environment with cryptographic export +controls. + +

5.1 Example

+ +

To utilise the JCE provider in a program, the fundamentals +are as follows; + +

+
+	/*
+	 * This will generate a random key, and encrypt the data
+	 */
+	Key		key;
+	KeyGenerator	keyGen;
+	Cipher		encrypt;
+
+	Security.addProvider(new BouncyCastleProvider());
+
+	try
+	{
+		// "BC" is the name of the BouncyCastle provider
+		keyGen = KeyGenerator.getInstance("DES", "BC");
+		keyGen.init(new SecureRandom());
+
+		key = keyGen.generateKey();
+
+		encrypt = Cipher.getInstance("DES/CBC/PKCS5Padding", "BC");
+	}
+	catch (Exception e)
+	{
+		System.err.println(e);
+		System.exit(1);
+	}
+
+	encrypt.init(Cipher.ENCRYPT_MODE, key);
+
+	bOut = new ByteArrayOutputStream();
+	cOut = new CipherOutputStream(bOut, encrypt);
+
+	cOut.write("plaintext".getBytes());
+	cOut.close();
+
+	// bOut now contains the cipher text
+
+
+

+The provider can also be configured as part of your environment via static registration +by adding an entry to the java.security properties file (found in $JAVA_HOME/jre/lib/security/java.security, where $JAVA_HOME is the location of your JDK/JRE distribution). You'll find detailed +instructions in the file but basically it comes down to adding a line: +

+
+    security.provider.<n>=org.bouncycastle.jce.provider.BouncyCastleProvider
+
+
+

Where <n> is the preference you want the provider at (1 being the most prefered). +

Where you put the jar is up to mostly up to you, although with jdk1.3 and +jdk1.4 the best (and in some cases only) place to have it is in $JAVA_HOME/jre/lib/ext. Note: under Windows there will normally be a JRE and a JDK install of Java if you think you have installed it correctly and it still doesn't work chances are you have added the provider to the installation not being used. +

+Note: with JDK 1.4 and later you will need to have installed the unrestricted policy +files to take full advantage of the provider. If you do not install the policy files you are likely +to get something like the following: + +

+        java.lang.SecurityException: Unsupported keysize or algorithm parameters
+                at javax.crypto.Cipher.init(DashoA6275)
+
+ +The policy files can be found at the same place you downloaded the JDK. +

+

5.2 Algorithms

+ +

Symmetric (Block)

+ +

Modes: +

+ +

+Where (n) is a multiple of 8 that gives the blocksize in bits, +eg, OFB8. Note that OFB and CFB mode can be used with plain text that +is not an exact multiple of the block size if NoPadding has been specified. +

+ +Padding Schemes: +

+ +

+When placed together this gives a specification for an algorithm +as; +

+ +

+Note: default key sizes are in bold. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameKeySizes (in bits) Block SizeNotes
AES0 .. 256 (192)128 bit 
AESWrap0 .. 256 (192)128 bitA FIPS AES key wrapper
Blowfish0 .. 448 (448)64 bit 
Camellia128, 192, 256128 bit 
CamelliaWrap128, 192, 256128 bit 
CAST50 .. 128(128)64 bit 
CAST60 .. 256(256)128 bit 
DES6464 bit 
DESede128, 19264 bit 
DESedeWrap128, 192128 bitA Draft IETF DESede key wrapper
GOST2814725664 bit 
IDEA128 (128)64 bitOnly included in extended provider jar.
Noekeon128(128)128 bit 
RC20 .. 1024 (128)64 bit 
RC50 .. 128 (128)64 bitUses a 32 bit word
RC5-640 .. 256 (256)128 bitUses a 64 bit word
RC60 .. 256 (128)128 bit 
Rijndael0 .. 256 (192)128 bit 
SEED128(128)128 bit 
SEEDWrap128(128)128 bit 
Serpent128, 192, 256 (256)128 bit 
Skipjack0 .. 128 (128)64 bit 
TEA128 (128)64 bit 
Twofish128, 192, 256 (256)128 bit 
XTEA128 (128)64 bit 
+ +

Symmetric (Stream)

+ +

+Note: default key sizes are in bold. +

+ + + + + + + + + +
NameKeySizes (in bits)Notes
RC440 .. 2048 bits (128) 
HC128(128) 
HC256(256) 
Salsa20128/256(128) 
VMPC128/6144(128) 
Grainv18064 bit IV
Grain12812896 bit IV
+ +

Block Asymmetric

+ +

Encoding: +

+

Note: except as indicated in PKCS 1v2 we recommend you use OAEP, as +mandated in X9.44. + +

+When placed together with RSA this gives a specification for an algorithm +as; +

+ + + + + +
NameKeySizes (in bits)Notes
RSAany multiple of 8 bits large enough for the encryption(2048) 
ElGamalany multiple of 8 bits large enough for the encryption(1024) 
+ +

Key Agreement

+ +

+Diffie-Hellman key agreement is supported using the "DH", "ECDH", and +"ECDHC" (ECDH with cofactors) key agreement instances. +

+Note: with basic "DH" only the basic algorithm fits in with the JCE API, if +you're using long-term public keys you may want to look at the light-weight +API. +

+

ECIES

+

+An implementation of ECIES (stream mode) as described in IEEE P 1363a. +

+Note: At the moment this is still a draft, don't use it for anything +that may be subject to long term storage, the key values produced may well +change as the draft is finalised. +

+

Digest

+

+ + + + + + + + + + + + + + + + + + + + + +
NameOutput (in bits)Notes
GOST3411256 
MD2128 
MD4128 
MD5128 
RipeMD128128basic RipeMD
RipeMD160160enhanced version of RipeMD
RipeMD256Digest256expanded version of RipeMD128
RipeMD320Digest320expanded version of RipeMD160
SHA1160 
SHA-224224FIPS 180-2
SHA-256256FIPS 180-2
SHA-384384FIPS 180-2
SHA-512512FIPS 180-2
SHA3-224224 
SHA3-256256 
SHA3-384384 
SHA3-512512 
Tiger192 
Whirlpool512 
+ +

MAC

+ + + + + + + + + + + + + + + + +
NameOutput (in bits)Notes
Any MAC based on a block cipher, CBC (the default) and CFB modes.half the cipher's block size (usually 32 bits) 
VMPC-MAC128 
HMac-MD2128 
HMac-MD4128 
HMac-MD5128 
HMac-RipeMD128128 
HMac-RipeMD160160 
HMac-SHA1160 
HMac-SHA224224 
HMac-SHA256256 
HMac-SHA384384 
HMac-SHA512512 
HMac-Tiger192 
+ +

Examples: +

+ + +

Signature Algorithms

+ +

Schemes: +

+ +

PBE

+ +

Schemes: +

+ +

+Defined in Bouncy Castle JCE Provider + + + + + + + + + + + + + + + + +
NameKey Generation SchemeKey Length (in bits)
PBEWithMD2AndDESPKCS5 Scheme 164
PBEWithMD2AndRC2PKCS5 Scheme 1128
PBEWithMD5AndDESPKCS5 Scheme 164
PBEWithMD5AndRC2PKCS5 Scheme 1128
PBEWithSHA1AndDESPKCS5 Scheme 164
PBEWithSHA1AndRC2PKCS5 Scheme 1128
PBEWithSHAAnd2-KeyTripleDES-CBCPKCS12128
PBEWithSHAAnd3-KeyTripleDES-CBCPKCS12192
PBEWithSHAAnd128BitRC2-CBCPKCS12128
PBEWithSHAAnd40BitRC2-CBCPKCS1240
PBEWithSHAAnd128BitRC4PKCS12128
PBEWithSHAAnd40BitRC4PKCS1240
PBEWithSHAAndTwofish-CBCPKCS12256
PBEWithSHAAndIDEA-CBCPKCS12128
+ +

5.3 Certificates

+

+The Bouncy Castle provider will read X.509 certficates (v2 or v3) as per the examples in +the java.security.cert.CertificateFactory class. They can be provided either +in the normal PEM encoded format, or as DER binaries. +

+The CertificiateFactory will also read X.509 CRLs (v2) from either PEM or DER encodings. +

+In addition to the classes in the org.bouncycastle.asn1.x509 package for certificate +generation a more JCE "friendly" class is provided in the package org.bouncycastle.x509. The JCE "friendly" class supports RSA, DSA, and EC-DSA. +

+

5.4 Keystore

+

+The Bouncy Castle package has three implementation of a keystore. +

+The first "BKS" is a keystore that will work with the keytool in the same +fashion as the Sun "JKS" keystore. The keystore is resistent to tampering +but not inspection. +

+The second, Keystore.BouncyCastle, or Keystore.UBER will only work with the keytool +if the password is provided on the command line, as the entire keystore +is encrypted +with a PBE based on SHA1 and Twofish. PBEWithSHAAndTwofish-CBC. +This makes the entire keystore resistant to tampering and inspection, +and forces verification. +The Sun JDK provided keytool will attempt to load a keystore even if no +password is given, +this is impossible for this version. (One might wonder about going to all +this trouble and then having the password on the command line! New keytool +anyone?). +

+In the first case, the keys are encrypted with 3-Key-TripleDES. +

+The third is a PKCS12 compatible keystore. PKCS12 provides a slightly +different situation from the regular key store, the keystore password is +currently the only password used for storing keys. Otherwise it supports +all the functionality required for it to be used with the keytool. In some +situations other libraries always expect to be dealing with Sun certificates, +if this is the case use PKCS12-DEF, and the certificates produced by the +key store will be made using the default provider. In the default case PKCS12 uses 3DES for key protection and 40 bit RC2 for protecting the certificates. It is also possible to use 3DES for both by using PKCS12-3DES-3DES or PKCS12-DEF-3DES-3DES as the KeyStore type. +

+There is an example program that produces PKCS12 files suitable for +loading into browsers. It is in the package +org.bouncycastle.jce.examples. +

+

+

5.5 Additional support classes for Elliptic Curve.

+

+There are no classes for supporting EC in the JDK prior to JDK 1.5. If you are using +an earlier JDK you can find classes for using EC in the following +packages: +

+ +

6.0 BouncyCastle S/MIME

+ +To be able to fully compile and utilise the BouncyCastle S/MIME +package (including the test classes) you need the jar files for +the following APIs. + + +

6.1 Setting up BouncyCastle S/MIME in JavaMail

+ +The BouncyCastle S/MIME handlers may be set in JavaMail two ways. + +