实现远程自动配置

This commit is contained in:
yushijinhun 2017-08-21 22:30:11 +08:00
parent e47b187304
commit 3185f73f6b
No known key found for this signature in database
GPG key ID: 5BC167F73EA558E4
9 changed files with 3160 additions and 17 deletions

View file

@ -1,5 +1,7 @@
# authlib-injector
通过运行时修改authlib实现游戏外登录
通过运行时修改authlib实现游戏外登录并为Yggdrasil服务端的实现提供规范
关于该项目的详细介绍见[wiki](https://github.com/to2mbn/authlib-injector/wiki)。
## 编译
```
@ -9,7 +11,6 @@ gradle clean shadowJar
或者直接从[Jenkins](https://ci.to2mbn.org/job/authlib-injector)下载构建好的JAR。
## 部署
### 配置
@ -18,15 +19,7 @@ gradle clean shadowJar
#### 生成签名公钥
服务端返回的profile properties需要带有数字签名。
可以通过以下方法生成一个新的RSA私钥
```
openssl genrsa -out key.pem 4096
```
然后从私钥产生公钥:
```
openssl rsa -in key.pem -pubout
```
生成方法见[签名密钥对](https://github.com/to2mbn/authlib-injector/wiki/%E7%AD%BE%E5%90%8D%E5%AF%86%E9%92%A5%E5%AF%B9)。
### 加载
#### 作为javaagent加载
@ -52,6 +45,8 @@ authlib-injector提供了以下方式来指定配置文件按优先级排序
* 可以在编译时向`src/main/resources`中添加配置文件或者直接向JAR中添加JAR为zip格式
4. 当前目录下的`authlib-injector.yaml`文件
## 本项目与authlib-agent的关系
authlib-agent项目存在较多历史遗留问题并且原项目的javaagent部分及后端部分耦合在一起需要一起构建。因此将原项目的javaagent部分重写并更名authlib-injector同时提供更加友好的配置方式以供其它yggdrasil服务端实现使用。
### 远程自动配置
对于实现了本规范中[扩展API](https://github.com/to2mbn/authlib-injector/wiki/Yggdrasil%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#%E6%89%A9%E5%B1%95api)的Yggdrasil服务端可以直接通过添加以下JVM参数来配置不需要配置文件
```
-javaagent:{authlib-injector.jar的路径}=@{Yggdrasil服务端的URLAPI Root}
```

View file

@ -1,5 +1,7 @@
package org.to2mbn.authlibinjector;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@ -7,18 +9,25 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.lang.instrument.ClassFileTransformer;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.Optional;
import java.util.function.Consumer;
import org.to2mbn.authlibinjector.internal.org.json.JSONException;
import org.to2mbn.authlibinjector.internal.org.json.JSONObject;
import org.to2mbn.authlibinjector.internal.org.json.JSONTokener;
import org.to2mbn.authlibinjector.transform.ClassTransformer;
import org.yaml.snakeyaml.Yaml;
public final class AuthlibInjector {
private static final String[] nonTransformablePackages = new String[] { "java.", "javax.", "com.sun.", "com.oracle.", "jdk.", "sun.", "org.apache.", "com.google.", "oracle.", "com.oracle.", "com.paulscode.", "io.netty.", "org.lwjgl.", "net.java.", "org.w3c.", "javassist." };
private static final String[] nonTransformablePackages = new String[] { "java.", "javax.", "com.sun.",
"com.oracle.", "jdk.", "sun.", "org.apache.", "com.google.", "oracle.", "com.oracle.", "com.paulscode.",
"io.netty.", "org.lwjgl.", "net.java.", "org.w3c.", "javassist." };
private AuthlibInjector() {}
@ -40,6 +49,9 @@ public final class AuthlibInjector {
}
private static InjectorConfig loadConfig() {
Optional<InjectorConfig> remoteConfig = tryRemoteConfig();
if (remoteConfig.isPresent()) return remoteConfig.get();
try (Reader reader = new InputStreamReader(lookupConfig(), StandardCharsets.UTF_8)) {
Yaml yaml = new Yaml();
return yaml.loadAs(reader, InjectorConfig.class);
@ -50,7 +62,7 @@ public final class AuthlibInjector {
private static InputStream lookupConfig() throws IOException {
String configProperty = System.getProperty("org.to2mbn.authlibinjector.config");
if (configProperty != null) {
if (configProperty != null && !configProperty.startsWith("@")) {
Path configFile = Paths.get(configProperty);
if (!Files.exists(configFile)) {
log("file not exists: {0}", configProperty);
@ -75,4 +87,36 @@ public final class AuthlibInjector {
}
}
private static Optional<InjectorConfig> tryRemoteConfig() {
String configProperty = System.getProperty("org.to2mbn.authlibinjector.config");
if (!configProperty.startsWith("@")) {
return empty();
}
String url = configProperty.substring(1);
log("trying to config remotely: {0}", url);
JSONObject remoteConfig;
try {
remoteConfig = jsonGet(url);
} catch (IOException e) {
log("unable to configure remotely: {0}", e);
return empty();
}
InjectorConfig config = new InjectorConfig();
config.setApiRoot(url.endsWith("/") ? url : url + "/");
config.setDebug("true".equals(System.getProperty("org.to2mbn.authlibinjector.remoteconfig.debug")));
config.readFromJson(remoteConfig);
if (config.isDebug()) {
log("fetched remote config: {0}", remoteConfig);
}
return of(config);
}
private static JSONObject jsonGet(String url) throws IOException {
try (Reader reader = new InputStreamReader(new URL(url).openStream(), StandardCharsets.UTF_8)) {
return new JSONObject(new JSONTokener(reader));
} catch (JSONException e) {
throw new IOException("Unresolvable JSON", e);
}
}
}

View file

@ -1,7 +1,10 @@
package org.to2mbn.authlibinjector;
import static java.util.Optional.ofNullable;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import org.to2mbn.authlibinjector.internal.org.json.JSONObject;
import org.to2mbn.authlibinjector.transform.SkinWhitelistTransformUnit;
import org.to2mbn.authlibinjector.transform.TransformUnit;
import org.to2mbn.authlibinjector.transform.YggdrasilApiTransformUnit;
@ -13,7 +16,8 @@ public class InjectorConfig {
final String header = "-----BEGIN PUBLIC KEY-----\n";
final String end = "-----END PUBLIC KEY-----";
if (input.startsWith(header) && input.endsWith(end)) {
return Base64.getDecoder().decode(input.substring(header.length(), input.length() - end.length()).replace("\n", ""));
return Base64.getDecoder()
.decode(input.substring(header.length(), input.length() - end.length()).replace("\n", ""));
} else {
throw new IllegalArgumentException("Bad key format");
}
@ -63,4 +67,15 @@ public class InjectorConfig {
units.add(new YggdrasilKeyTransformUnit(decodePublicKey(publicKey)));
}
}
public void readFromJson(JSONObject json) {
skinWhitelistDomains = new ArrayList<>();
ofNullable(json.optJSONArray("skinDomains"))
.ifPresent(it -> it.forEach(domain -> {
if (domain instanceof String)
skinWhitelistDomains.add((String) domain);
}));
ofNullable(json.optString("signaturePublickey"))
.ifPresent(it -> publicKey = it);
}
}

View file

@ -0,0 +1,937 @@
package org.to2mbn.authlibinjector.internal.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.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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 <code>get</code> and <code>opt</code>
* methods for accessing the values by index, and <code>put</code> methods for
* adding or replacing values. The values can be any of these types:
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
* <code>Number</code>, <code>String</code>, or the
* <code>JSONObject.NULL object</code>.
* <p>
* The constructor can convert a JSON text into a Java object. The
* <code>toString</code> method converts to JSON text.
* <p>
* A <code>get</code> method returns a value if one can be found, and throws an
* exception if one cannot be found. An <code>opt</code> method returns a
* default value instead of throwing an exception, and so is useful for
* obtaining optional values.
* <p>
* The generic <code>get()</code> and <code>opt()</code> methods return an
* object which you can cast or query for type. There are also typed
* <code>get</code> and <code>opt</code> methods that do type checking and type
* coercion for you.
* <p>
* The texts produced by the <code>toString</code> methods strictly conform to
* JSON syntax rules. The constructors are more forgiving in the texts they will
* accept:
* <ul>
* <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
* before the closing bracket.</li>
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
* &nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
* quote)</small>.</li>
* <li>Strings do not need to be quoted at all if they do not begin with a quote
* or single quote, and if they do not contain leading or trailing spaces, and
* if they do not contain any of these characters:
* <code>{ } [ ] / \ : , #</code> and if they do not look like numbers and if
* they are not the reserved words <code>true</code>, <code>false</code>, or
* <code>null</code>.</li>
* </ul>
*
* @author JSON.org
* @version 2016-05-20
*/
public class JSONArray implements Iterable<Object>, Serializable {
private static final long serialVersionUID = 1L;
/**
* The arrayList where the JSONArray's properties are kept.
*/
private final List<Object> list;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
list = 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();
list.add(JSONObject.NULL);
} else {
x.back();
list.add(x.nextValue());
}
switch (x.nextClean()) {
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 <code>[</code>&nbsp;<small>(left
* bracket)</small> and ends with <code>]</code> &nbsp;
* <small>(right bracket)</small>.
* @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) {
list = new ArrayList<>();
if (collection != null) {
for (Object o : collection) {
list.add(JSONObject.wrap(o));
}
}
}
/**
* Construct a JSONArray from an array
*
* @param array
* The array
* @throws JSONException
* If not an array.
*/
public <T> JSONArray(T[] array) throws JSONException {
this();
for (T element : array) {
this.put(JSONObject.wrap(element));
}
}
@Override
public Iterator<Object> iterator() {
return list.iterator();
}
/**
* 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 = 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 = 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 = 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 enum value associated with an index.
*
* @param clazz
* The type of enum to retrieve.
* @param index
* The index must be between 0 and length() - 1.
* @param <E>
* The enum to retrieve
* @return The enum value at the index location
* @throws JSONException
* if the key is not found or if the value cannot be
* converted to an enum.
*/
public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException {
E val = optEnum(clazz, index);
if (val == null) {
// JSONException should really take a throwable argument.
// If it did, I would re-implement this with the Enum.valueOf
// method and place any thrown exception in the JSONException
throw new JSONException("JSONObject[" + JSONObject.quote(Integer.toString(index))
+ "] is not an enum of type " + JSONObject.quote(clazz.getSimpleName())
+ ".");
}
return val;
}
/**
* Get the BigDecimal 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 BigDecimal.
*/
public BigDecimal getBigDecimal(int index) throws JSONException {
Object object = get(index);
try {
return new BigDecimal(object.toString());
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] could not convert to BigDecimal.");
}
}
/**
* Get the BigInteger 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 BigInteger.
*/
public BigInteger getBigInteger(int index) throws JSONException {
Object object = get(index);
try {
return new BigInteger(object.toString());
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] could not convert to BigInteger.");
}
}
/**
* 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 = 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 = 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 = 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 = 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 = 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(opt(index));
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int length() {
return list.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 >= length()) ? null : list
.get(index);
}
/**
* 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 getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 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 getDouble(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 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 getInt(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the enum value associated with a key.
*
* @param clazz
* The type of enum to retrieve.
* @param index
* The index must be between 0 and length() - 1.
* @param <E>
* The enum to retrieve
* @return The enum value at the index location or null if not found
*/
public <E extends Enum<E>> E optEnum(Class<E> clazz, int index) {
return this.optEnum(clazz, index, null);
}
/**
* Get the enum value associated with a key.
*
* @param clazz
* The type of enum to retrieve.
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default in case the value is not found
* @param <E>
* The enum to retrieve
* @return The enum value at the index location or defaultValue if the value
* is not found or cannot be assigned to clazz
*/
public <E extends Enum<E>> E optEnum(Class<E> clazz, int index, E defaultValue) {
try {
Object val = opt(index);
if (JSONObject.NULL.equals(val)) {
return defaultValue;
}
if (clazz.isAssignableFrom(val.getClass())) {
// we just checked it!
@SuppressWarnings("unchecked")
E myE = (E) val;
return myE;
}
return Enum.valueOf(clazz, val.toString());
} catch (IllegalArgumentException e) {
return defaultValue;
} catch (NullPointerException e) {
return defaultValue;
}
}
/**
* Get the optional BigInteger 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 BigInteger optBigInteger(int index, BigInteger defaultValue) {
try {
return getBigInteger(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional BigDecimal 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 BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
try {
return getBigDecimal(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 = 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 = opt(index);
return o instanceof JSONObject ? (JSONObject) o : null;
}
/**
* 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 getLong(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns null 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, null);
}
/**
* 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 = opt(index);
return JSONObject.NULL.equals(object) ? defaultValue : object
.toString();
}
/**
* 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;
}
/**
* 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) {
list.add(value);
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 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 < length()) {
list.set(index, value);
} else {
while (index != 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) {
return index >= 0 && index < length()
? list.remove(index)
: null;
}
/**
* 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.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable representation of the
* array.
*/
@Override
public String toString() {
try {
return this.toString(0);
} 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 <code>[</code>&nbsp;<small>(left
* bracket)</small> and ending with <code>]</code> &nbsp;
* <small>(right bracket)</small>.
* @throws JSONException
* If the data structure is not acyclical
*/
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.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param writer
* The writer
* @return The writer.
* @throws JSONException
* If the data structure is not acyclical or an I/O
* error occurs
*/
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.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param writer
* Writes the serialized JSON
* @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
* If the data structure is not acyclical or an I/O
* error occurs
*/
public Writer write(Writer writer, int indentFactor, int indent)
throws JSONException {
try {
boolean commanate = false;
int length = length();
writer.write('[');
if (length == 1) {
JSONObject.writeValue(writer, list.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, list.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);
}
}
/**
* Returns a java.util.List containing all of the elements in this array. If
* an element in the array is a JSONArray or JSONObject it will also be
* converted.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a java.util.List containing the elements of this array
*/
public List<Object> toList() {
List<Object> results = new ArrayList<>(list.size());
for (Object element : list) {
if (element == null || JSONObject.NULL.equals(element)) {
results.add(null);
} else if (element instanceof JSONArray) {
results.add(((JSONArray) element).toList());
} else if (element instanceof JSONObject) {
results.add(((JSONObject) element).toMap());
} else {
results.add(element);
}
}
return results;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof JSONArray) {
JSONArray another = (JSONArray) obj;
return list.equals(another.list);
}
return false;
}
@Override
public int hashCode() {
return list.hashCode();
}
}

View file

@ -0,0 +1,45 @@
package org.to2mbn.authlibinjector.internal.org.json;
/**
* The JSONException is thrown by the JSON.org classes when things are amiss.
*
* @author JSON.org
* @version 2015-12-09
*/
public class JSONException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Constructs a JSONException with an explanatory message.
*
* @param message
* Detail about the reason for the exception.
*/
public JSONException(final String message) {
super(message);
}
/**
* Constructs a JSONException with an explanatory message and cause.
*
* @param message
* Detail about the reason for the exception.
* @param cause
* The cause.
*/
public JSONException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Constructs a new JSONException with the specified cause.
*
* @param cause
* The cause.
*/
public JSONException(final Throwable cause) {
super(cause.getMessage(), cause);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
package org.to2mbn.authlibinjector.internal.org.json;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>, and
* <code>JSONWriter.value(</code>Object<code>)</code>. The
* <code>toJSONString</code> method will be used instead of the default behavior
* of using the Object's <code>toString()</code> method and quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own
* JSON serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}

View file

@ -0,0 +1,487 @@
package org.to2mbn.authlibinjector.internal.org.json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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 2014-05-03
*/
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.markSupported()
? reader
: new BufferedReader(reader);
eof = false;
usePrevious = false;
previous = 0;
index = 0;
character = 1;
line = 1;
}
/**
* Construct a JSONTokener from an InputStream.
*
* @param inputStream
* The source.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public JSONTokener(InputStream inputStream) throws JSONException {
this(new InputStreamReader(inputStream));
}
/**
* 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.
*
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public void back() throws JSONException {
if (usePrevious || index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
index -= 1;
character -= 1;
usePrevious = true;
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 eof && !usePrevious;
}
/**
* Determine if the source string still contains characters that next() can
* consume.
*
* @return true if not yet at the end of the source.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public boolean more() throws JSONException {
this.next();
if (end()) {
return false;
}
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.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public char next() throws JSONException {
int c;
if (usePrevious) {
usePrevious = false;
c = previous;
} else {
try {
c = reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
if (c <= 0) { // End of stream
eof = true;
c = 0;
}
}
index += 1;
if (previous == '\r') {
line += 1;
character = c == '\n' ? 0 : 1;
} else if (c == '\n') {
line += 1;
character = 0;
} else {
character += 1;
}
previous = (char) c;
return 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 (end()) {
throw this.syntaxError("Substring bounds error");
}
pos += 1;
}
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
*
* @throws JSONException
* If an I/O error or a syntax error occurs
* @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 <code>"</code>&nbsp;
* <small>(double quote)</small> or <code>'</code>&nbsp;
* <small>(single quote)</small>.
* @return A String.
* @throws JSONException
* Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
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':
try {
sb.append((char) Integer.parseInt(this.next(4), 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape.", e);
}
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.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public String nextTo(char delimiter) throws JSONException {
StringBuilder sb = new StringBuilder();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
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.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
c == '\n' || c == '\r') {
if (c != 0) {
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 = nextClean();
String string;
switch (c) {
case '"':
case '\'':
return nextString(c);
case '{':
back();
return new JSONObject(this);
case '[':
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.
*/
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
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.
* @throws JSONException
* If an I/O error or a syntax error occurs
*/
public char skipTo(char to) throws JSONException {
char c;
try {
long startIndex = index;
long startCharacter = character;
long startLine = line;
reader.mark(1000000);
do {
c = this.next();
if (c == 0) {
reader.reset();
index = startIndex;
character = startCharacter;
line = startLine;
return c;
}
} while (c != to);
} catch (IOException exception) {
throw new JSONException(exception);
}
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 + toString());
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message
* The error message.
* @param causedBy
* The throwable that caused the error.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message, Throwable causedBy) {
return new JSONException(message + toString(), causedBy);
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return " at " + index + " [character " + character + " line " +
line + "]";
}
}

View file

@ -0,0 +1,25 @@
/**
* Modified <a href="https://github.com/stleary/JSON-java">org.json:json</a>.
*
* <pre>
* 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.
* </pre>
*/
package org.to2mbn.authlibinjector.internal.org.json;