001: package com.protomatter.util;
002:
003: /**
004: * {{{ The Protomatter Software License, Version 1.0
005: * derived from The Apache Software License, Version 1.1
006: *
007: * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution,
022: * if any, must include the following acknowledgment:
023: * "This product includes software developed for the
024: * Protomatter Software Project
025: * (http://protomatter.sourceforge.net/)."
026: * Alternately, this acknowledgment may appear in the software itself,
027: * if and wherever such third-party acknowledgments normally appear.
028: *
029: * 4. The names "Protomatter" and "Protomatter Software Project" must
030: * not be used to endorse or promote products derived from this
031: * software without prior written permission. For written
032: * permission, please contact support@protomatter.com.
033: *
034: * 5. Products derived from this software may not be called "Protomatter",
035: * nor may "Protomatter" appear in their name, without prior written
036: * permission of the Protomatter Software Project
037: * (support@protomatter.com).
038: *
039: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
040: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
041: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
042: * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
043: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
044: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
045: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
046: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
047: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
048: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
049: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
050: * SUCH DAMAGE. }}}
051: */
052:
053: import java.io.*;
054: import java.util.*;
055:
056: /**
057: * This class is used in conjunction with the MIMEMessage class
058: * to make a multipart MIME message. A MIMEAttachment enacpsulates
059: * a single attachment (i.e. an image, a document, etc). Attached
060: * binaries are encoded using the Base64 method.
061: *
062: * @see MIMEMessage
063: */
064: public class MIMEAttachment implements Serializable {
065: private Hashtable headers = new Hashtable();
066: private boolean isBinary = false;
067: private byte[] content = null;
068: private static String CRLF = "\r\n";
069:
070: /**
071: * Create a MIMEAttachment object with the given MIME content type and
072: * description. The content will not be encoded using Base64.
073: */
074: public MIMEAttachment(String type, String description,
075: String content) {
076: this ();
077: setHeader("Content-Type", type);
078: setHeader("Content-Description", description);
079: this .content = content.getBytes();
080: setBinary(false);
081: }
082:
083: /**
084: * Create a MIMEAttachment object with the given MIME content type and
085: * description. The content will be encoded using Base64 if
086: * the <TT>isBinary</TT> flag is <tt>true</TT>.
087: */
088: public MIMEAttachment(String type, String description, byte[] data,
089: boolean isBinary) {
090: this (type, description, data);
091: setBinary(isBinary);
092: }
093:
094: /**
095: * Create a MIMEAttachment object with the given MIME type and
096: * description. The content will be encoded using Base64.
097: */
098: public MIMEAttachment(String type, String description, byte[] data) {
099: this ();
100: setHeader("Content-Type", type);
101: setHeader("Content-Description", description);
102: this .content = data;
103: setBinary(true);
104: }
105:
106: /**
107: * Create an empty attachment.
108: */
109: public MIMEAttachment() {
110: super ();
111: }
112:
113: /**
114: * Set the headers. Keys and values in
115: * must be strings.
116: */
117: public void setHeaders(Hashtable headers) {
118: this .headers = headers;
119: }
120:
121: /**
122: * Set the flag to indicate that the content
123: * of this attachment is binary.
124: */
125: public void setBinary(boolean b) {
126: isBinary = b;
127: }
128:
129: /**
130: * Set the content of this attachment.
131: */
132: public void setContent(String content) {
133: this .content = content.getBytes();
134: }
135:
136: /**
137: * Set the content of this attachment.
138: */
139: public void setContent(byte[] content) {
140: this .content = content;
141: }
142:
143: /**
144: * Set a header value.
145: */
146: public void setHeader(String headerName, String headerVal) {
147: headers.put(headerName, headerVal);
148: }
149:
150: /**
151: * Remove a header value.
152: */
153: public void removeHeader(String headerName) {
154: headers.remove(headerName);
155: }
156:
157: /**
158: * Get a header value.
159: */
160: public String getHeader(String headerName) {
161: String val = (String) headers.get(headerName);
162: return val;
163: }
164:
165: /**
166: * Some headers (such as Content-Disposition) have multiple
167: * key="value" pairs associated with them. This method allows
168: * you to get at those values easily. For instance, to get
169: * the "filename" chunk of the "Content-Disposition" header,
170: * call getSubHeader("Content-Disposition", "filename");
171: * If you call it with sub = "", it will retrieve the first
172: * value (which doesn't have a name)
173: */
174: public String getSubHeader(String name, String sub) {
175: String h = getHeader(name);
176: if (h == null) // not going to find much there.
177: return h;
178: StringTokenizer st = new StringTokenizer(h, ";");
179: if (sub.equals(""))
180: return stripSurroundingWhiteSpace(st.nextToken());
181: else
182: st.nextToken(); // skip the first one.
183: while (st.hasMoreTokens()) {
184: StringTokenizer nst = new StringTokenizer(
185: stripSurroundingWhiteSpace(st.nextToken()), "=");
186: String key = nst.nextToken();
187: String val = nst.nextToken();
188: if (key.equals(sub)) {
189: if (val.startsWith("\"") && val.endsWith("\""))
190: return val.substring(1, val.length() - 1);
191: else
192: return val;
193: }
194: }
195: // not found.
196: return "";
197: }
198:
199: // used by getSubHeader
200: private static String stripSurroundingWhiteSpace(String s) {
201: int i = 0; // index of first non-whitespace char
202: int j = 0; // index of last non-whitespace char
203: for (i = 0; i < s.length()
204: && Character.isWhitespace(s.charAt(i)); i++)
205: ;
206: for (j = s.length() - 1; j >= 0
207: && Character.isWhitespace(s.charAt(j)); j--)
208: ;
209: return s.substring(i, j + 1);
210: }
211:
212: /**
213: * Produces a chunk of text, including the encoded attachment object
214: */
215: public String toString() {
216: StringWriter sw = new StringWriter();
217: PrintWriter pw = new PrintWriter(sw);
218: write(pw);
219: pw.flush();
220: return sw.toString();
221: }
222:
223: /**
224: * Append the content of this attachment to
225: * the given StringBuffer.
226: */
227: public void write(PrintWriter w) {
228: Enumeration e = headers.keys();
229: while (e.hasMoreElements()) {
230: String hName = (String) e.nextElement();
231: String hVal = getHeader(hName);
232: w.print(hName);
233: w.print(": ");
234: w.print(hVal);
235: w.print(CRLF);
236: }
237: if (isBinary()) {
238: if (getHeader("Content-Transfer-Encoding") == null) {
239: w.print("Content-Transfer-Encoding: BASE64");
240: w.print(CRLF);
241: }
242: w.print(CRLF);
243: w.print(CRLF);
244: String encoded = Base64.encode(content);
245:
246: // split the content up into 65 character wide blocks
247: int start = 0;
248: int end = 65;
249: while (start < encoded.length()) {
250: if (end >= encoded.length())
251: end = encoded.length();
252: w.print(encoded.substring(start, end));
253: w.print(CRLF);
254: start = end;
255: end += 65;
256: }
257: } else // content == null, contentString != null
258: {
259: w.print(CRLF);
260: w.print(new String(content));
261: }
262: }
263:
264: /**
265: * Return the content. If it's binary, it's a byte array
266: * of binary data, if it's ASCII, you can just call
267: * <TT>new String(attachmentgetContent())</TT>.
268: */
269: public byte[] getContent() {
270: return content;
271: }
272:
273: /**
274: * Get an Enumeration of the header names.
275: */
276: public Enumeration getHeaderNames() {
277: return headers.keys();
278: }
279:
280: /**
281: * Is the content of the attachment ascii or binary?
282: */
283: public boolean isBinary() {
284: return isBinary;
285: }
286: }
|