/* ********************************************************************** * * Use, duplication, or disclosure by the Government is subject to * restricted rights as set forth in the DFARS. * * BBNT Solutions LLC * A Part of * Verizon * 10 Moulton Street * Cambridge, MA 02138 * (617) 873-3000 * * Copyright (C) 2002 by BBNT Solutions, LLC * All Rights Reserved. * ********************************************************************** */ package com.bbn.openmap.tools.beanbox; import java.io.InputStream; import java.io.PrintWriter; /** * An RFC 844 or MIME message header. Includes methods for parsing * headers from incoming streams, fetching values, setting values, and * printing headers. Key values of null are legal: they indicate lines * in the header that don't have a valid key, but do have a value * (this isn't legal according to the standard, but lines like this * are everywhere). */ public class MessageHeader { private String keys[]; private String values[]; private int nkeys; public MessageHeader() { grow(); } public MessageHeader(InputStream is) throws java.io.IOException { parseHeader(is); } /** * Find the value that corresponds to this key. It finds only the * first occurrence of the key. * * @param k the key to find. * @return null if not found. */ public String findValue(String k) { if (k == null) { for (int i = nkeys; --i >= 0;) if (keys[i] == null) return values[i]; } else for (int i = nkeys; --i >= 0;) { if (k.equalsIgnoreCase(keys[i])) return values[i]; } return null; } public String getKey(int n) { if (n < 0 || n >= nkeys) return null; return keys[n]; } public String getValue(int n) { if (n < 0 || n >= nkeys) return null; return values[n]; } /** * Find the next value that corresponds to this key. It finds the * first value that follows v. To iterate over all the values of a * key use: * * <pre> * * for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) { * ... * } * * </pre> */ public String findNextValue(String k, String v) { boolean foundV = false; if (k == null) { for (int i = nkeys; --i >= 0;) if (keys[i] == null) if (foundV) return values[i]; else if (values[i].equalsIgnoreCase(v)) foundV = true; } else for (int i = nkeys; --i >= 0;) if (k.equalsIgnoreCase(keys[i])) if (foundV) return values[i]; else if (values[i].equalsIgnoreCase(v)) foundV = true; return null; } /** * Prints the key-value pairs represented by this header. Also * prints the RFC required blank line at the end. Omits pairs with * a null key. */ public void print(PrintWriter p) { for (int i = 0; i < nkeys; i++) if (keys[i] != null) p.println(keys[i] + (values[i] != null ? ": " + values[i] : "") + "\r"); p.println("\r"); p.flush(); } /** * Adds a key value pair to the end of the header. Duplicates are * allowed. */ public void add(String k, String v) { grow(); keys[nkeys] = k; values[nkeys] = v; nkeys++; } /** * Prepends a key value pair to the beginning of the header. * Duplicates are allowed. */ public void prepend(String k, String v) { grow(); for (int i = nkeys; i > 0; i--) { keys[i] = keys[i - 1]; values[i] = values[i - 1]; } keys[0] = k; values[0] = v; nkeys++; } /** * Overwrite the previous key/val pair at location 'i' with the * new k/v. If the index didn't exist before the key/val is simply * tacked onto the end. */ public void set(int i, String k, String v) { grow(); if (i < 0) { return; } else if (i > nkeys) { add(k, v); } else { keys[i] = k; values[i] = v; } } /** * Grow the key/value arrays as needed */ private void grow() { if (keys == null || nkeys >= keys.length) { String[] nk = new String[nkeys + 4]; String[] nv = new String[nkeys + 4]; if (keys != null) System.arraycopy(keys, 0, nk, 0, nkeys); if (values != null) System.arraycopy(values, 0, nv, 0, nkeys); keys = nk; values = nv; } } /** * Sets the value of a key. If the key already exists in the * header, it's value will be changed. Otherwise a new key/value * pair will be added to the end of the header. */ public void set(String k, String v) { for (int i = nkeys; --i >= 0;) if (k.equalsIgnoreCase(keys[i])) { values[i] = v; return; } add(k, v); } /** * Convert a message-id string to canonical form (strips off * leading and trailing <>s) */ public static String canonicalID(String id) { if (id == null) return ""; int st = 0; int len = id.length(); boolean substr = false; int c; while (st < len && ((c = id.charAt(st)) == '<' || c <= ' ')) { st++; substr = true; } while (st < len && ((c = id.charAt(len - 1)) == '>' || c <= ' ')) { len--; substr = true; } return substr ? id.substring(st, len) : id; } /** Parse a MIME header from an input stream. */ public void parseHeader(InputStream is) throws java.io.IOException { nkeys = 0; if (is == null) return; char s[] = new char[10]; int firstc = is.read(); while (firstc != '\n' && firstc != '\r' && firstc >= 0) { int len = 0; int keyend = -1; int c; boolean inKey = firstc > ' '; s[len++] = (char) firstc; parseloop: { parseloop2: while ((c = is.read()) >= 0) { switch (c) { case ':': if (inKey && len > 0) keyend = len; inKey = false; break; case '\t': c = ' '; case ' ': inKey = false; break; case '\r': case '\n': firstc = is.read(); if (c == '\r' && firstc == '\n') { firstc = is.read(); if (firstc == '\r') firstc = is.read(); } if (firstc == '\n' || firstc == '\r' || firstc > ' ') break parseloop; /* continuation */ continue parseloop2; } if (len >= s.length) { char ns[] = new char[s.length * 2]; System.arraycopy(s, 0, ns, 0, len); s = ns; } s[len++] = (char) c; } firstc = -1; } while (len > 0 && s[len - 1] <= ' ') len--; String k; if (keyend <= 0) { k = null; keyend = 0; } else { k = String.copyValueOf(s, 0, keyend); if (keyend < len && s[keyend] == ':') keyend++; while (keyend < len && s[keyend] <= ' ') keyend++; } String v; if (keyend >= len) v = new String(); else v = String.copyValueOf(s, keyend, len - keyend); add(k, v); } } public String toString() { StringBuffer result = new StringBuffer(super.toString()); for (int i = 0; i < keys.length; i++) { result.append("{").append(keys[i]).append(": ").append(values[i]).append("}"); } return result.toString(); } }