View Javadoc

1   /*
2    * @(#) $Id: CorrectedContentTypeDataSourceUTF7Support.java,v 1.1.2.1 2004/09/29 00:57:59 otsuka Exp $
3    * $Revision: 1.1.2.1 $
4    * Copyright (c) 2000 Shin Kinoshita All Rights Reserved.
5    */
6   package com.ozacc.mail.fetch.impl.sk_jp;
7   
8   import java.io.ByteArrayInputStream;
9   import java.io.ByteArrayOutputStream;
10  import java.io.IOException;
11  import java.io.InputStream;
12  
13  import javax.activation.DataSource;
14  import javax.mail.MessageAware;
15  import javax.mail.MessagingException;
16  import javax.mail.Part;
17  import javax.mail.internet.ContentType;
18  import javax.mail.internet.MimeBodyPart;
19  import javax.mail.internet.MimeMessage;
20  import javax.mail.internet.ParseException;
21  
22  import com.ozacc.mail.fetch.impl.sk_jp.io.ByteToCharUTF7;
23  
24  /***
25   * Content-Type:の不適合をISO-2022-JPに補正します。
26   * さらにcharset=UTF-7の場合にUTF-16のストリームに変換してgetContent()を
27   * 無理やり成功させます。<BR>
28   * また、未知のTES(Content-Transfer-Encoding:)だった場合に、"7bit"
29   * と見なしてボディを取得します。
30   * 使用方法は<PRE>
31   * Object o = new DataHandler(
32   *               new CorrectedContentTypeDataSourceUTF7Support(part, charset)
33   *            ).getContent();
34   * </PRE><P>のようになります。</P><P>
35   * スレッドセーフではありませんので利用者側で排他制御を行ってください。
36   * </P>
37   * @author Shin
38   * @version $Revision: 1.1.2.1 $ $Date: 2004/09/29 00:57:59 $
39   */
40  class CorrectedContentTypeDataSourceUTF7Support extends CorrectedContentTypeDataSource {
41  
42  	private boolean utf7 = false;
43  
44  	public CorrectedContentTypeDataSourceUTF7Support() {}
45  
46  	public CorrectedContentTypeDataSourceUTF7Support(DataSource dataSource, String defaultCharset) {
47  		super(dataSource, defaultCharset);
48  	}
49  
50  	public CorrectedContentTypeDataSourceUTF7Support(Part part, String defaultCharset)
51  																						throws MessagingException {
52  		super(part, defaultCharset);
53  	}
54  
55  	public void setDataSource(DataSource newSource) {
56  		super.setDataSource(newSource);
57  		utf7 = false;
58  	}
59  
60  	public void setDefaultCharset(String defaultCharset) {
61  		super.setDefaultCharset(defaultCharset);
62  		utf7 = false;
63  	}
64  
65  	public String getContentType() {
66  		try {
67  			ContentType contentType = new ContentType(super.getContentType());
68  			String specifiedCharset = contentType.getParameter("charset");
69  			if ("UTF-7".equalsIgnoreCase(specifiedCharset)) {
70  				// UTF-7コンバータが存在しない為、
71  				// 独自フィルタストリームを用いる。
72  				contentType.setParameter("charset", "UTF-16");
73  				utf7 = true;
74  			}
75  			return contentType.toString();
76  		} catch (ParseException e) {
77  			throw new InternalError();
78  		}
79  	}
80  
81  	public InputStream getInputStream() throws IOException {
82  		InputStream in = null;
83  		if (isInvalidEncodingAsMultipart()) {
84  			// multipart/*でありながら、不正なTransfer-Encodingだった場合
85  			// 2001/09/01 JPhone(SH07)の送信する画像付きメイルが、
86  			// Content-Type: multipart/mixed
87  			// Content-Transfer-Encoding: base64
88  			// 等というメッセージを送る場合があり、JavaMailが
89  			// これをデコードできない問題を回避。
90  			// multipart/*の場合のContent-Transfer-Encodingは、
91  			// "7bit""8bit""binary"に限られる。
92  			// それ以外の場合は生ストリームを返すようにしておく。
93  			in = getRawInputStream();
94  		}
95  		if (in == null) {
96  			try {
97  				in = super.getInputStream();
98  			} catch (IOException e) {
99  				// ここでのIOExceptionはエンコーディング不良の可能性が高い。
100 				// 生InputStreamを得てリトライ
101 				in = getRawInputStream();
102 				if (in == null)
103 					throw e;
104 			}
105 		}
106 		if (!utf7) {
107 			return in;
108 		}
109 		ByteArrayOutputStream out = new ByteArrayOutputStream();
110 		int c;
111 
112 		while ((c = in.read()) != -1) {
113 			out.write(c);
114 		}
115 
116 		ByteToCharUTF7 btc = new ByteToCharUTF7();
117 		byte[] bytes = out.toByteArray();
118 		char[] chars = new char[bytes.length];
119 
120 		// Bug fixed. Thanx to MOHI.
121 		// http://www.sk-jp.com/cgi-bin/treebbs.cgi?all=1220&s=1220
122 		int len = btc.convert(bytes, 0, bytes.length, chars, 0, chars.length);
123 		char[] w = new char[len];
124 		System.arraycopy(chars, 0, w, 0, len);
125 		String string = new String(w);
126 		return new ByteArrayInputStream(string.getBytes("UTF-16"));
127 	}
128 
129 	// Transfer-Encodingにしたがったデコードを行う前のストリームを得ます。
130 	// sourceがMessageAwareでない場合はnullが返されます。
131 	private InputStream getRawInputStream() throws IOException {
132 		if (!(source instanceof MessageAware)) {
133 			return null;
134 		}
135 		Part part = ((MessageAware)source).getMessageContext().getPart();
136 		try {
137 			if (part instanceof MimeMessage) {
138 				return ((MimeMessage)part).getRawInputStream();
139 			} else if (part instanceof MimeBodyPart) {
140 				return ((MimeBodyPart)part).getRawInputStream();
141 			} else {
142 				return null;
143 			}
144 		} catch (MessagingException mex) {
145 			throw new IOException(mex.toString());
146 		}
147 	}
148 
149 	// 不正なContent-Transfer-Encodingの場合にtrueを返します。
150 	private boolean isInvalidEncodingAsMultipart() {
151 		try {
152 			if (!new ContentType(getContentType()).match("multipart/*")) {
153 				return false;
154 			}
155 			if (!(source instanceof MessageAware)) {
156 				return false;
157 			}
158 			Part part = ((MessageAware)source).getMessageContext().getPart();
159 			String encoding = ((javax.mail.internet.MimePart)part).getEncoding();
160 			if ("7bit".equalsIgnoreCase(encoding) || "8bit".equalsIgnoreCase(encoding)
161 					|| "binary".equalsIgnoreCase(encoding)) {
162 				return false;
163 			}
164 		} catch (Exception e) {
165 			// この場合も不正だ、と。
166 		}
167 		return true;
168 	}
169 
170 }