70ad93cc7937eb033c52764d1068d8739aaf3468
[org.ibex.mail.git] / src / org / ibex / mail / Headers.java
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.
4
5 package org.ibex.mail;
6 import static org.ibex.mail.MailException.*;
7 import org.ibex.crypto.*;
8 import org.ibex.util.*;
9 import org.ibex.mail.protocol.*;
10 import org.ibex.js.*;
11 import org.ibex.io.*;
12 import org.ibex.io.Fountain;
13 import java.util.*;
14 import java.net.*;
15 import java.io.*;
16
17 public class Headers extends JS.Immutable implements Fountain {
18     private final Hash head = new Hash();
19     private final Hash headModified = new Hash();
20     public        int lines;
21     public  final boolean mime;
22
23     private String raw;
24     private StringFountain fountain;
25
26     public String get(String s) {
27         String ret = (String)headModified.get(s.toLowerCase());
28         if (ret==null) ret = (String)head.get(s.toLowerCase());
29         return ret;
30     }
31     public void put(String k, String v) {
32         Stream stream = getStream();
33         StringBuffer all = new StringBuffer();
34         int lines = 0;
35         boolean good = false;
36         String key = null;
37         for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
38             if (Character.isSpace(s.charAt(0))) { all.append(s); all.append("\r\n"); lines++; continue; }
39             if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
40             key = s.substring(0, s.indexOf(':')).toLowerCase();
41             lines++;
42             if (key.toLowerCase().equals(k.toLowerCase())) {
43                 good = true;
44                 all.append(k + ": " + v + "\r\n");
45                 continue;
46             }
47             all.append(s);
48             all.append("\r\n");
49         }
50         if (!good) {
51             lines++;
52             all.append(k + ": " + v + "\r\n");
53         }
54         this.raw = all.toString();
55         this.lines = lines;
56         this.fountain = new Fountain.StringFountain(this.raw);
57     }
58     public JS get(JS s) throws JSExn { return JSU.S(get(JSU.toString(s).toLowerCase())); }
59
60     public Stream getStream() { return fountain.getStream(); }
61     public int    getLength() { return fountain.getLength(); }
62     public int    getNumLines() { return fountain.getNumLines(); }
63     public Stream getStreamWithCRLF() { return new Stream(raw+"\r\n"); }
64     
65     // FIXME
66     public String getString() { return raw; }
67
68     public Headers(Stream stream) throws Malformed { this(stream, false); }
69     public Headers(Stream stream, boolean assumeMime) throws Malformed {
70         StringBuffer all = new StringBuffer();
71         String key = null;
72         int lines = 0;
73         for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
74             all.append(s);
75             all.append("\r\n");
76             lines++;
77             if (Character.isSpace(s.charAt(0))) {
78                 if (key == null) throw new Malformed("Message began with a blank line; no headers");
79                 head.put(key, head.get(key) + " " + s.trim());
80                 continue;
81             }
82             if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
83             key = s.substring(0, s.indexOf(':')).toLowerCase();
84             for(int i=0; i<key.length(); i++)
85                 if (key.charAt(i) < 33 || key.charAt(i) > 126)
86                     throw new Malformed("Header key \""+key+"\" contains invalid character \"" + key.charAt(i) + "\"");
87             String val = s.substring(s.indexOf(':') + 1).trim();
88             if (get(key) != null) val = get(key) + " " + val; // just append it to the previous one;
89             head.put(key, val);
90         }
91         this.raw = all.toString();
92         this.fountain = new Fountain.StringFountain(this.raw);
93         this.lines = lines;
94         this.mime = assumeMime | (get("mime-version") != null && get("mime-version").trim().equals("1.0"));
95         /*
96           java.util.Enumeration e = head.keys();
97           while(e.hasNext()) {
98           String k = (String)e.next();
99           String v = (String)head.get(k);
100           if (mime) k = Encode.RFC2047.decode(k);
101           v = uncomment(v);
102           if (mime) v = Encode.RFC2047.decode(v);
103           head.put(k, v);
104           }
105         */
106     }
107
108     // Helpers //////////////////////////////////////////////////////////////////////////////
109
110     public static Stream skip(Stream stream) {
111         for(String s = stream.readln(); s!=null && s.length() > 0;) s = stream.readln();
112         return stream;
113     }
114
115     public static String uncomment(String val) {
116         boolean inquotes = false;
117         for(int i=0; i<val.length(); i++) {
118             if (val.charAt(i) == '\"') inquotes = !inquotes;
119             if (val.charAt(i) == '(' && !inquotes)
120                 val = val.substring(0, i) + val.substring(val.indexOf(i--, ')') + 1);
121         }
122         return val;
123     }
124 }