// // (C) Copyright 2007 VeriSign, Inc. All Rights Reserved. // // VeriSign, Inc. shall have no responsibility, financial or // otherwise, for any consequences arising out of the use of // this material. The program material is provided on an "AS IS" // BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. // // Distributed under an Apache License // http://www.apache.org/licenses/LICENSE-2.0 // package org.verisign.joid; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; /** * Parses an OpenID message. * * TODO: Made public only for unit tests. */ public class MessageParser { private final static Log log = LogFactory.getLog(MessageParser.class); static char newline = '\n'; /** * Unrolls a message as a string. This string will use the * <code>name:value</code> format of the specification. See also * {@link #toUrlString()}. * * @return the message as a string. */ static String toPostString(Message message) throws OpenIdException { return toStringDelimitedBy(message, ":", newline); } /** * Unrolls a message as a string. This string will use encoding * suitable for URLs. See also {@link #toPostString()}. * * @return the message as a string. */ static String toUrlString(Message message) throws OpenIdException { return toStringDelimitedBy(message, "=", '&'); } private static String toStringDelimitedBy(Message message, String kvDelim, char lineDelim) throws OpenIdException { Map map = message.toMap(); Set set = map.entrySet(); StringBuffer sb = new StringBuffer(); try { for (Iterator iter=set.iterator(); iter.hasNext();){ Map.Entry mapEntry = (Map.Entry) iter.next(); String key = (String) mapEntry.getKey(); String value = (String) mapEntry.getValue(); if (lineDelim == newline){ sb.append(key+kvDelim+value); sb.append(lineDelim); } else { if (value != null) { sb.append(URLEncoder.encode(key, "UTF-8")+kvDelim +URLEncoder.encode(value, "UTF-8")); if (iter.hasNext()) { sb.append(lineDelim); } } else { throw new OpenIdException("Value for key '" + key + "' is null in message map"); } } } return sb.toString(); } catch (UnsupportedEncodingException e){ // should not happen throw new RuntimeException("Internal error"); } } static int numberOfNewlines(String query) throws IOException { BufferedReader br = new BufferedReader(new StringReader(query)); int n = 0; while (br.readLine() != null){n += 1;} //log.warn ("number of lines="+n+" for "+query); return n; } /** * Translate a query string to a Map. * * TODO: Made public only for unit tests. Do not use. */ public static Map urlEncodedToMap(String query) throws UnsupportedEncodingException { Map map = new HashMap(); if (query == null) { return map; } StringTokenizer st = new StringTokenizer(query, "?&=;", true); String previous = null; while (st.hasMoreTokens()) { String current = st.nextToken(); if ("?".equals(current) || "&".equals(current) || ";".equals(current)) { //ignore } else if ("=".equals(current)) { String name = URLDecoder.decode(previous, "UTF-8"); if (st.hasMoreTokens()){ String value = URLDecoder.decode(st.nextToken(), "UTF-8"); if (isGoodValue(value)){ map.put(name, value); } } } else { previous = current; } } return map; } private static boolean isGoodValue(String value) { if ("&".equals(value) || ";".equals(value)){ return false; } // more tests here perchance return true; } static Map postedToMap(String query) throws IOException { Map map = new HashMap(); if (query == null) { return map; } BufferedReader br = new BufferedReader(new StringReader(query)); String s = br.readLine(); while (s != null) { int index = s.indexOf(":"); if (index != -1) { String name = s.substring(0, index); String value = s.substring(index+1, s.length()); if (name != null && value != null){ map.put(name, value); } } s = br.readLine(); } return map; } }