+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
package org.ibex.mail;
+import static org.ibex.mail.MailException.*;
+import org.ibex.crypto.*;
+import org.ibex.util.*;
+import org.ibex.mail.protocol.*;
+import org.ibex.js.*;
+import org.ibex.io.*;
+import org.ibex.io.Fountain;
+import java.util.*;
+import java.net.*;
+import java.io.*;
// FEATURE: MIME RFC2045, 2046, 2049
/** This class contains logic for encoding and decoding MIME multipart messages */
public class MIME {
- public static class QuotedPrintable {
-
- public static String decode(String s, boolean lax) {
- //
- // =XX -> hex representation, must be uppercase
- // 9, 32, 33-60, 62-126 can be literal
- // 9, 32 at end-of-line must get encoded
- // trailing whitespace must be deleted when decoding
- // =\n = soft line break
- // lines cannot be more than 76 chars long
- //
-
- // lax is used for RFC2047 headers; removes restrictions on which chars you can encode
- return s;
+
+ /** Part = Headers+Body */
+ public static class Part extends JSReflection implements Fountain {
+ public final Headers headers;
+ public final ContentType contentType;
+ private final String encoding;
+
+ private final Fountain all;
+ private final Fountain body;
+
+ public Stream getStream() { return all.getStream(); }
+ public int getNumLines() { return all.getNumLines(); }
+ public long getLength() { return all.getLength(); }
+ public Fountain getBody() { return body; }
+
+ public JS get(JS key) throws JSExn {
+ String k = JSU.toString(key);
+ if ("body".equals(k)) {
+ StringBuffer sb = new StringBuffer();
+ getBody().getStream().transcribe(sb);
+ return JSU.S(sb.toString());
+ }
+ return super.get(key);
+ }
+
+ public Part(final Fountain fount, String[] keyval) {
+ Headers h = new Headers(fount);
+ this.headers = keyval==null ? h : new Headers(h, keyval);
+ String ctype = headers.get("content-type");
+ this.encoding = headers.get("content-transfer-encoding");
+ String enc = this.encoding;
+ if (enc!=null) enc = enc.toLowerCase();
+ if (!(enc == null || enc.equals("7bit") || enc.equals("8bit") || enc.equals("binary") ||
+ enc.equals("quoted-printable") || enc.equals("base64"))) {
+ Log.warn(MIME.class, "unknown TransferEncoding \"" + encoding + "\"");
+ ctype = "application/octet-stream";
+ }
+ this.contentType = new ContentType(ctype, headers.get("content-description"), headers.get("content-id"), encoding);
+ // FIXME: this is a horrible, tangled mess.
+ this.body = new Fountain() {
+ public int getNumLines() { return Stream.countLines(this.getStream()); }
+ public long getLength() { return Stream.countBytes(this.getStream()); }
+ public Stream getStream() { return transformBodyStream(Headers.skip(fount.getStream())); }
+ };
+ this.all =
+ keyval==null
+ ? fount
+ : Fountain.Util.concat(this.headers, Fountain.Util.create("\r\n"), this.body);
+ }
+
+ private Stream transformBodyStream(Stream body) {
+ //"quoted-printable".equals(encoding) ? Encode.QuotedPrintable.decode(body.toString(),false) :
+ //"base64".equals(encoding) ? Encode.fromBase64(body.toString()) :
+ return body;
+ }
+
+ /*
+ public Part getPart(int i) {
+ Stream stream = body.getStream();
+ Vec v = new Vec();
+ // first part begins with a boundary delimiter
+ for(String s = stream.readln(); s != null; s = stream.readln())
+ if (s.equals("--" + contentType.parameters.get("boundary"))) break;
+ while(true) {
+ Stream substream = new BoundaryStream(stream, (String)contentType.parameters.get("boundary"));
+ Part p = new Part(substream, true, null); // FIXME split off headers
+ v.addElement(p);
+ if (substream.isLast()) break;
+ }
+ return parts = (Part[])v.copyInto(new Part[v.size()]);
+ }
+ */
+ }
+
+ /*
+ public static class Boundary implements Stream.Transformer {
+ private final String boundary;
+ private boolean done = false;
+ private boolean last = false;
+ public Boundary(String bounardy) { this.boundary = boundary; }
+ public boolean isLast() { while(!done) readln(); return last; }
+ public Stream transform(Stream stream) {
+ for(String s = stream.readln(); s != null; s = stream.readln()) {
+ if (boundary != null && (s.equals(boundary) || s.equals(boundary + "--"))) {
+ body.setLength(body.length() - 2); // preceeding CRLF is part of delimiter
+ last = s.equals(boundary + "--");
+ done = true;
+ break;
+ }
+ body.append(s);
+ body.append("\r\n");
+ //lines++;
+ }
}
}
+ */
}