001 /* 002 * Copyright 2010 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package com.hs.mail.imap.processor.fetch; 017 018 import java.io.IOException; 019 import java.io.InputStream; 020 import java.util.HashMap; 021 import java.util.Map; 022 023 import org.apache.commons.io.IOUtils; 024 import org.apache.james.mime4j.MimeException; 025 import org.apache.james.mime4j.parser.Field; 026 import org.apache.james.mime4j.parser.MimeTokenStream; 027 028 /** 029 * 030 * @author Won Chul Doh 031 * @since Mar 8, 2010 032 * 033 */ 034 public class PartContentBuilder { 035 036 public static final byte[] EMPTY = {}; 037 038 private MimeTokenStream parser; 039 private boolean topLevel = true; 040 041 public PartContentBuilder() { 042 parser = MimeParser.createDefaultMimeParser(); 043 } 044 045 public void build(InputStream is, int[] path) { 046 try { 047 parser.setRecursionMode(MimeTokenStream.M_RECURSE); 048 parser.parse(is); 049 topLevel = true; 050 if (path != null) { 051 for (int i = 0; i < path.length; i++) { 052 to(path[i]); 053 } 054 } 055 } catch (Exception ex) { 056 } 057 } 058 059 private void skipToStartOfInner(int position) throws IOException, 060 MimeException { 061 final int state = parser.next(); 062 switch (state) { 063 case MimeTokenStream.T_START_MULTIPART: 064 break; 065 case MimeTokenStream.T_START_MESSAGE: 066 break; 067 case MimeTokenStream.T_END_OF_STREAM: 068 throw new PartNotFoundException(position); 069 case MimeTokenStream.T_END_BODYPART: 070 throw new PartNotFoundException(position); 071 default: 072 skipToStartOfInner(position); 073 } 074 } 075 076 private void to(int position) throws IOException, MimeException { 077 try { 078 if (topLevel) { 079 topLevel = false; 080 } else { 081 skipToStartOfInner(position); 082 } 083 for (int count = 0; count < position;) { 084 int state = parser.next(); 085 if (state == MimeTokenStream.T_START_BODYPART) { 086 count++; 087 } else if (state == MimeTokenStream.T_BODY && position == 1) { 088 count++; 089 } else if (state == MimeTokenStream.T_START_MULTIPART) { 090 if (count > 0 && count < position) { 091 ignore(); 092 } 093 } else if (state == MimeTokenStream.T_END_OF_STREAM) { 094 throw new PartNotFoundException(position); 095 } 096 } 097 } catch (IllegalStateException e) { 098 throw new PartNotFoundException(position, e); 099 } 100 } 101 102 private void ignore() throws IOException, MimeException { 103 for (int state = parser.next(); state != MimeTokenStream.T_END_MULTIPART; state = parser 104 .next()) { 105 if (state == MimeTokenStream.T_START_MULTIPART) { 106 ignore(); 107 break; 108 } else if (state == MimeTokenStream.T_END_OF_STREAM) { 109 throw new MimeException("Premature end of stream"); 110 } 111 } 112 } 113 114 public Map<String, String> getMimeHeader() throws IOException, MimeException { 115 Map<String, String> header = new HashMap<String, String>(); 116 for (int state = parser.getState(); state != MimeTokenStream.T_END_HEADER; 117 state = parser.next()) { 118 if (state == MimeTokenStream.T_FIELD) { 119 Field field = parser.getField(); 120 header.put(field.getName(), field.getBody()); 121 } else if (state == MimeTokenStream.T_END_OF_STREAM) { 122 throw new MimeException("Premature end of stream"); 123 } 124 } 125 return header; 126 } 127 128 public Map<String, String> getMessageHeader() throws IOException, MimeException { 129 advanceToMessage(); 130 return getMimeHeader(); 131 } 132 133 private void advanceToMessage() throws IOException, MimeException { 134 for (int state = parser.getState(); state != MimeTokenStream.T_START_MESSAGE; 135 state = parser.next()) { 136 if (state == MimeTokenStream.T_END_OF_STREAM) { 137 throw new MimeException("Premature end of stream"); 138 } 139 } 140 } 141 142 public byte[] getMimeBodyContent() throws IOException, MimeException { 143 parser.setRecursionMode(MimeTokenStream.M_FLAT); 144 for (int state = parser.getState(); state != MimeTokenStream.T_BODY 145 && state != MimeTokenStream.T_START_MULTIPART; 146 state = parser.next()) { 147 if (state == MimeTokenStream.T_END_OF_STREAM) { 148 return EMPTY; 149 } 150 } 151 return IOUtils.toByteArray(parser.getInputStream()); 152 } 153 154 public byte[] getMessageBodyContent() throws IOException, MimeException { 155 advanceToMessageBody(); 156 return getMimeBodyContent(); 157 } 158 159 private void advanceToMessageBody() throws IOException, MimeException { 160 for (int state = parser.getState(); state != MimeTokenStream.T_BODY; 161 state = parser.next()) { 162 if (state == MimeTokenStream.T_END_OF_STREAM) { 163 throw new MimeException("Premature end of stream"); 164 } 165 } 166 } 167 168 public class PartNotFoundException extends MimeException { 169 170 private static final long serialVersionUID = 1L; 171 private final int position; 172 173 public PartNotFoundException(int position) { 174 this(position, null); 175 } 176 177 public PartNotFoundException(int position, Exception e) { 178 super("Part " + position + " not found.", e); 179 this.position = position; 180 } 181 182 public final int getPosition() { 183 return position; 184 } 185 186 } 187 188 }