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 // FEATURE: MIME RFC2045, 2046, 2049
19 /** This class contains logic for encoding and decoding MIME multipart messages */
22 public static class Part extends JSReflection implements Fountain {
23 public final Headers headers;
24 public final ContentType contentType;
25 private final String encoding;
27 private final Fountain all;
28 public Stream getStream() { return all.getStream(); }
29 public int getNumLines() { return all.getNumLines(); }
30 public int getLength() { return all.getLength(); }
32 public Stream getBody() {
34 "quoted-printable".equals(encoding) ? Encode.QuotedPrintable.decode(body.toString(),false) :
35 "base64".equals(encoding) ? Encode.fromBase64(body.toString()) :
37 Headers.skip(getStream());
40 public Part(Fountain all) {
41 this.headers = new Headers(all.getStream());
42 String ctype = headers.get("content-type");
43 this.encoding = headers.get("content-transfer-encoding");
44 if (!(encoding == null || encoding.equals("7bit") || encoding.equals("8bit") || encoding.equals("binary") ||
45 encoding.equals("quoted-printable") || encoding.equals("base64"))) {
46 Log.warn(MIME.class, "unknown TransferEncoding \"" + encoding + "\"");
47 ctype = "application/octet-stream";
49 this.contentType = new ContentType(ctype, headers.get("content-description"), headers.get("content-id"), encoding);
54 public Part getPart(int i) {
55 Stream stream = body.getStream();
57 // first part begins with a boundary delimiter
58 for(String s = stream.readln(); s != null; s = stream.readln())
59 if (s.equals("--" + contentType.parameters.get("boundary"))) break;
61 Stream substream = new BoundaryStream(stream, (String)contentType.parameters.get("boundary"));
62 Part p = new Part(substream, true, null); // FIXME split off headers
64 if (substream.isLast()) break;
66 return parts = (Part[])v.copyInto(new Part[v.size()]);
71 public static class Boundary implements Stream.Transformer {
72 private final String boundary;
73 private boolean done = false;
74 private boolean last = false;
75 public Boundary(String bounardy) { this.boundary = boundary; }
76 public boolean isLast() { while(!done) readln(); return last; }
77 public Stream transform(Stream stream) {
78 for(String s = stream.readln(); s != null; s = stream.readln()) {
79 if (boundary != null && (s.equals(boundary) || s.equals(boundary + "--"))) {
80 body.setLength(body.length() - 2); // preceeding CRLF is part of delimiter
81 last = s.equals(boundary + "--");