/*
* The MIT License
*
* Copyright 2014 Karol Bucek.
*
* 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.
*/
package org.jruby.ext.openssl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Locale;
import org.jcodings.specific.UTF8Encoding;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.jruby.Ruby;
import org.jruby.RubyEncoding;
import org.jruby.RubyFile;
import org.jruby.RubyIO;
import org.jruby.RubyString;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.ext.openssl.x509store.PEMInputOutput;
/**
*
* @author kares
*/
abstract class StringHelper {
static RubyString newString(final Ruby runtime, final byte[] bytes) {
final ByteList byteList = new ByteList(bytes, false);
return RubyString.newString(runtime, byteList);
}
static RubyString newString(final Ruby runtime, final byte[] bytes, final int count) {
final ByteList byteList = new ByteList(bytes, 0, count, false);
return RubyString.newString(runtime, byteList);
}
static ByteList setByteListShared(final RubyString str) {
try {
str.setByteListShared();
return str.getByteList();
}
catch (NoSuchMethodError err) { // JRuby 1.6
RubyString dup = (RubyString) str.dup();
return dup.getByteList();
}
}
static RubyString newUTF8String(final Ruby runtime, final ByteList bytes) {
ByteList byteList = new ByteList(RubyEncoding.encodeUTF8(bytes), UTF8Encoding.INSTANCE, false);
return new RubyString(runtime, runtime.getString(), byteList);
}
static RubyString newUTF8String(final Ruby runtime, final CharSequence chars) {
ByteList byteList = new ByteList(RubyEncoding.encodeUTF8(chars), UTF8Encoding.INSTANCE, false);
return new RubyString(runtime, runtime.getString(), byteList);
}
static RubyString newStringFrozen(final Ruby runtime, final ByteList bytes) {
final RubyString str = RubyString.newStringShared(runtime, bytes);
str.setFrozen(true); return str;
}
static RubyString newStringFrozen(final Ruby runtime, final CharSequence chars) {
final RubyString str = RubyString.newString(runtime, chars);
str.setFrozen(true); return str;
}
static byte[] readX509PEM(final ThreadContext context, IRubyObject arg) {
final RubyString str = StringHelper.readPossibleDERInput(context, arg);
final ByteList bytes = str.getByteList();
return readX509PEM(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
}
static byte[] readX509PEM(final byte[] bytes, final int offset, final int length) {
InputStreamReader in = new InputStreamReader(new ByteArrayInputStream(bytes, offset, length));
try {
byte[] readBytes = PEMInputOutput.readX509PEM(in);
if ( readBytes != null ) return readBytes;
}
catch (IOException e) {
// this is not PEM encoded, let's use the default argument
}
return bytes;
}
static RubyString readPossibleDERInput(final ThreadContext context, final IRubyObject arg) {
return readInput(context, OpenSSL.to_der_if_possible(context, arg));
}
static RubyString readInput(final ThreadContext context, final IRubyObject arg) {
if ( arg instanceof RubyIO ) {
final IRubyObject result;
if ( arg instanceof RubyFile ) {
result = ( (RubyFile) arg.dup() ).read(context);
}
else {
result = ( (RubyIO) arg ).read(context);
}
if ( result instanceof RubyString ) return (RubyString) result;
throw context.runtime.newArgumentError("IO `" + arg.inspect() + "' contained no data");
}
return arg.asString();
}
static final ByteList NEW_LINE = new ByteList(new byte[] { '\n' }, false);
static final ByteList COMMA_SPACE = new ByteList(new byte[] { ',',' ' }, false);
static void gsub(final Ruby runtime, final ByteList str, final byte match, final byte replace) {
final int begin = str.getBegin();
final int slen = str.getRealSize();
final byte[] bytes = str.getUnsafeBytes();
for ( int i = begin; i < begin + slen; i++ ) {
if ( bytes[i] == match ) bytes[i] = replace;
}
}
static final char[] S20 = new char[] {
' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ',
};
private static final DateTimeFormatter ASN_DATE_NO_ZONE =
DateTimeFormat.forPattern("MMM dd HH:mm:ss yyyy") // + " zzz"
.withLocale(Locale.US)
.withZone(DateTimeZone.UTC);
static StringBuilder appendGMTDateTime(final StringBuilder text, final DateTime time) {
final String date = ASN_DATE_NO_ZONE.print( time.getMillis() );
final int len = text.length();
text.append(date).append(' ').append("GMT");
if ( date.charAt(4) == '0' ) { // Jul 07 -> Jul 7
text.setCharAt(len + 4, ' ');
}
return text;
}
//static StringBuilder lowerHexBytes(final BigInteger bytes) {
// return lowerHexBytes(bytes.toByteArray(), 1); // skip the sign bit
//}
static StringBuilder lowerHexBytes(final byte[] bytes, final int offset) {
final int len = bytes.length;
final StringBuilder hex = new StringBuilder(len * 3);
for (int i = offset; i < bytes.length; i++ ) {
final String h = Integer.toHexString( bytes[i] & 0xFF );
if ( h.length() == 1 ) hex.append('0');
hex.append( h ).append(':');
}
if ( hex.length() > 0 ) hex.setLength( hex.length() - 1 );
return hex;
}
static void appendLowerHexValue(final StringBuilder text, final byte[] hex,
final int indent, final int rowLength) {
final StringBuilder hexStr = lowerHexBytes( hex, 0 );
final int len = hexStr.length(); int left = len;
while ( left > 0 ) {
int print = rowLength; if ( left < rowLength ) print = left;
final int start = len - left;
text.append(S20,0,indent).append( hexStr, start, start + print ).append('\n');
left -= print;
}
}
@SuppressWarnings("unchecked")
static <T extends CharSequence> ArrayList<T> split(final T string, final char separator) {
final ArrayList<T> split = new ArrayList<T>(8); int last = 0;
for ( int i = 0; i < string.length(); i++ ) {
if ( string.charAt(i) == separator ) {
split.add( (T) string.subSequence(last, i) ); last = ++i;
}
}
if ( last == 0 ) split.add(string); // split.isEmpty
else split.add( (T) string.subSequence(last, string.length()) );
return split;
}
public static String[] split(final String string, final char separator) {
final ArrayList<CharSequence> split = split((CharSequence) string, separator);
return split.toArray( new String[ split.size() ] );
}
}