
org.drizzle.jdbc.internal.common.Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of drizzle-jdbc Show documentation
Show all versions of drizzle-jdbc Show documentation
BSD Licensed JDBC driver for Drizzle and MySQL
The newest version!
/*
* Drizzle-JDBC
*
* Copyright (c) 2009-2011, Marcus Eriksson, Jay Pipes
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the driver nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.drizzle.jdbc.internal.common;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
/**
* User: marcuse Date: Feb 19, 2009 Time: 8:40:51 PM
*/
public class Utils {
private static final int START_BIT_MILLISECONDS = 17;
private static final int START_BIT_SECONDS = 11;
private static final int START_BIT_MINUTES = 5;
/*
* These masks allow us to "cut up" the packed integer into
* its components.
*/
private static final int MASK_HOURS = 0x0000001F;
private static final int MASK_MINUTES = 0x000007E0;
private static final int MASK_SECONDS = 0x0001F800;
private static final int MASK_MILLISECONDS = 0xFFFE0000;
private static boolean java5Determined = false;
private static boolean isJava5 = false;
/**
* returns true if the byte b needs to be escaped
*
* @param b the byte to check
* @return true if the byte needs escaping
*/
public static boolean needsEscaping(final byte b) {
if ((b & 0x80) == 0) {
switch (b) {
case '"':
case 0:
case '\n':
case '\r':
case '\\':
case '\'':
case '\032':
return true;
}
}
return false;
}
/*public static byte [] escape(byte [] input) {
byte [] buffer = new byte[input.length*2];
for(byte b:buffer) {
if(b <= (byte)127) {
switch(b) {
case 0:
case '"':
case '\n':
case '\r':
case '\\':
case '\'':
case '\032':
}
}
}
} */
/**
* escapes the given string, new string length is at most twice the length of str
*
* @param str the string to escape
* @return an escaped string
*/
public static String sqlEscapeString(final String str) {
StringBuilder buffer = new StringBuilder(str.length() * 2);
boolean neededEscaping = false;
for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i);
if (needsEscaping((byte) c)) {
neededEscaping = true;
buffer.append('\\');
}
buffer.append(c);
}
return neededEscaping ? buffer.toString() : str;
}
/**
* Counts the number of occurrences of a character in a string.
*
* Does not count chars enclosed in single or double quotes
*
* @param str the string to check
* @param chr the character to count
* @return the number of matching characters in the string
*/
public static int countChars(final String str, final char chr) {
int count = 0;
boolean isWithinDoubleQuotes = false;
boolean isWithinQuotes = false;
for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i);
if (c == '"' && !isWithinQuotes && !isWithinDoubleQuotes) {
isWithinDoubleQuotes = true;
} else if (c == '"' && !isWithinQuotes) {
isWithinDoubleQuotes = false;
}
if (c == '\'' && !isWithinQuotes && !isWithinDoubleQuotes) {
isWithinQuotes = true;
} else if (c == '\'' && !isWithinDoubleQuotes) {
isWithinQuotes = false;
}
if (!isWithinDoubleQuotes && !isWithinQuotes) {
if (chr == c) {
count++;
}
}
}
return count;
}
public static List createQueryParts(String query) {
boolean isWithinDoubleQuotes = false;
boolean isWithinQuotes = false;
int queryPos = 0;
int lastQueryPos = 0;
List queryParts = new LinkedList();
for (int i = 0; i < query.length(); i++) {
final char c = query.charAt(i);
if (c == '"' && !isWithinQuotes && !isWithinDoubleQuotes) {
isWithinDoubleQuotes = true;
} else if (c == '"' && !isWithinQuotes) {
isWithinDoubleQuotes = false;
}
if (c == '\'' && !isWithinQuotes && !isWithinDoubleQuotes) {
isWithinQuotes = true;
} else if (c == '\'' && !isWithinDoubleQuotes) {
isWithinQuotes = false;
}
if (!isWithinDoubleQuotes && !isWithinQuotes) {
if (c == '?') {
queryParts.add(query.substring(lastQueryPos, queryPos));
lastQueryPos = queryPos + 1;
}
}
queryPos++;
}
queryParts.add(query.substring(lastQueryPos, queryPos));
return queryParts;
}
private enum ParsingState {
WITHIN_COMMENT, WITHIN_QUOTES, WITHIN_DOUBLE_QUOTES, NORMAL
}
public static String stripQuery(final String query) {
final StringBuilder sb = new StringBuilder();
ParsingState parsingState = ParsingState.NORMAL;
ParsingState nextParsingState = ParsingState.NORMAL;
//final byte[] queryBytes = query.getBytes();
for (int i = 0; i < query.length(); i++) {
final char c = query.charAt(i);
int nextCodePoint = 0;
if (i < query.length() - 1) {
nextCodePoint = query.codePointAt(i + 1);
}
switch (parsingState) {
case WITHIN_DOUBLE_QUOTES:
if (c == '"') {
nextParsingState = ParsingState.NORMAL;
}
break;
case WITHIN_QUOTES:
if (c == '\'') {
nextParsingState = ParsingState.NORMAL;
}
break;
case NORMAL:
if (c == '\'') {
nextParsingState = ParsingState.WITHIN_QUOTES;
} else if (c == '"') {
nextParsingState = ParsingState.WITHIN_DOUBLE_QUOTES;
} else if (c == '/' && nextCodePoint == '*') {
nextParsingState = ParsingState.WITHIN_COMMENT;
parsingState = ParsingState.WITHIN_COMMENT;
} else if (c == '#') {
return sb.toString();
}
break;
case WITHIN_COMMENT:
if (c == '*' && nextCodePoint == '/') {
nextParsingState = ParsingState.NORMAL;
i++;
}
break;
}
if (parsingState != ParsingState.WITHIN_COMMENT) {
sb.append(c);
}
parsingState = nextParsingState;
}
return sb.toString();
}
/**
* encrypts a password
*
* protocol for authentication is like this: 1. mysql server sends a random array of bytes (the seed) 2. client
* makes a sha1 digest of the password 3. client hashes the output of 2 4. client digests the seed 5. client updates
* the digest with the output from 3 6. an xor of the output of 5 and 2 is sent to server 7. server does the same
* thing and verifies that the scrambled passwords match
*
* @param password the password to encrypt
* @param seed the seed to use
* @return a scrambled password
* @throws NoSuchAlgorithmException if SHA1 is not available on the platform we are using
*/
public static byte[] encryptPassword(final String password, final byte[] seed) throws NoSuchAlgorithmException {
if (password == null || password.equals("")) {
return new byte[0];
}
final MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
final byte[] stage1 = messageDigest.digest(password.getBytes());
messageDigest.reset();
final byte[] stage2 = messageDigest.digest(stage1);
messageDigest.reset();
messageDigest.update(seed);
messageDigest.update(stage2);
final byte[] digest = messageDigest.digest();
final byte[] returnBytes = new byte[digest.length];
for (int i = 0; i < digest.length; i++) {
returnBytes[i] = (byte) (stage1[i] ^ digest[i]);
}
return returnBytes;
}
/**
* packs the time portion of a millisecond time stamp into an int
*
* format:
*
* The part of the day, stored in a 4 byte integer as follows:
*
* | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* | mS | mS | mS | mS | mS | mS | mS | mS |
* | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* | mS | mS | mS | mS | mS | mS | mS | SS |
* | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 |
* | SS | SS | SS | SS | SS | MM | MM | MM |
* | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
* | MM | MM | MM | HH | HH | HH | HH | HH |
*
*
* @param milliseconds the milliseconds to pack
* @return a packed integer containing the time
*/
public static int packTime(final long milliseconds) {
final int millis = (int) (milliseconds % 1000);
final int seconds = (int) ((milliseconds / 1000) % 60);
final int minutes = (int) ((milliseconds / (1000 * 60)) % 60);
final int hours = (int) ((milliseconds / (1000 * 60 * 60)) % 24);
/* OK, now we pack the pieces into a 4-byte integer */
return (millis * (1 << START_BIT_MILLISECONDS))
+ (seconds * (1 << START_BIT_SECONDS))
+ (minutes * (1 << START_BIT_MINUTES))
+ hours;
}
/**
* unpacks an integer packed by packTime
*
* @param packedTime the packed time
* @return a millisecond time
* @see Utils#packTime(long)
*/
public static long unpackTime(final int packedTime) {
final int hours = (packedTime & MASK_HOURS);
final int minutes = (packedTime & MASK_MINUTES) >> (START_BIT_MINUTES);
final int seconds = (packedTime & MASK_SECONDS) >> (START_BIT_SECONDS);
final int millis = (packedTime & MASK_MILLISECONDS) >> (START_BIT_MILLISECONDS);
long returnValue = (long) hours * 60 * 60 * 1000;
returnValue += (long) minutes * 60 * 1000;
returnValue += (long) seconds * 1000;
returnValue += (long) millis;
return returnValue;
}
/**
* Copies the original byte array content to a new byte array. The resulting byte array is
* always "length" size. If length is smaller than the original byte array, the resulting
* byte array is truncated. If length is bigger than the original byte array, the resulting
* byte array is filled with zero bytes.
*
* @param orig the original byte array
* @param length how big the resulting byte array will be
* @return the copied byte array
*/
public static byte[] copyWithLength(byte[] orig, int length) {
// No need to initialize with zero bytes, because the bytes are already initialized with that
byte[] result = new byte[length];
int howMuchToCopy = length < orig.length ? length : orig.length;
System.arraycopy(orig, 0, result, 0, howMuchToCopy);
return result;
}
/**
* Copies from original byte array to a new byte array. The resulting byte array is
* always "to-from" size.
*
* @param orig the original byte array
* @param from index of first byte in original byte array which will be copied
* @param to index of last byte in original byte array which will be copied. This can be
* outside of the original byte array
* @return resulting array
*/
public static byte[] copyRange(byte[] orig, int from, int to) {
int length = to - from;
byte[] result = new byte[length];
int howMuchToCopy = orig.length - from < length ? orig.length - from : length;
System.arraycopy(orig, from, result, 0, howMuchToCopy);
return result;
}
/**
* Returns if it is a Java version up to Java 5.
*
* @return true if the VM is <= Java 5
*/
public static boolean isJava5() {
if (!java5Determined) {
try {
java.util.Arrays.copyOf(new byte[0], 0);
isJava5 = false;
} catch (java.lang.NoSuchMethodError e) {
isJava5 = true;
}
java5Determined = true;
}
return isJava5;
}
public static short byteArrayToShort(byte [] b) {
int value = byteArrayToInt(b);
if(value > Short.MAX_VALUE)
return Short.MAX_VALUE;
if(value < Short.MIN_VALUE)
return Short.MIN_VALUE;
return (short) value;
}
public static byte byteArrayToByte(byte [] b) {
int value = byteArrayToInt(b);
if(value > Byte.MAX_VALUE)
return Byte.MAX_VALUE;
if(value < Byte.MIN_VALUE)
return Byte.MIN_VALUE;
return (byte) value;
}
/**
* convert a byte array to an int
*
* sums negative numbers since Integer.MIN_VALUE < -Integer.MAX_VALUE
* @param b
* @return
*/
public static int byteArrayToInt(byte [] b) {
int sum = 0;
int len = b.length;
for(int i = 0; i < b.length; i++) {
if(b[i] == '.'){
len = i;
break;
}
}
int limit = -Integer.MAX_VALUE;
int startIdx = 0;
boolean isPositive = true;
if(len > 1) {
byte x = (byte) (b[0] - '0');
if(x < 0 || x > 9) {
if(b[0] == '+') {
isPositive = true;
limit = -Integer.MAX_VALUE;
startIdx++;
} else if(b[0] == '-') {
isPositive = false;
limit = Integer.MIN_VALUE;
startIdx++;
}
}
}
int factor = (int) Math.pow(10, len-1-startIdx);
for(int i = startIdx; i < len; i++) {
byte x = (byte) (b[i] - '0');
if(x < 0 || x > 9) throw new NumberFormatException("Could not parse as int");
int oldSum = sum;
sum -= (x * factor);
if(sum > oldSum || sum < limit) {
if(isPositive)
return Integer.MAX_VALUE;
else
return Integer.MIN_VALUE;
}
factor/=10;
}
return isPositive?-sum:sum;
}
public static long byteArrayToLong(byte [] b) {
long sum = 0;
int len = b.length;
for(int i = 0; i < b.length; i++) {
if(b[i] == '.'){
len = i;
break;
}
}
long limit = -Long.MAX_VALUE;
int startIdx = 0;
boolean isPositive = true;
if(len > 1) {
byte x = (byte) (b[0] - '0');
if(x < 0 || x > 9) {
if(b[0] == '+') {
isPositive = true;
limit = -Long.MAX_VALUE;
startIdx++;
} else if(b[0] == '-') {
isPositive = false;
limit = Long.MIN_VALUE;
startIdx++;
}
}
}
long factor = (long) Math.pow(10, len-1-startIdx);
for(int i = startIdx; i < len; i++) {
byte x = (byte) (b[i] - '0');
if(x < 0 || x > 9) throw new NumberFormatException("Could not parse as long");
long oldSum = sum;
sum -= (x * factor);
if(sum > oldSum || sum < limit) {
if(isPositive)
return Long.MAX_VALUE;
else
return Long.MIN_VALUE;
}
factor/=10;
}
return isPositive?-sum:sum;
//return Integer.MAX_VALUE;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy