1/**************************************************************** 2 * Licensed to the Apache Software Foundation (ASF) under one * 3 * or more contributor license agreements. See the NOTICE file * 4 * distributed with this work for additional information * 5 * regarding copyright ownership. The ASF licenses this file * 6 * to you under the Apache License, Version 2.0 (the * 7 * "License"); you may not use this file except in compliance * 8 * with the License. You may obtain a copy of the License at * 9 * * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, * 13 * software distributed under the License is distributed on an * 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15 * KIND, either express or implied. See the License for the * 16 * specific language governing permissions and limitations * 17 * under the License. * 18 ****************************************************************/ 19 20package org.apache.james.mime4j.message; 21 22import java.io.IOException; 23import java.io.InputStream; 24import java.io.OutputStream; 25import java.util.Stack; 26 27import org.apache.james.mime4j.BodyDescriptor; 28import org.apache.james.mime4j.ContentHandler; 29import org.apache.james.mime4j.MimeStreamParser; 30import org.apache.james.mime4j.decoder.Base64InputStream; 31import org.apache.james.mime4j.decoder.QuotedPrintableInputStream; 32import org.apache.james.mime4j.field.Field; 33import org.apache.james.mime4j.field.UnstructuredField; 34 35 36/** 37 * Represents a MIME message. The following code parses a stream into a 38 * <code>Message</code> object. 39 * 40 * <pre> 41 * Message msg = new Message(new BufferedInputStream( 42 * new FileInputStream("mime.msg"))); 43 * </pre> 44 * 45 * 46 * 47 * @version $Id: Message.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $ 48 */ 49public class Message extends Entity implements Body { 50 51 /** 52 * Creates a new empty <code>Message</code>. 53 */ 54 public Message() { 55 } 56 57 /** 58 * Parses the specified MIME message stream into a <code>Message</code> 59 * instance. 60 * 61 * @param is the stream to parse. 62 * @throws IOException on I/O errors. 63 */ 64 public Message(InputStream is) throws IOException { 65 MimeStreamParser parser = new MimeStreamParser(); 66 parser.setContentHandler(new MessageBuilder()); 67 parser.parse(is); 68 } 69 70 71 /** 72 * Gets the <code>Subject</code> field. 73 * 74 * @return the <code>Subject</code> field or <code>null</code> if it 75 * doesn't exist. 76 */ 77 public UnstructuredField getSubject() { 78 return (UnstructuredField) getHeader().getField(Field.SUBJECT); 79 } 80 81 /** 82 * 83 * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream) 84 */ 85 public void writeTo(OutputStream out) throws IOException { 86 getHeader().writeTo(out); 87 88 Body body = getBody(); 89 if (body instanceof Multipart) { 90 Multipart mp = (Multipart) body; 91 mp.writeTo(out); 92 } else { 93 body.writeTo(out); 94 } 95 } 96 97 98 private class MessageBuilder implements ContentHandler { 99 private Stack stack = new Stack(); 100 101 public MessageBuilder() { 102 } 103 104 private void expect(Class c) { 105 if (!c.isInstance(stack.peek())) { 106 throw new IllegalStateException("Internal stack error: " 107 + "Expected '" + c.getName() + "' found '" 108 + stack.peek().getClass().getName() + "'"); 109 } 110 } 111 112 /** 113 * @see org.apache.james.mime4j.ContentHandler#startMessage() 114 */ 115 public void startMessage() { 116 if (stack.isEmpty()) { 117 stack.push(Message.this); 118 } else { 119 expect(Entity.class); 120 Message m = new Message(); 121 ((Entity) stack.peek()).setBody(m); 122 stack.push(m); 123 } 124 } 125 126 /** 127 * @see org.apache.james.mime4j.ContentHandler#endMessage() 128 */ 129 public void endMessage() { 130 expect(Message.class); 131 stack.pop(); 132 } 133 134 /** 135 * @see org.apache.james.mime4j.ContentHandler#startHeader() 136 */ 137 public void startHeader() { 138 stack.push(new Header()); 139 } 140 141 /** 142 * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String) 143 */ 144 public void field(String fieldData) { 145 expect(Header.class); 146 ((Header) stack.peek()).addField(Field.parse(fieldData)); 147 } 148 149 /** 150 * @see org.apache.james.mime4j.ContentHandler#endHeader() 151 */ 152 public void endHeader() { 153 expect(Header.class); 154 Header h = (Header) stack.pop(); 155 expect(Entity.class); 156 ((Entity) stack.peek()).setHeader(h); 157 } 158 159 /** 160 * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor) 161 */ 162 public void startMultipart(BodyDescriptor bd) { 163 expect(Entity.class); 164 165 Entity e = (Entity) stack.peek(); 166 Multipart multiPart = new Multipart(); 167 e.setBody(multiPart); 168 stack.push(multiPart); 169 } 170 171 /** 172 * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream) 173 */ 174 public void body(BodyDescriptor bd, InputStream is) throws IOException { 175 expect(Entity.class); 176 177 String enc = bd.getTransferEncoding(); 178 if ("base64".equals(enc)) { 179 is = new Base64InputStream(is); 180 } else if ("quoted-printable".equals(enc)) { 181 is = new QuotedPrintableInputStream(is); 182 } 183 184 Body body = null; 185 if (bd.getMimeType().startsWith("text/")) { 186 body = new MemoryTextBody(is, bd.getCharset()); 187 } else { 188 body = new MemoryBinaryBody(is); 189 } 190 191 ((Entity) stack.peek()).setBody(body); 192 } 193 194 /** 195 * @see org.apache.james.mime4j.ContentHandler#endMultipart() 196 */ 197 public void endMultipart() { 198 stack.pop(); 199 } 200 201 /** 202 * @see org.apache.james.mime4j.ContentHandler#startBodyPart() 203 */ 204 public void startBodyPart() { 205 expect(Multipart.class); 206 207 BodyPart bodyPart = new BodyPart(); 208 ((Multipart) stack.peek()).addBodyPart(bodyPart); 209 stack.push(bodyPart); 210 } 211 212 /** 213 * @see org.apache.james.mime4j.ContentHandler#endBodyPart() 214 */ 215 public void endBodyPart() { 216 expect(BodyPart.class); 217 stack.pop(); 218 } 219 220 /** 221 * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream) 222 */ 223 public void epilogue(InputStream is) throws IOException { 224 expect(Multipart.class); 225 StringBuffer sb = new StringBuffer(); 226 int b; 227 while ((b = is.read()) != -1) { 228 sb.append((char) b); 229 } 230 ((Multipart) stack.peek()).setEpilogue(sb.toString()); 231 } 232 233 /** 234 * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream) 235 */ 236 public void preamble(InputStream is) throws IOException { 237 expect(Multipart.class); 238 StringBuffer sb = new StringBuffer(); 239 int b; 240 while ((b = is.read()) != -1) { 241 sb.append((char) b); 242 } 243 ((Multipart) stack.peek()).setPreamble(sb.toString()); 244 } 245 246 /** 247 * TODO: Implement me 248 * 249 * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream) 250 */ 251 public void raw(InputStream is) throws IOException { 252 throw new UnsupportedOperationException("Not supported"); 253 } 254 255 } 256} 257