📄 mimedecoder.java
字号:
/****************************************************************************** * Mail4ME - Mail for the Java 2 Micro Edition * * A lightweight, J2ME- (and also J2SE-) compatible package for sending and * receiving Internet mail messages using the SMTP and POP3 protocols. * * Copyright (c) 2000-2002 J鰎g Pleumann <joerg@pleumann.de> * * Mail4ME is part of the EnhydraME family of projects. See the following web * sites for more information: * * -> http://mail4me.enhydra.org * -> http://me.enhydra.org * * Mail4ME is distributed under the Enhydra Public License (EPL), which is * discussed in great detail here: * * -> http://www.enhydra.org/software/license/index.html * * Have fun! ******************************************************************************/package de.trantor.mail;import java.io.ByteArrayOutputStream;import java.util.Vector;/** * Is a helper class for decoding MIME data contained in e-mail messages. As * the name implies, MimeDecoder allows a kind of read-only view of the MIME * parts contained in a message, giving access to textual as well as binary * content. * <p> * Every MimeDecoder works on and thus represents exactly one MIME part, which * according to RFC 2045 - RFC 2049 may be either of the following two: * <ul> * <li> A single-part, meaning that it's a leaf in the tree-like hierarchy of * MIME parts. In this case content may be retrieved directly using the * getBodyLine() or getBodyBytes() methods. * <li> A multi-part, meaning that it's an inner node in the hierarchy. In * that case subordinate parts should be investigated. These parts may * again be single-part as well as multi-part. * </ul> * To start working with a MimeDecoder, a new instance has to be created for * a given message, which results in a top-level MIME view of the message. * Subordinate parts can then be accessed using the getPart() method that serves * as a kind of factory method for MimeDecoders representing the lower MIME * levels of the message. * <p> * Please note that a MimeDecoder is not notified of any changes to the * underlying message it is working on, so changes to the message will make the * MimeDecoder invalid. A new MimeDecoder should be created after changes have * been applied. Otherwise the results are unpredictable and will most probably * result in some kind of Exception. * <p> * One could argue that there should be a closer integration between the Message * and the MimeDecoder classes that is able to handle these changes. The * rationale for the given implementation is - again - memory savings. The * MimeDecoder is actually an optional component that *can* be used on a Message * if the application demands it. The Message class, on the other hand, is not * dependent on the MimeDecoder at all, which means that the Message class can * be deployed without having to deploy the MimeDecoder class, too. * <p> * A short note for implementators that want to understand the internal * structure of the MimeDecoder class: The basic idea is that the MimeDecoder * holds a direct reference to the message's Vector of lines (both header and * body) as well as two integer fields telling where the part represented by * the MimeDecoder starts and ends. In case of multi-parts, an additonal * Vector holds all the occurences of the part boundary string, which makes * it quite easy to create additional MimeDecoders for the subordinate MIME * parts. * * @see Message * @see #MimeDecoder(Message) * @see #getBodyLine(int) * @see #getBodyBytes() * @see #getPart(int) */public class MimeDecoder { /** * Holds the header and body parts of the message this MimeDecoder is working * on. This is a direct reference to the Vector used inside the corresponding * message, not a copy of it. */ private Vector lines; /** * Holds the line number where the body of the MIME part represented by this * MimeDecoder instance begins (in the underlying message). */ private int begin; /** * Holds the line number where the body of the MIME part represented by this * MimeDecoder instance ends. Note that this is exclusive, so we're talking * about the first line that does not belong to the body any more. */ private int end; /** * Holds the type of the MIME part represented by this MimeDecoder instance. * The type is deduced from the "Content-Type:" header field. */ private String type; /** * Holds the name of the MIME part represented by this MimeDecoder instance. * The name is deduced from the "name=" sub-field of the "Content-Type:" * header field. Is is mainly used for binary attachments like pictures etc. */ private String name; /** * Holds the encoding of the MIME part represented by this MimeDecoder * instance. The encoding is deduced from the "Content-Transfer-Encoding:" * header field. */ private String encoding; /** * Holds all the line numbers where separators belonging to this MIME part are * found, in case this is a multipart body. The line numbers are stored in * Integer instances. Note that the actual subordinate MIME parts lie between * the given lines (exclusive!), so if we have n values in this Vector, we * actually have n-1 body parts. */ private Vector parts; /** * Creates a new MimeDecoder for a given message. This constructor returns * a new instance of the MimeDecoder class that grants access to a top-level * MIME view of the message. Depending on the value returned by the getType() * method, the message contains only this part -- in which case it can be * accessed by the getBodyLine() or getBodyBytes() methods --, or it is * constructed from a number of subordinate MIME parts which can be accessed * one by one using the getPart() method. * * @see #getBodyLine(int) * @see #getBodyBytes() */ public MimeDecoder(Message message) { init(message.getLines(), 0, message.getLines().size()); } /** * Creates a new MimeDecoder instance for a subordinate part of the part or * message represented by the given MimeDecoder. The new part ranges from * the given beginning to the given end. This is an internal constructor * required by the getPart() method. */ private MimeDecoder(MimeDecoder parent, int begin, int end) { init(parent.lines, begin, end); } /** * Initializes a newly created MimeDecoder. This is the most tricky method * in the whole class, because it has to fetch all MIME information from * the part's header and find all the occurences of the part boundary, in * case it is a multipart body. * <p> * The method expects the messages line vector and the beginning of the * part's header as well as the end (being the first line after the body). * Note that during its operation the method advances the begin counter to * the beginning of the part body, before it assigns the two counters to * the private fields of the same names. */ private void init(Vector lines, int begin, int end) { this.lines = lines; /** * Walk through the whole header and look for the MIME-relevant fields. * The end of the header is denoted by the usual empty line. Note that * the begin counter advances while we're doing this, so that it points * to the first line of the body later. */ String s = (String)lines.elementAt(begin); while (!s.equals("")) { begin++; /** * Undo header folding, that is, put logical header lines that span * multiple physical ones into one String. While this is superfluous * for the main message header (because the Pop3Client class already * does this) it is necessary for the headers of the various MIME parts * (which the Pop3Client doesn't know about). */ while (begin < end) { String t = (String)lines.elementAt(begin); if (t.startsWith(" ") || t.startsWith("\t")) { s = s + t; begin++; } else break; } /** * Fetch a lower-case representation of the field name found in the * current header line. Check if it is a MIME-relevant field. If so, * extract additional information from the field and store it in the * corresponding attributes of the MIME decoder. */ String name = Message.getStringName(s).toLowerCase(); if (name.equals("content-type")) { /** * The "Content-Type:" field usually contains several sub-fields, so * the first thing we do is split the list of sub-fields into a * String array. The first array entry must be the basic content type. * The other elements may contain additional information, so we walk * through the array starting from index 1. */ String[] elements = Message.getStringElements(Message.getStringValue(s), ';'); type = elements[0].toLowerCase(); for (int i = 1; i < elements.length; i++) { /** * If the MimeDecoder represents a multipart body *and* the part * boundary string is found, iterate through the whole body to find * all the occurences of the boundary string. */ if (type.startsWith("multipart/") && getStringName(elements[i]).toLowerCase().equals("boundary")) { /** * The occurences of the boundary inside the body begin with two * additional hyphens. */ String boundary = "--" + getStringValue(elements[i]); /** * Initialize and fill the part list. */ parts = new Vector(); for (int j = begin; j < end; j++) { if (getLine(j).startsWith(boundary)) { parts.addElement(new Integer(j)); } } } /** * If the MimeDecoder represents a singlepart body, that is, a leaf * in the tree-like hierarchy of MIME parts, it may have a filename. */ else if (getStringName(elements[i]).toLowerCase().equals("name")) { this.name = getStringValue(elements[i]); } } } /** * The "Content-Transfer-Encoding" attribute tells us how the MIME part * is encoded. A common encoding that doesn't require any additional work * is "text/plain", which is also the default encoding if none is * specified explicitly. If the encoding is "base64", the part's data * should be retrieved using the getBodyBytes() method. */ else if (name.equals("content-transfer-encoding")) { encoding = Message.getStringValue(s); } if (begin < end) { s = (String)lines.elementAt(begin); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -