name fix
[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 // 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 {
19
20     public Headers set(String k, String v) {
21         Stream stream = getStream();
22         StringBuffer all = new StringBuffer();
23         int lines = 0;
24         String key = null;
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();
29             }
30             if (key.toLowerCase().equals(k.toLowerCase())) {
31                 if (v != null) { all.append(k + ": " + v + "\r\n"); lines++; v = null; }
32                 continue;
33             }
34             all.append(s);
35             all.append("\r\n");
36             lines++;
37         }
38         if (v != null) {
39             lines++;
40             all.append(k + ": " + v + "\r\n");
41         }
42         all.append("\r\n");
43         return new Original(new Stream(all.toString()));
44     }
45     
46     // FIXME
47     public abstract String getString();
48
49     public abstract String get(String s);
50     public abstract java.util.Enumeration names();
51
52     public Headers set(String[] keyval) {
53         Headers ret = this;
54         for(int i=0; i<keyval.length; i+=2)
55             ret = ret.set(keyval[i], keyval[i+1]);
56         return ret;
57     }
58     public Headers remove(String key) { return set(key, null); /* FIXME */ }
59
60     public String getLowerCaseTrimmed(String s) {
61         String ret = get(s);
62         return ret==null ? null : ret.toLowerCase();
63     }
64     
65     public static class Original extends Headers {
66         private final Hash head = new Hash();
67         private final Hash headModified = new Hash();
68         public        int lines;
69         public  final boolean mime;
70         private String raw;
71         private StringFountain fountain;
72
73         public String getString() { return raw; }
74         public Stream getStream() { return fountain.getStream(); }
75         public int    getLength() { return fountain.getLength(); }
76         public int    getNumLines() { return fountain.getNumLines(); }
77         public Stream getStreamWithCRLF() { return new Stream(raw+"\r\n"); }
78         public JS get(JS s) throws JSExn { return JSU.S(get(JSU.toString(s).toLowerCase())); }
79         public java.util.Enumeration names() { return head.enumerateKeys(); }
80         
81         public String get(String s) { return (String)head.get(s.toLowerCase()); }
82         public Original(Stream stream) throws Malformed { this(stream, false); }
83         public Original(Stream stream, boolean assumeMime) throws Malformed {
84             StringBuffer all = new StringBuffer();
85             String key = null;
86             int lines = 0;
87             for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
88                 all.append(s);
89                 all.append("\r\n");
90                 lines++;
91                 if (Character.isSpace(s.charAt(0))) {
92                     if (key == null) throw new Malformed("Message began with a blank line; no headers");
93                     head.put(key, head.get(key) + " " + s.trim());
94                     continue;
95                 }
96                 if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
97                 key = s.substring(0, s.indexOf(':')).toLowerCase();
98                 for(int i=0; i<key.length(); i++)
99                     if (key.charAt(i) < 33 || key.charAt(i) > 126)
100                         throw new Malformed("Header key \""+key+"\" contains invalid character \"" + key.charAt(i) + "\"");
101                 String val = s.substring(s.indexOf(':') + 1).trim();
102                 if (get(key) != null) val = get(key) + " " + val; // just append it to the previous one;
103                 head.put(key, val);
104             }
105             this.raw = all.toString();
106             this.fountain = new Fountain.StringFountain(this.raw);
107             this.lines = lines;
108             this.mime = assumeMime | (get("mime-version") != null && get("mime-version").trim().equals("1.0"));
109             /*
110               java.util.Enumeration e = head.keys();
111               while(e.hasNext()) {
112               String k = (String)e.next();
113               String v = (String)head.get(k);
114               if (mime) k = Encode.RFC2047.decode(k);
115               v = uncomment(v);
116               if (mime) v = Encode.RFC2047.decode(v);
117               head.put(k, v);
118               }
119             */
120         }
121
122
123     // Helpers //////////////////////////////////////////////////////////////////////////////
124
125     public static Stream skip(Stream stream) {
126         for(String s = stream.readln(); s!=null && s.length() > 0;) s = stream.readln();
127         return stream;
128     }
129
130     public static String uncomment(String val) {
131         boolean inquotes = false;
132         for(int i=0; i<val.length(); i++) {
133             if (val.charAt(i) == '\"') inquotes = !inquotes;
134             if (val.charAt(i) == '(' && !inquotes)
135                 val = val.substring(0, i) + val.substring(val.indexOf(i--, ')') + 1);
136         }
137         return val;
138     }
139     }
140 }