
package com.xerox.amazonws.common;

import java.io.CharArrayWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.BitSet;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;

/**
 * This encoder is to be used instead of the URLEncoder when encoding params for version 2 signing
 */
public class SignerEncoder {
	private static BitSet dontEncode;
	private static String defaultEncoding;

	static {
		// encode everything except what is included in the bitset
		dontEncode = new BitSet(256);
		for (int i='a'; i<='z'; i++) {
			dontEncode.set(i);
		}
		for (int i='A'; i<='Z'; i++) {
			dontEncode.set(i);
		}
		for (int i='0'; i<='9'; i++) {
			dontEncode.set(i);
		}
		dontEncode.set('-');
		dontEncode.set('_');
		dontEncode.set('.');
		dontEncode.set('~');

		defaultEncoding = (String)AccessController.doPrivileged(new GetPropertyAction("file.encoding"));
	}

	public static String encode(String str) throws UnsupportedEncodingException {
		int lowerDiff = 'a' - 'A';
		StringBuffer ret = new StringBuffer(str.length());
		CharArrayWriter charsToEscape = new CharArrayWriter();
		try {
			for (int pos = 0; pos < str.length(); pos++) {
				int chr = (int)str.charAt(pos);
				if (dontEncode.get(chr)) {
					ret.append((char)chr);
				}
				else {
					do {
						charsToEscape.write(chr);
						if (chr >= 0xD800 && chr <= 0xDBFF) {
							if (pos < (str.length()-1)) {
								int lower = (int)str.charAt(pos+1);
								if (lower >= 0xDC00 && lower <= 0xDFFF) {
									charsToEscape.write(lower);
									pos++;
								}
							}
						}
						pos++;
					} while (pos < str.length() && !dontEncode.get((chr = (int)str.charAt(pos))));
					charsToEscape.flush();
					String tmp = new String(charsToEscape.toCharArray());
					byte [] bytes = tmp.getBytes("UTF-8");
					for (int i=0; i<bytes.length; i++) {
						ret.append("%");
						char ch = Character.forDigit((bytes[i] >> 4) & 0xf, 16);
						if (Character.isLetter(ch)) { ch -= lowerDiff; }
						ret.append(ch);
						ch = Character.forDigit(bytes[i] & 0xf, 16);
						if (Character.isLetter(ch)) { ch -= lowerDiff; }
						ret.append(ch);
					}
					charsToEscape.reset();
				}
			}
		} catch (UnsupportedEncodingException ex) { }
		return ret.toString();
	}

	public static void main(String [] args) throws Exception {
		String test1 = "abcdef ABCDEF -_.~";
		System.out.println("'"+test1+"' signer encoded as '"+SignerEncoder.encode(test1)+"'");
		String test2 = "hi\u2022there\077";
		System.out.println("'"+test2+"' signer encoded as '"+SignerEncoder.encode(test2)+"'");
		System.out.println("'"+test2+"' url encoded as '"+java.net.URLEncoder.encode(test2, "UTF-8")+"'");
	}
}

@@ -123,137 +123,19 @@
 ava.
-nio.charset.Charset;%0Aimport java.util.BitSet;%0Aimport java.security.AccessController;%0Aimport sun.security.action.GetPropertyAction
+util.BitSet
 ;%0A%0A/
@@ -307,48 +307,8 @@
 ode;
-%0A%09private static String defaultEncoding;
 %0A%0A%09s
@@ -650,24 +650,24 @@
 e.set('.');%0A
+
 %09%09dontEncode
@@ -680,108 +680,8 @@
 ~');
-%0A%0A%09%09defaultEncoding = (String)AccessController.doPrivileged(new GetPropertyAction(%22file.encoding%22));
 %0A%09%7D%0A

drop unused code that produced warnings