import java.io.*;
public class Misc {
+ public static final java.nio.charset.Charset utf8 = java.nio.charset.Charset.forName("UTF-8");
private static Map<Integer, String> stext = new HashMap<Integer, String>();
static {
ret = new ErrorHandler(ret);
return(ret);
}
+
+ public static int hex2int(char digit) {
+ if((digit >= '0') && (digit <= '9'))
+ return(digit - '0');
+ if((digit >= 'a') && (digit <= 'f'))
+ return(digit - 'a' + 10);
+ if((digit >= 'A') && (digit <= 'F'))
+ return(digit - 'A' + 10);
+ throw(new NumberFormatException("Invalid hex digit " + digit));
+ }
+
+ public static char int2hex(int nibble, boolean upper) {
+ if((nibble >= 0) && (nibble <= 9))
+ return((char)('0' + nibble));
+ if((nibble >= 10) && (nibble <= 15))
+ return((char)((upper?'A':'a') + nibble - 10));
+ throw(new NumberFormatException("Invalid hex nibble " + nibble));
+ }
+
+ public static String htmlq(String in) {
+ StringBuilder buf = new StringBuilder();
+ for(int i = 0; i < in.length(); i++) {
+ char c = in.charAt(i);
+ if(c == '&')
+ buf.append("&");
+ else if(c == '<')
+ buf.append("<");
+ else if(c == '>')
+ buf.append(">");
+ else
+ buf.append(c);
+ }
+ return(buf.toString());
+ }
+
+ public static String urlq(String in) {
+ byte[] bytes = in.getBytes(utf8);
+ StringBuilder buf = new StringBuilder();
+ for(int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ if((b < 32) || (b == ' ') || (b == '&') || (b == '?') || (b == '/') || (b == '=') || (b == '#') || (b == '%') || (b == '+') || (b >= 128)) {
+ buf.append('%');
+ buf.append(int2hex((b & 0xf0) >> 4, true));
+ buf.append(int2hex(b & 0x0f, true));
+ } else {
+ buf.append((char)b);
+ }
+ }
+ return(buf.toString());
+ }
}
--- /dev/null
+package dolda.jsvc.util;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+public class MixedBuffer {
+ private ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ private Writer conv;
+ private Charset cs;
+
+ public MixedBuffer(Charset cs) {
+ this.cs = cs;
+ conv = new OutputStreamWriter(buf, cs);
+ }
+
+ public MixedBuffer() {
+ this(Misc.utf8);
+ }
+
+ public void append(byte b) {
+ buf.write(b);
+ }
+
+ public void append(char c) {
+ try {
+ conv.write(c);
+ conv.flush();
+ } catch(IOException e) {
+ throw(new Error(e));
+ }
+ }
+
+ public String convert() throws java.nio.charset.CharacterCodingException {
+ CharsetDecoder dec = cs.newDecoder();
+ ByteBuffer in = ByteBuffer.wrap(buf.toByteArray());
+ CharBuffer out = dec.decode(in);
+ return(out.toString());
+ }
+
+ public int size() {
+ return(buf.size());
+ }
+}
--- /dev/null
+package dolda.jsvc.util;
+
+import dolda.jsvc.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.CharacterCodingException;
+
+public class Params {
+ public static class EncodingException extends RequestRestart {
+ public EncodingException(String msg) {
+ super(msg);
+ }
+
+ public void respond(Request req) {
+ throw(Restarts.stdresponse(400, "Invalid parameter encoding", getMessage()));
+ }
+ }
+
+ public static MultiMap<String, String> urlparams(String q) {
+ try {
+ MultiMap<String, String> ret = new WrappedMultiMap<String, String>(new TreeMap<String, Collection<String>>());
+ String st = "key";
+ String key = null; /* Java is stupid. */
+ MixedBuffer buf = new MixedBuffer();
+ int i = 0;
+ while(true) {
+ int c = (i >= q.length())?-1:(q.charAt(i++));
+ if(st == "key") {
+ if(c == '%') {
+ if(q.length() - i < 2)
+ throw(new EncodingException("Invalid character escape"));
+ try {
+ buf.append((byte)((Misc.hex2int(q.charAt(i)) << 4) | Misc.hex2int(q.charAt(i + 1))));
+ } catch(NumberFormatException e) {
+ throw(new EncodingException("Invalid character escape"));
+ }
+ i += 2;
+ } else if(c == '=') {
+ key = buf.convert();
+ buf = new MixedBuffer();
+ st = "val";
+ } else if(c == '&') {
+ ret.add(buf.convert(), "");
+ buf = new MixedBuffer();
+ } else if(c == -1) {
+ if(buf.size() == 0) {
+ break;
+ } else {
+ ret.add(buf.convert(), "");
+ buf = new MixedBuffer();
+ }
+ } else {
+ buf.append((char)c);
+ }
+ } else if(st == "val") {
+ if(c == '%') {
+ if(q.length() - i < 2)
+ throw(new EncodingException("Invalid character escape"));
+ try {
+ buf.append((byte)((Misc.hex2int(q.charAt(i)) << 4) | Misc.hex2int(q.charAt(i + 1))));
+ } catch(NumberFormatException e) {
+ throw(new EncodingException("Invalid character escape"));
+ }
+ i += 2;
+ } else if((c == '&') || (c == -1)) {
+ ret.add(key, buf.convert());
+ buf = new MixedBuffer();
+ st = "key";
+ } else if(c == '+') {
+ buf.append(' ');
+ } else {
+ buf.append((char)c);
+ }
+ }
+ }
+ return(ret);
+ } catch(CharacterCodingException e) {
+ throw(new EncodingException("Escaped parameter text is not proper UTF-8"));
+ }
+ }
+
+ public static MultiMap<String, String> urlparams(URL url) {
+ return(urlparams(url.getQuery()));
+ }
+
+ public static MultiMap<String, String> urlparams(Request req) {
+ return(urlparams(req.url()));
+ }
+
+ public static String encquery(Map<String, String> pars) {
+ StringBuilder buf = new StringBuilder();
+ boolean f = true;
+ for(Map.Entry<String, String> par : pars.entrySet()) {
+ if(!f)
+ buf.append('&');
+ buf.append(Misc.urlq(par.getKey()));
+ buf.append('=');
+ buf.append(Misc.urlq(par.getValue()));
+ f = false;
+ }
+ return(buf.toString());
+ }
+}