1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
6 import static org.ibex.mail.MailException.*;
7 import org.ibex.crypto.*;
8 import org.ibex.util.*;
9 import org.ibex.mail.protocol.*;
12 import org.ibex.io.Fountain;
17 // FIXME: this is important: folded headers: can insert CRLF anywhere that whitespace appears (before the whitespace)
18 public abstract class Headers extends JS.Immutable implements Fountain {
20 public Headers set(String k, String v) {
21 Stream stream = getStream();
22 StringBuffer all = new StringBuffer();
25 for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
26 if (!Character.isSpace(s.charAt(0))) {
27 if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
28 key = s.substring(0, s.indexOf(':')).toLowerCase();
30 if (key.toLowerCase().equals(k.toLowerCase())) {
31 if (v != null) { all.append(k + ": " + v + "\r\n"); lines++; v = null; }
40 all.append(k + ": " + v + "\r\n");
43 return new Original(new Stream(all.toString()));
47 //public abstract String getString();
48 public abstract String[] getHeaderNames();
49 public abstract String get(String s);
51 public Headers set(String[] keyval) {
53 for(int i=0; i<keyval.length; i+=2)
54 ret = ret.set(keyval[i], keyval[i+1]);
57 public Headers remove(String key) { return set(key, null); /* FIXME */ }
59 public static class Original extends Headers {
60 private final Hash head = new Hash();
62 public final boolean mime;
64 private Fountain fountain;
66 //public String getString() { return raw; }
68 public Stream getStream() { return fountain.getStream(); }
69 public long getLength() { return fountain.getLength(); }
70 public int getNumLines() { return fountain.getNumLines(); }
72 public JS get(JS s) throws JSExn { return JSU.S(get(JSU.toString(s).toLowerCase())); }
74 public String[] getHeaderNames() { return (String[])head.dumpkeys(new String[head.size()]); }
75 public String get(String s) { return (String)head.get(s.toLowerCase()); }
77 public Original(Stream stream) throws Malformed { this(stream, false); }
78 public Original(Stream stream, boolean assumeMime) throws Malformed {
79 StringBuffer all = new StringBuffer();
82 for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
86 if (Character.isSpace(s.charAt(0))) {
87 if (key == null) throw new Malformed("Message began with a blank line; no headers");
88 head.put(key, head.get(key) + " " + s.trim());
91 if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
92 key = s.substring(0, s.indexOf(':')).toLowerCase();
93 for(int i=0; i<key.length(); i++)
94 if (key.charAt(i) < 33 || key.charAt(i) > 126) {
96 throw new Malformed("Header key \""+key+"\" contains invalid character \"" +
97 key.charAt(i) + "\" (0x"+Integer.toString(key.charAt(i), 16) +")");
99 String val = s.substring(s.indexOf(':') + 1).trim();
100 if (get(key) != null) val = get(key) + " " + val; // just append it to the previous one;
103 this.raw = all.toString();
104 // FIXME: be more efficient here?
105 this.fountain = new Fountain.StringFountain(this.raw);
107 this.mime = assumeMime | (get("mime-version") != null && get("mime-version").trim().equals("1.0"));
109 java.util.Enumeration e = head.keys();
111 String k = (String)e.next();
112 String v = (String)head.get(k);
113 if (mime) k = Encode.RFC2047.decode(k);
115 if (mime) v = Encode.RFC2047.decode(v);
122 // Helpers //////////////////////////////////////////////////////////////////////////////
124 public static Stream skip(Stream stream) {
125 for(String s = stream.readln(); s!=null && s.length() > 0;) s = stream.readln();
129 public static String uncomment(String val) {
130 boolean inquotes = false;
131 for(int i=0; i<val.length(); i++) {
132 if (val.charAt(i) == '\"') inquotes = !inquotes;
133 if (val.charAt(i) == '(' && !inquotes)
134 val = val.substring(0, i) + val.substring(val.indexOf(i--, ')') + 1);