// 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 { /** 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++; } } } */ }