Coverage report

  %line %branch
com.ozacc.mail.fetch.impl.sk_jp.MailUtility
0% 
0% 

 1  
 /*
 2  
  * @(#) $Id: MailUtility.java,v 1.1.2.1 2005/01/18 07:20:59 otsuka Exp $
 3  
  * Copyright (c) 2000-2004 Shin Kinoshita All Rights Reserved.
 4  
  */
 5  
 package com.ozacc.mail.fetch.impl.sk_jp;
 6  
 
 7  
 import java.io.ByteArrayInputStream;
 8  
 import java.io.IOException;
 9  
 import java.io.InputStream;
 10  
 import java.io.UnsupportedEncodingException;
 11  
 import java.util.Date;
 12  
 
 13  
 import javax.activation.DataHandler;
 14  
 import javax.mail.BodyPart;
 15  
 import javax.mail.Message;
 16  
 import javax.mail.MessagingException;
 17  
 import javax.mail.Multipart;
 18  
 import javax.mail.Part;
 19  
 import javax.mail.internet.AddressException;
 20  
 import javax.mail.internet.ContentDisposition;
 21  
 import javax.mail.internet.ContentType;
 22  
 import javax.mail.internet.HeaderTokenizer;
 23  
 import javax.mail.internet.InternetAddress;
 24  
 import javax.mail.internet.MailDateFormat;
 25  
 import javax.mail.internet.MimeUtility;
 26  
 import javax.mail.internet.ParseException;
 27  
 
 28  
 import com.ozacc.mail.fetch.impl.sk_jp.io.CharCodeConverter;
 29  
 import com.ozacc.mail.fetch.impl.sk_jp.io.UnicodeCorrector;
 30  
 import com.ozacc.mail.fetch.impl.sk_jp.text.EntityRefEncoder;
 31  
 import com.ozacc.mail.fetch.impl.sk_jp.util.StringValues;
 32  
 import com.sun.mail.util.BASE64EncoderStream;
 33  
 
 34  
 /**
 35  
  * JavaMail¤Î¥µ¥Ý¡¼¥È¥¯¥é¥¹¤Ç¤¹¡£
 36  
  * <P>
 37  
  * ¼ç¤Ë¥Ø¥Ã¥À¤ËÂФ¹¤?¤µ¤Þ¤¶¤Þ¤Ê²Ã¹©µ¡Ç½¤òÄ󶡤·¤Þ¤¹¡£
 38  
  * </P>
 39  
  * @author Shin
 40  
  * @version $Revision: 1.1.2.1 $ $Date: 2005/01/18 07:20:59 $
 41  
  */
 42  0
 public class MailUtility {
 43  
 
 44  
 	public static String getPersonal(InternetAddress a) {
 45  0
 		if (a.getPersonal() != null)
 46  0
 			return a.getPersonal();
 47  0
 		return a.toString();
 48  
 	}
 49  
 
 50  
 	/** get comma separated E-Mail addresses. */
 51  
 	public static String getMailAddresses(InternetAddress[] addresses) {
 52  0
 		if (addresses == null)
 53  0
 			return null;
 54  0
 		StringValues buf = new StringValues();
 55  0
 		for (int i = 0; i < addresses.length; i++) {
 56  0
 			buf.add(addresses[i].getAddress());
 57  
 		}
 58  0
 		return buf.getString();
 59  
 	}
 60  
 
 61  
 	/** get comma separated personal names. */
 62  
 	public static String getPersonalNames(InternetAddress[] addresses) {
 63  0
 		if (addresses == null)
 64  0
 			return null;
 65  0
 		StringValues buf = new StringValues();
 66  
 		String name;
 67  0
 		for (int i = 0; i < addresses.length; i++) {
 68  0
 			name = decodeText(unfold(addresses[i].getPersonal()));
 69  0
 			if (name == null) {
 70  0
 				name = addresses[i].toString();
 71  
 			}
 72  0
 			buf.add(name);
 73  
 		}
 74  0
 		return buf.getString();
 75  
 	}
 76  
 
 77  
 	public static String getAddressesHTML(InternetAddress[] addresses) {
 78  0
 		if (addresses == null)
 79  0
 			return null;
 80  0
 		StringValues buf = new StringValues();
 81  0
 		StringBuffer href = new StringBuffer();
 82  
 		String name;
 83  0
 		for (int i = 0; i < addresses.length; i++) {
 84  0
 			href.append("<a href=\"mailto:");
 85  0
 			href.append(addresses[i].getAddress());
 86  0
 			href.append("\">");
 87  0
 			name = addresses[i].getPersonal();
 88  0
 			if (name != null) {
 89  0
 				name = decodeText(name);
 90  
 			}
 91  0
 			if (name == null) {
 92  0
 				name = addresses[i].toString();
 93  
 			}
 94  0
 			href.append(EntityRefEncoder.encode(name));
 95  0
 			href.append("</a>");
 96  0
 			buf.add(new String(href));
 97  0
 			href.setLength(0);
 98  
 		}
 99  0
 		return buf.getString();
 100  
 	}
 101  
 
 102  
 	/** get the Content-Transfer-Encoding: header value. */
 103  
 	public static String getTransferEncoding(byte[] b) {
 104  0
 		int nonAscii = 0;
 105  0
 		for (int i = 0; i < b.length; i++) {
 106  0
 			if (b[i] < 0) {
 107  0
 				nonAscii++;
 108  
 			}
 109  
 		}
 110  0
 		if (nonAscii == 0)
 111  0
 			return "7bit";
 112  0
 		if (nonAscii < b.length - nonAscii)
 113  0
 			return "quoted-printable";
 114  0
 		return "base64";
 115  
 	}
 116  
 
 117  
 	/**
 118  
 	 * ¥Ñ¡¼¥È¤òÊÝÍ­¤¹¤?¿ÆMessage¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤·¤Þ¤¹¡£
 119  
 	 * @param part ¥Ñ¡¼¥È
 120  
 	 * @return ¥Ä¥ê¡¼¹½Â¤¤ÎºÇ¾å°Ì¤Ë¤¢¤¿¤?¥á¥Ã¥»¡¼¥¸¥ª¥Ö¥¸¥§¥¯¥È
 121  
 	 */
 122  
 	public static Message getParentMessage(Part part) {
 123  0
 		Part current = part;
 124  
 		Multipart mp;
 125  0
 		while (!(current instanceof Message)) {
 126  0
 			mp = ((BodyPart)current).getParent();
 127  0
 			if (mp == null)
 128  0
 				return null; // Should it throw exception?
 129  0
 			current = mp.getParent();
 130  0
 			if (current == null)
 131  0
 				return null; // Should it throw exception?
 132  
 		}
 133  0
 		return (Message)current;
 134  
 	}
 135  
 
 136  
 	//////////////////////////////////////////////////////////////////////////
 137  
 	// note: JavaMail1.2 later
 138  0
 	private static MailDateFormat mailDateFormat = new MailDateFormat();
 139  
 
 140  
 	/**
 141  
 	 * Date¹½Ê¸¤Î¸úÀä¿"JST"¥¿¥¤¥à¥¾¡¼¥ó¤ÎÊäÀµ¤ò¹Ô¤¤¤Þ¤¹¡£
 142  
 	 * <P>
 143  
 	 * JavaMail¤Ï"JST"¤Èµ­½Ò¤µ¤?¤?¥¿¥¤¥à¥¾¡¼¥ó¤ò²ò¼á¤·¤Þ¤»¤ó¡£ ¤³¤³¤ÏËÜÍ?"+0900"¤Ç¤Ê¤±¤?¤Ð¤Ê¤é¤Ê¤¤¤È¤³¤úÀǤ¹¡£ <BR>
 144  
 	 * »ÅÊ?¤¬¤Ê¤¤¤Î¤Ç" JST"¤¬´Þ¤Þ¤?¤?ʸ»úÎó¤Î¾?¹ç¤Ï"+0900"¤òÊä´°¤·¤Æ
 145  
 	 * MailDateFormat#parse()¤òÄ̤¹¤è¤¦¤Êparse()¤Î¥é¥Ã¥Ñ¤òÍѰդ·¤Þ¤¹¡£
 146  
 	 * </P>
 147  
 	 * <P>
 148  
 	 * ¤³¤Î¼ÂÁõ¤Ï°?»?²óÈòŪ¤Ê¤â¤Î¤Ç¤¢¤ê¡¢´°Á´¤Ê¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£
 149  
 	 * </P>
 150  
 	 */
 151  
 	public static Date parseDate(String rfc822DateString) {
 152  0
 		if (rfc822DateString == null) {
 153  0
 			return null;
 154  
 		}
 155  
 		try {
 156  0
 			if (rfc822DateString.indexOf(" JST") == -1 || rfc822DateString.indexOf('+') >= 0) {
 157  0
 				synchronized (mailDateFormat) {
 158  0
 					return mailDateFormat.parse(rfc822DateString);
 159  
 				}
 160  
 			}
 161  
 			// correct the pseudo header
 162  0
 			StringBuffer buf = new StringBuffer(rfc822DateString.substring(0, rfc822DateString
 163  0
 					.indexOf("JST")));
 164  0
 			buf.append("+0900");
 165  0
 			synchronized (mailDateFormat) {
 166  0
 				return mailDateFormat.parse(new String(buf));
 167  
 			}
 168  0
 		} catch (java.text.ParseException e) {
 169  0
 			return null;
 170  
 		}
 171  
 	}
 172  
 
 173  
 	//////////////////////////////////////////////////////////////////////////
 174  
 	/**
 175  
 	 * Subject:¤Ë"Re: "¤òÉղä·¤Þ¤¹¡£
 176  
 	 * <P>
 177  
 	 * ¤¢¤?ÄøÅÙ´²ÍƤË"Re: "¤Ë¶á¤¤Ê¸»úÎó¤È"[hoge]"¤ò¼è¤?½?¤­¤Þ¤¹¡£ <BR>
 178  
 	 * ¤¿¤À¤·¡¢°Õ¿Þ¤·¤Ê¤¤Éôʬ¤¬¾Ã¤µ¤?¤Æ¤·¤Þ¤¦»ö¤â¤¢¤?ÆÀ¤Þ¤¹¡£ <BR>
 179  
 	 * JavaMail¤Îreply()¤Ç¤Ï"Re: "¤¬¥¨¥ó¥³¡¼¥É¤µ¤?¤Æ¤¤¤¿¾?¹ç¤Ë Àµ¤·¤¯"Re: "¤ò¼è¤?½?¤¤¤Æ¤¯¤?¤Þ¤»¤ó¡£
 180  
 	 * </P>
 181  
 	 */
 182  
 	public static String createReplySubject(String src) {
 183  0
 		if (src == null || src.length() == 0) {
 184  0
 			return "Re: (no subject)";
 185  
 		}
 186  0
 		String work = src;
 187  0
 		if (work.charAt(0) == '[' && work.indexOf(']') > 0) {
 188  0
 			int afterBracket = indexOfNonLWSP(work, work.indexOf(']') + 1, false);
 189  0
 			if (afterBracket < 0) {
 190  0
 				work = "";
 191  
 			} else {
 192  0
 				work = work.substring(afterBracket);
 193  
 			}
 194  
 		}
 195  0
 		if (work.length() > 3 && "Re:".equalsIgnoreCase(work.substring(0, 3))) {
 196  0
 			int afterRe = indexOfNonLWSP(work, 3, false);
 197  0
 			if (afterRe < 0) {
 198  0
 				work = "";
 199  
 			} else {
 200  0
 				work = work.substring(afterRe);
 201  
 			}
 202  
 		}
 203  0
 		return "Re: " + work;
 204  
 	}
 205  
 
 206  
 	//////////////////////////////////////////////////////////////////////////
 207  
 	/**
 208  
 	 * Æ?ÎϤµ¤?¤¿¥¢¥É¥?¥¹¤òInternetAddress·Á¼°¤ËÊÑ´¹¤·¤Þ¤¹¡£
 209  
 	 * <p>
 210  
 	 * "̵̾¤··¯ <abc@example.com>(¥³¥á¥ó¥È)"Åù¤Îʸ»úÎ?(¥¨¥ó¥³¡¼¥É̵¤·)¤?
 211  
 	 * ÅϤµ¤?¤Æ¤â¡¢Àµ¤·¤¯personalʸ»úÎó¤¬ÀßÄꤵ¤?¤?¤è¤¦¤Ë¤·¤Þ¤¹¡£ <br>
 212  
 	 * InternetAddress#parse()¤Ï¥¨¥ó¥³¡¼¥ÉºÑ¤ß¤Îʸ»úÎó¤òÁ°Äó¤Ë¤·¤Æ¤¤¤?¤¿¤á¡¢ ¤³¤Î¥á¥½¥Ã¥É¤ÎÌÜŪ¤Ë¤Ï±è¤¤¤Þ¤»¤ó¡£
 213  
 	 * </p>
 214  
 	 * @param addresses ¥á¥¤¥?¥¢¥É¥?¥¹Ê¸»úÎ?(¥«¥ó¥Þ¶èÀÚ¤?)
 215  
 	 */
 216  
 	public static InternetAddress[] parseAddresses(String addressesString) throws AddressException {
 217  0
 		return parseAddresses(addressesString, true);
 218  
 	}
 219  
 
 220  
 	public static InternetAddress[] parseAddresses(String addressesString, boolean strict)
 221  
 																							throws AddressException {
 222  0
 		if (addressesString == null)
 223  0
 			return null;
 224  
 		try {
 225  0
 			InternetAddress[] addresses = InternetAddress.parse(addressesString, strict);
 226  
 			// correct personals
 227  0
 			for (int i = 0; i < addresses.length; i++) {
 228  0
 				addresses[i].setPersonal(addresses[i].getPersonal(), "ISO-2022-JP");
 229  
 			}
 230  0
 			return addresses;
 231  0
 		} catch (UnsupportedEncodingException e) {
 232  0
 			throw new InternalError(e.toString());
 233  
 		}
 234  
 	}
 235  
 
 236  
 	// InternetAddress.parse(
 237  
 	//          encodeText(addressesString, "ISO-2022-JP", "B"), strict);
 238  
 	// ¤ÇÎɤµ¤½¤¦¤Ê¤â¤Î¤À¤¬¡¢¤³¤?¤Ç¤Ï¡¦¡¦¤¿¤·¤«¤Ê¤ó¤«ÌäÂ꤬¤¢¤Ã¤¿¤Ï¤º¡£
 239  
 	//////////////////////////////////////////////////////////////////////////
 240  
 	/**
 241  
 	 * header value¤Î unfolding ¤ò¹Ô¤¤¤Þ¤¹¡£ ¶õÇò¤ò¸·Ì©¤Ë°·¤¦¤¿¤á¤Ë¤Ï decodeText ¤è¤?Àè¤Ë¸Æ¤Ó½Ð¤¹É¬Íפ¬¤¢¤ê¤Þ¤¹¡£
 242  
 	 */
 243  
 	public static String unfold(String source) {
 244  0
 		if (source == null)
 245  0
 			return null;
 246  0
 		StringBuffer buf = new StringBuffer();
 247  0
 		boolean skip = false;
 248  
 		char c;
 249  
 		// <CRLF>¥·¡¼¥±¥ó¥¹¤òÁ°Äó¤È¤¹¤?¤Ê¤éindexOf()¤Ç½½Ê¬¤Ç¤¹¤¬¡¢
 250  
 		// ǰ¤Î¤¿¤áCR¡¢LF¤¤¤º¤?¤âµöÍÆ¤·¤Þ¤¹¡£
 251  0
 		for (int i = 0; i < source.length(); i++) {
 252  0
 			c = source.charAt(i);
 253  0
 			if (skip) {
 254  0
 				if (isLWSP(c)) {
 255  0
 					continue;
 256  
 				}
 257  0
 				skip = false;
 258  
 			}
 259  0
 			if (c != '\r' && c != '\n') {
 260  0
 				buf.append(c);
 261  
 			} else {
 262  0
 				buf.append(' ');
 263  0
 				skip = true;
 264  
 			}
 265  
 		}
 266  0
 		return new String(buf);
 267  
 	}
 268  
 
 269  
 	/**
 270  
 	 * header value¤Î folding ¤ò¹Ô¤¤¤Þ¤¹¡£
 271  
 	 * <P>
 272  
 	 * white space¤òfoldingÂоݤˤ·¤Þ¤¹¡£ <BR>
 273  
 	 * 76bytes¤òͤ¨¤Ê¤¤white space°ÌÃÖ¤Ë <CRLF>¤òÁÞÆ?¤·¤Þ¤¹¡£
 274  
 	 * </P>
 275  
 	 * <P>
 276  
 	 * Ã?:quote¤ò̵»?¤·¤Þ¤¹¤Î¤Ç¡¢structured field¤Ç¤ÏÉÔÅԹ礬 ȯÀ¸¤¹¤?²ÄǽÀ­¤¬¤¢¤ê¤Þ¤¹¡£
 277  
 	 * </P>
 278  
 	 * @param used ¥Ø¥Ã¥À¤Î':'¤Þ¤Ç¤Îʸ»ú¿ô¡£76 - used¤¬ºÇ½é¤Îfolding¸õÊä·?
 279  
 	 * @return folding¤µ¤?¤¿( <CRLF>SPACE¤¬ÁÞÆ?¤µ¤?¤¿)ʸ»úÎ?
 280  
 	 */
 281  
 	public static String fold(String source, int used) {
 282  0
 		if (source == null)
 283  0
 			return null;
 284  0
 		StringBuffer buf = new StringBuffer();
 285  0
 		String work = source;
 286  
 		int lineBreakIndex;
 287  0
 		while (work.length() > 76) {
 288  0
 			lineBreakIndex = work.lastIndexOf(' ', 76);
 289  0
 			if (lineBreakIndex == -1)
 290  0
 				break;
 291  0
 			buf.append(work.substring(0, lineBreakIndex));
 292  0
 			buf.append("\r\n");
 293  0
 			work = work.substring(lineBreakIndex);
 294  
 		}
 295  0
 		buf.append(work);
 296  0
 		return new String(buf);
 297  
 	}
 298  
 
 299  
 	//////////////////////////////////////////////////////////////////////////
 300  
 	/**
 301  
 	 * ¥Ñ¡¼¥È¤Ë¥Æ¥­¥¹¥È¤ò¥»¥Ã¥È¤·¤Þ¤¹¡£
 302  
 	 * Part#setText() ¤ÎÂå¤?¤ê¤Ë¤³¤Á¤é¤ò»È¤¦¤³¤È¤Ç¡¢
 303  
 	 * "ISO-2022-JP" ¥³¥ó¥Ð¡¼¥¿¤Ç¤Ï¥¨¥ó¥³¡¼¥É¤Ç¤­¤Ê¤¤ CP932 ¤Î
 304  
 	 * ʸ»ú¤ò¥¨¥ó¥³¡¼¥É¤Ç¤­¤Þ¤¹¡£
 305  
 	 */
 306  
 	public static void setTextContent(Part p, String s) throws MessagingException {
 307  
 		//p.setText(content, "ISO-2022-JP");
 308  0
 		p.setDataHandler(new DataHandler(class="keyword">new JISDataSource(s)));
 309  0
 		p.setHeader("Content-Transfer-Encoding", "7bit");
 310  0
 	}
 311  
 
 312  
 	/**
 313  
 	 * Æ?Ëܸ?¤ò´Þ¤à¥Ø¥Ã¥ÀÍѥƥ­¥¹¥È¤òÀ¸À®¤·¤Þ¤¹¡£
 314  
 	 * ÊÑ´¹·?²Ì¤Ï ASCII ¤Ê¤Î¤Ç¡¢¤³¤?¤ò¤½¤Î¤Þ¤Þ setSubject ¤? InternetAddress
 315  
 	 * ¤Î¥Ñ¥é¥á¥¿¤È¤·¤Æ»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤¡£
 316  
 	 * "ISO-2022-JP" ¥³¥ó¥Ð¡¼¥¿¤Ç¤Ï¥¨¥ó¥³¡¼¥É¤Ç¤­¤Ê¤¤ CP932 ¤Î
 317  
 	 * ʸ»ú¤ò¥¨¥ó¥³¡¼¥É¤Ç¤­¤Þ¤¹¡£¤¿¤À¤·¡¢encodeText() ¤È°Û¤Ê¤ê¡¢
 318  
 	 * folding ¤Î°Õ¼±¤ò¤·¤Æ¤ª¤é¤º¡¢¤Þ¤¿ ASCII Éôʬ¤ò½?¤¤¤ÆÊ¬³?
 319  
 	 * ¥¨¥ó¥³¡¼¥É¤ò¹Ô¤¦¤³¤È¤â¤Ç¤­¤Þ¤»¤ó¡£
 320  
 	 */
 321  
 	public static String encodeWordJIS(String s) {
 322  
 		try {
 323  0
 			return "=?ISO-2022-JP?B?"
 324  0
 					+ new String(BASE64EncoderStream.encode(CharCodeConverter
 325  0
 							.sjisToJis(UnicodeCorrector.getInstance("Windows-31J").correct(s)
 326  0
 									.getBytes("Windows-31J")))) + "?=";
 327  0
 		} catch (UnsupportedEncodingException e) {
 328  0
 			throw new RuntimeException("CANT HAPPEN");
 329  
 		}
 330  
 	}
 331  
 
 332  
 	//////////////////////////////////////////////////////////////////////////
 333  
 	/**
 334  
 	 * ¥Ø¥Ã¥ÀÆâ¤Îʸ»úÎó¤ò¥Ç¥³¡¼¥É¤·¤Þ¤¹¡£
 335  
 	 * <p>
 336  
 	 * MimeUtility¤ÎÀ©Ìó¤ò´Ë¤á¤ÆÆ?ËܤÇήÄ̤¹¤?¥¨¥ó¥³¡¼¥É·Á¼°¤ËÂб?¡£
 337  
 	 * ËÜÍè¤Ï¡¢encoded-word¤Ènon-encoded-word¤Î´Ö¤Ë¤Ïlinear-white-space¤¬É¬Í×
 338  
 	 * ¤Ê¤Î¤Ç¤¹¤¬¡¢¶õÇò¤¬Ìµ¤¤¾?½ê¤Ç¥¨¥ó¥³¡¼¥É¤¹¤?¥¿¥³¥á¥¤¥é¤¬Â¿¤¤¤Î¤Ç¡£
 339  
 	 * </p>
 340  
 	 * <p>
 341  
 	 * JIS¥³¡¼¥É¤ò¥¨¥ó¥³¡¼¥É̵¤·¤Çµ­½Ò¤¹¤?¥¿¥³¥á¥¤¥é¤â¤¢¤ê¤Þ¤¹¡£ <br>
 342  
 	 * ¥½¡¼¥¹¤ËESC¤¬´Þ¤Þ¤?¤Æ¤¤¤¿¤éÀ¸JIS¤È¸«¤Ê¤·¤Þ¤¹¡£
 343  
 	 * </p>
 344  
 	 * <p>
 345  
 	 * =?utf-8?Q?¡¦¡¦¡¦JIS¥³¡¼¥É¡¦¡¦?=¤Ê¤ó¤Æ¤µ¤é¤Ë¥¿¥³¤Ê¥á¥¤¥é¤â¡£ <br>
 346  
 	 * »ûÀ·¤Ë¥Ç¥³¡¼¥É¸å¤Ë¤Þ¤ÀESC¤¬»Ä¤Ã¤Æ¤¿¤éISO-2022-JP¤È¸«¤Ê¤¹¤³¤È¤Ë¤·¤Þ¤¹¡£
 347  
 	 * </p>
 348  
 	 * <p>
 349  
 	 * ¤µ¤é¤Ë¡¢multibyte character ¤ÎÁ°¸å¤ÇÊ̤Πencoded-word ¤ËÀڤäƤ·¤Þ¤¦ ¥á¥¤¥é¤â¡Ä¡£ÎÙÀܤ¹¤?
 350  
 	 * encoded-word ¤Î CES ¤¬Æ±¤¸¾?¹ç¤Ï¥Ð¥¤¥ÈÎó¤Î ·?¹ç¤ò¹Ô¤Ã¤Æ¤«¤? CES ¥Ç¥³¡¼¥É¤ò¹Ô¤¦¤è¤¦¤Ë¤·¤¿¡Ä¡£
 351  
 	 * </p>
 352  
 	 * <p>
 353  
 	 * Æ?Ëܸ?¤ËÆÃ²½¤·¤Æ¤Þ¤¹¤Í¤¨¡¦¡¦¡¦¡£
 354  
 	 * </p>
 355  
 	 * @param source encoded text
 356  
 	 * @return decoded text
 357  
 	 */
 358  
 	public static String decodeText(String source) {
 359  0
 		if (source == null)
 360  0
 			return null;
 361  
 		// specially for Japanese
 362  0
 		if (source.indexOf('\u001b') >= 0) {
 363  
 			// ISO-2022-JP
 364  
 			try {
 365  0
 				return new String(source.getBytes("ISO-8859-1"), "ISO-2022-JP");
 366  0
 			} catch (UnsupportedEncodingException e) {
 367  0
 				throw new InternalError();
 368  
 			}
 369  
 		}
 370  0
 		String decodedText = new RFC2047Decoder(source).get();
 371  0
 		if (decodedText.indexOf('\u001b') >= 0) {
 372  
 			try {
 373  0
 				return new String(decodedText.getBytes("ISO-8859-1"), "ISO-2022-JP");
 374  0
 			} catch (UnsupportedEncodingException e) {
 375  0
 				throw new InternalError();
 376  
 			}
 377  
 		}
 378  0
 		return decodedText;
 379  
 	}
 380  
 
 381  
 	// Æ?Ëܸ?¤ò¥Ç¥³¡¼¥É¤¹¤?¾å¤ÇÌäÂ꤬¤¢¤?¤Î¤Ç¡¢encoded-word¤ÎÀÚ¤?½Ð¤·¤Ï¤¹¤Ù¤ÆÆÈ¼«¤Ë
 382  
 	// Netscape¤Ê¤É¤Ï"()."Åù¤Îʸ»ú¤Çencoded-word¤òÀڤäƤ·¤Þ¤¦¤¬¡¢JavaMail¤Ï
 383  
 	// ¤³¤Î¤È¤­encoded-word¤Î½ª¤?¤ê¤òȽÄê¤Ç¤­¤º¡¢°?Éô¤Îʸ»ú¤ò·çÍûÀµ¤»¤Æ¤·¤Þ¤¦¡£
 384  
 	// ¤Þ¤¿¡¢encoded-word ¤òʸ»ú¥Ç¥³¡¼¥É¤¹¤?¤Î¤òÃٱ䤵¤»¡¢ÎÙÀܤ¹¤? encoded-word
 385  
 	// ¤Î CES ¤¬Æ±¤¸¾?¹ç¤Ï¡¢Àè¤Ë TES ¥Ç¥³¡¼¥É¤ò¹Ô¤Ã¤¿¥Ð¥¤¥ÈÎó¤ò·?¹ç¤·¤Æ¤«¤?
 386  
 	// CES ¤Ë½¾¤Ã¤¿¥Ç¥³¡¼¥É¤ò¹Ô¤¦¡£¥Þ¥?¥Á¥Ð¥¤¥Èʸ»ú¤òʬÃǤ¹¤? sender ¤¬¤¤¤?¤«¤é¡£
 387  
 	static class RFC2047Decoder {
 388  
 
 389  
 		private String source;
 390  
 
 391  
 		private String pooledCES;
 392  
 
 393  
 		private byte[] pooledBytes;
 394  
 
 395  
 		private StringBuffer buf;
 396  
 
 397  
 		private int pos = 0;
 398  
 
 399  
 		private int startIndex;
 400  
 
 401  
 		private int endIndex;
 402  
 
 403  
 		public RFC2047Decoder(String source) {
 404  
 			this.source = source;
 405  
 			buf = new StringBuffer(source.length());
 406  
 			parse();
 407  
 		}
 408  
 
 409  
 		private void parse() {
 410  
 			while (hasEncodedWord()) {
 411  
 				String work = source.substring(pos, startIndex);
 412  
 				if (indexOfNonLWSP(work, 0, false) > -1) {
 413  
 					sweepPooledBytes();
 414  
 					buf.append(work);
 415  
 				} // encoded-wordƱ»Î¤Î´Ö¤ÎLWSP¤Ïº?½?
 416  
 				parseWord();
 417  
 			}
 418  
 			sweepPooledBytes();
 419  
 			buf.append(source.substring(pos));
 420  
 		}
 421  
 
 422  
 		// encoded-word ¤¬¤¢¤Ã¤¿¾?¹ç¡¢startIndex/endIndex ¤ò¥»¥Ã¥È¤¹¤?
 423  
 		private boolean hasEncodedWord() {
 424  
 			startIndex = source.indexOf("=?", pos);
 425  
 			if (startIndex == -1)
 426  
 				return false;
 427  
 			endIndex = source.indexOf("?=", startIndex + 2);
 428  
 			if (endIndex == -1)
 429  
 				return false;
 430  
 			// ËÜÍè¤Ï encoded-word Ãæ¤Ë LWSP ¤¬¤¢¤Ã¤Æ¤Ï¤¤¤±¤Ê¤¤¤¬
 431  
 			// encoded-word ¤ÎÅÓÃæ¤Ç folding ¤·¤Æ¤·¤Þ¤¦ sender ¤¬¤¤¤?¤é¤·¤¤
 432  
 			// °Ê²¼¤ò¥³¥á¥ó¥È¤Ë¤¹¤?¤³¤È¤Ç encoded-word ¤Î¸úã§¼±¤Î²ÄǽÀ­¤?
 433  
 			// ½Ð¤Æ¤¯¤?¤¬¡¢¸úã§¼±¤Ë¤Ê¤?³ÎΨ°Ê¾å¤ËÁ°µ­¤Î¤è¤¦¤Ê illegal ¤Ê
 434  
 			// ¥á¥Ã¥»¡¼¥¸¤ÎÊ?¤¬Â¿¤¤¤Î¤¬¼Â¾ð¤Î¤è¤¦¤À¡£
 435  
 			// thx > YOSI
 436  
 			//int i = indexOfLWSP(source, startIndex + 2, false, (char)0);
 437  
 			//if (i >= 0 && i < endIndex)
 438  
 			//    return false;
 439  
 			endIndex += 2;
 440  
 			return true;
 441  
 		}
 442  
 
 443  
 		private void parseWord() {
 444  
 			try {
 445  
 				int s = startIndex + 2;
 446  
 				int e = source.indexOf('?', s);
 447  
 				if (e == endIndex - 2)
 448  
 					throw new RuntimeException();
 449  
 				String ces = source.substring(s, e);
 450  
 				try {
 451  
 					"".getBytes(ces); // FIXME: check whether supported or not
 452  
 				} catch (UnsupportedEncodingException ex) {
 453  
 					ces = "JISAutoDetect";
 454  
 				}
 455  
 				s = e + 1;
 456  
 				e = source.indexOf('?', s);
 457  
 				if (e == endIndex - 2)
 458  
 					throw new RuntimeException();
 459  
 				String tes = source.substring(s, e);
 460  
 				byte[] bytes = decodeByTES(source.substring(e + 1, endIndex - 2), tes);
 461  
 				if (ces.equals(pooledCES)) {
 462  
 					// append bytes
 463  
 					byte[] w = new byte[pooledBytes.length + bytes.length];
 464  
 					System.arraycopy(pooledBytes, 0, w, 0, pooledBytes.length);
 465  
 					System.arraycopy(bytes, 0, w, pooledBytes.length, bytes.length);
 466  
 					pooledBytes = w;
 467  
 				} else {
 468  
 					sweepPooledBytes();
 469  
 					pooledCES = ces;
 470  
 					pooledBytes = bytes;
 471  
 				}
 472  
 			} catch (Exception ex) {
 473  
 				ex.printStackTrace();
 474  
 				// contains RuntimeException
 475  
 				buf.append(source.substring(startIndex, endIndex));
 476  
 			}
 477  
 			pos = endIndex;
 478  
 		}
 479  
 
 480  
 		private void sweepPooledBytes() {
 481  
 			if (pooledBytes == null)
 482  
 				return;
 483  
 			try {
 484  
 				buf.append(new String(pooledBytes, pooledCES));
 485  
 			} catch (UnsupportedEncodingException e) {
 486  
 				throw new InternalError("CANT HAPPEN: Illegal encoding = " + pooledCES);
 487  
 			}
 488  
 			pooledCES = null;
 489  
 			pooledBytes = null;
 490  
 		}
 491  
 
 492  
 		public String get() {
 493  
 			return new String(buf);
 494  
 		}
 495  
 	}
 496  
 
 497  0
 	private static byte[] decodeByTES(String s, String tes) {
 498  
 		// Ä̾?¤¢¤?ÆÀ¤Ê¤¤¤¬¡¢LWSP ¤òµÍ¤á¤?
 499  
 		int i;
 500  0
 		while ((i = indexOfLWSP(s, 0, false, (char)0)) >= 0)
 501  0
 			s = s.substring(0, i) + s.substring(i + 1);
 502  0
 		if (tes.equalsIgnoreCase("B") && s.length() % 4 != 0) {
 503  
 			// BASE64DecoderStream ¤ÏÀµ³Î¤Ë¥Ñ¥Ç¥£¥ó¥°¤µ¤?¤Æ¤¤¤Ê¤¤¤È
 504  
 			// IOException ¤Ë¤Ê¤?¤Î¤Ç¡¢ÌµÍ?¤ä¤?¶ºÀµ¡£
 505  0
 			switch (4 - s.length() % 4) {
 506  
 				case 1:
 507  0
 					s += '=';
 508  0
 					break;
 509  
 				case 2:
 510  0
 					s += "==";
 511  0
 					break;
 512  
 				case 3:
 513  0
 					if (s.charAt(s.length() - 1) != '=')
 514  0
 						s += "===";
 515  
 					else
 516  0
 						s = s.substring(0, s.length() - 1);
 517  
 					break;
 518  
 			}
 519  
 		}
 520  
 		try {
 521  0
 			ByteArrayInputStream bis = new ByteArrayInputStream(com.sun.mail.util.ASCIIUtility
 522  0
 					.getBytes(s));
 523  
 			InputStream is;
 524  0
 			if (tes.equalsIgnoreCase("B"))
 525  0
 				is = new com.sun.mail.util.BASE64DecoderStream(bis);
 526  0
 			else if (tes.equalsIgnoreCase("Q"))
 527  0
 				is = new com.sun.mail.util.QDecoderStream(bis);
 528  
 			else
 529  0
 				throw new UnsupportedEncodingException(tes);
 530  0
 			int count = bis.available();
 531  0
 			byte[] bytes = new byte[count];
 532  0
 			count = is.read(bytes, 0, count);
 533  0
 			if (count != bytes.length) {
 534  0
 				byte[] w = new byte[count];
 535  0
 				System.arraycopy(bytes, 0, w, 0, count);
 536  0
 				bytes = w;
 537  
 			}
 538  0
 			return bytes;
 539  0
 		} catch (IOException e) {
 540  0
 			e.printStackTrace();
 541  0
 			throw new RuntimeException("CANT HAPPEN");
 542  
 		}
 543  
 	}
 544  
 
 545  
 	/**
 546  
 	 * ʸ»úÎó¤ò¥¨¥ó¥³¡¼¥É¤·¤Þ¤¹¡£
 547  
 	 * <p>
 548  
 	 * MimeUtility(¶¯¤¤¤Æ¤ÏMimeMessageÅù¤?)¤Ç¤Ï¡¢1»ú¤Ç¤âÈóASCIIʸ»ú¤¬´Þ¤Þ¤?¤?
 549  
 	 * ¤Èʸ»úÎóÁ´ÂΤò¥¨¥ó¥³¡¼¥É¤·¤Æ¤·¤Þ¤¤¤Þ¤¹¡£
 550  
 	 * <br>
 551  
 	 * ¤³¤Î¥á¥½¥Ã¥É¤Ç¤Ï¶õÇò¤Ç¶èÀÚ¤é¤?¤¿ÈϰϤÀ¤±¤ò¥¨¥ó¥³¡¼¥É¤·¤Þ¤¹¡£ <br>
 552  
 	 * Subject¤Î"Re: "Åù¤¬¥¨¥ó¥³¡¼¥É¤µ¤?¤Æ¤¤¤?¤È¡¢¤³¤Îʸ»úÎó¤ÇIn-Reply-To:
 553  
 	 * References:¤ÎÂå¤?¤ê¤Ë¥¹¥?¥Ã¥É¤ò·ÁÀ®¤·¤è¤¦¤È¤·¤Æ¤â¼ºÇÔ¤¹¤?¤³¤È¤Ë¤Ê¤?
 554  
 	 * ¤¿¤á¡¢¤³¤Á¤é¤Î¥¨¥ó¥³¡¼¥ÉÊ?¼°¤òÍѤ¤¤¿¤¬¤?¿Í¤â¤¤¤?¤«¤â¤·¤?¤Þ¤»¤ó¡¦¡¦¡£
 555  
 	 * </p>
 556  
 	 * <p>
 557  
 	 * Ê?¿Ë¤Ï¡¢ASCIIÉô¤ËÁ°¸å¤Î¶õÇò°?¤Ä¤ò´Þ¤Þ¤»¡¢¤½¤?°Ê³°¤Ï¶õÇò¤â´Þ¤á¤ÆÁ´¤Æ
 558  
 	 * encoded-word¤È¤·¤Þ¤¹¡£()¤ÎÆâ¦¤Ï¶õÇò̵¤·¤Ç¤â¥¨¥ó¥³¡¼¥ÉÂоݤǤ¹¡£
 559  
 	 * </p>
 560  
 	 * @param source text
 561  
 	 * @return encoded text
 562  
 	 */
 563  
 	// "()" ¤Î°·¤¤¤Ë¤³¤À¤?¤ê¤¹¤®¤Æ°Û¾?¤Ë±ø¤¤-_-¡£
 564  
 	// "()"¤Ê¤ó¤«Ìµ»?¤·¤Æ¤Þ¤È¤á¤Æ encode ¤¹¤?¤è¤¦¤Ë¤¹¤?¤Ð¤¹¤Ã¤­¤?¤¹¤?¤±¤É¡Ä¡£
 565  
 	public static String encodeText(String source, String charset, String encoding)
 566  
 																					throws UnsupportedEncodingException {
 567  0
 		if (source == null)
 568  0
 			return null;
 569  
 		int boundaryIndex;
 570  
 		int startIndex;
 571  0
 		int endIndex = 0;
 572  
 		int lastLWSPIndex;
 573  0
 		StringBuffer buf = new StringBuffer();
 574  0
 		while (true) {
 575  
 			// check the end of ASCII part
 576  0
 			boundaryIndex = indexOfNonAscii(source, endIndex);
 577  0
 			if (boundaryIndex == -1) {
 578  0
 				buf.append(source.substring(endIndex));
 579  0
 				return new String(buf);
 580  
 			}
 581  
 			// any LWSP has taken (back track).
 582  0
 			lastLWSPIndex = indexOfLWSP(source, boundaryIndex, true, '(');
 583  0
 			startIndex = indexOfNonLWSP(source, lastLWSPIndex, true) + 1;
 584  
 			// ASCII part ¤Î½ªÎ»°ÌÃ֤ϡ¢¼¡¤Î non ASCII ¤ÈÈæ¤Ù¤Æ
 585  
 			// ºÇ¤? ASCII ʸ»ú¤è¤ê¤Î¶õÇòʸ»ú°ÌÃÖ¤Þ¤¿¤Ï'('¤Î¼¡°ÌÃÖ
 586  0
 			startIndex = (endIndex > startIndex) ? endIndex : startIndex;
 587  0
 			if (startIndex > endIndex) {
 588  
 				// ASCII part
 589  0
 				buf.append(source.substring(endIndex, startIndex));
 590  
 				// JavaMail¤ÏencodeWordÆâ¤Çfolding¤¹¤?¤±¤É¤½¤?¤ÏencodedWord
 591  
 				// ¤ËÂФ·¤Æ¤Î¤ß¡£¥Ø¥Ã¥À¤½¤Î¤â¤Î¤ËÂФ¹¤?folding¤Ï¤·¤Æ¤¯¤?¤Ê¤¤¡£
 592  0
 				if (isLWSP(source.charAt(startIndex))) {
 593  
 					// folding ¤Ë¤è¤? ¶õÇò°?¤Ä¤¬³ÎÊݤµ¤?¤?¤Î¤Ç¥¹¥­¥Ã¥×
 594  0
 					buf.append("\r\n ");
 595  0
 					startIndex++;
 596  
 					// ¤Ê¤ª¡¢'('¤Î¾?¹ç¤Ï¶õÇò¤òÆ?¤?¤Ê¤¤¤Î¤Ç folding ¤·¤Ê¤¤
 597  
 				}
 598  
 			}
 599  
 			// any LWSP has taken.
 600  0
 			endIndex = indexOfNonLWSP(source, boundaryIndex, false);
 601  0
 			while ((endIndex = indexOfLWSP(source, endIndex, false, ')')) != -1) {
 602  0
 				endIndex = indexOfNonLWSP(source, endIndex, false);
 603  0
 				int nextBoundary = indexOfLWSP(source, endIndex, false, (char)0);
 604  0
 				if (nextBoundary == -1) {
 605  0
 					if (indexOfNonAscii(source, endIndex) != -1) {
 606  0
 						endIndex = -1;
 607  0
 						break;
 608  
 					}
 609  
 				} else {
 610  0
 					int nonAscii = indexOfNonAscii(source, endIndex);
 611  0
 					if (nonAscii != -1 && nonAscii < nextBoundary) {
 612  0
 						endIndex = nextBoundary;
 613  0
 						continue;
 614  
 					}
 615  
 				}
 616  0
 				break;
 617  
 			}
 618  0
 			boolean needFolding = false;
 619  0
 			if (endIndex < 0) {
 620  0
 				endIndex = source.length();
 621  0
 			} else if (isLWSP(source.charAt(endIndex - 1))) {
 622  
 				// folding ¤Ë¤è¤? ¶õÇò°?¤Ä¤¬³ÎÊݤµ¤?¤?(ͽÄ?)¤Ê¤Î¤Ç¸º¤é¤¹
 623  0
 				endIndex--;
 624  0
 				needFolding = true;
 625  
 			}
 626  0
 			String encodeTargetText = source.substring(startIndex, endIndex);
 627  0
 			buf.append(MimeUtility.encodeWord(encodeTargetText, charset, encoding));
 628  0
 			if (needFolding) {
 629  
 				// folding ¤Ë¤è¤? ¶õÇò°?¤Ä¤¬³ÎÊݤµ¤?¤?¤Î¤Ç¥¹¥­¥Ã¥×
 630  0
 				endIndex++;
 631  0
 				buf.append("\r\n ");
 632  
 			}
 633  
 		}
 634  
 	}
 635  
 
 636  
 	/**
 637  
 	 * »ØÄ?°ÌÃÖ¤«¤éºÇ½é¤Ë¸«¤Ä¤«¤Ã¤¿ÈóASCIIʸ»ú¤ÎIndex¤òÊÖ¤·¤Þ¤¹¡£ startIndex ¤¬Èϰϳ°¤Î¾?¹ç¤Ï -1 ¤òÊÖ¤·¤Þ¤¹¡£
 638  
 	 * (IndexOutOfBoundsException ¤Ç¤Ï¤Ê¤¤)
 639  
 	 * @param source ¸¡º÷¤¹¤?ʸ»úÎ?
 640  
 	 * @param startIndex ¸¡º÷³«»Ï°ÌÃÖ
 641  
 	 * @return ¸¡½Ð¤·¤¿ÈóASCIIʸ»úIndex¡£¸«¤Ä¤«¤é¤Ê¤±¤?¤Ð-1¡£
 642  
 	 */
 643  
 	public static int indexOfNonAscii(String source, class="keyword">int startIndex) {
 644  0
 		for (int i = startIndex; i < source.length(); i++) {
 645  0
 			if (source.charAt(i) > 0x7f) {
 646  0
 				return i;
 647  
 			}
 648  
 		}
 649  0
 		return -1;
 650  
 	}
 651  
 
 652  
 	/**
 653  
 	 * »ØÄ?°ÌÃÖ¤«¤éºÇ½é¤Ë¸«¤Ä¤«¤Ã¤¿LWSP°Ê³°¤Îʸ»ú¤ÎIndex¤òÊÖ¤·¤Þ¤¹¡£ startIndex ¤¬Èϰϳ°¤Î¾?¹ç¤Ï -1 ¤òÊÖ¤·¤Þ¤¹¡£
 654  
 	 * (IndexOutOfBoundsException ¤Ç¤Ï¤Ê¤¤)
 655  
 	 * @param source ¸¡º÷¤¹¤?ʸ»úÎ?
 656  
 	 * @param startIndex ¸¡º÷³«»Ï°ÌÃÖ
 657  
 	 * @param decrease true¤Ç¸åÊ?¸¡º?
 658  
 	 * @return ¸¡½Ð¤·¤¿ÈóASCIIʸ»úIndex¡£¸«¤Ä¤«¤é¤Ê¤±¤?¤Ð-1¡£
 659  
 	 */
 660  
 	public static int indexOfNonLWSP(String source, class="keyword">int startIndex, boolean decrease) {
 661  
 		char c;
 662  0
 		int inc = 1;
 663  0
 		if (decrease)
 664  0
 			inc = -1;
 665  0
 		for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {
 666  0
 			c = source.charAt(i);
 667  0
 			if (!isLWSP(c)) {
 668  0
 				return i;
 669  
 			}
 670  
 		}
 671  0
 		return -1;
 672  
 	}
 673  
 
 674  
 	/**
 675  
 	 * »ØÄ?°ÌÃÖ¤«¤éºÇ½é¤Ë¸«¤Ä¤«¤Ã¤¿LWSP¤ÎIndex¤òÊÖ¤·¤Þ¤¹¡£ startIndex ¤¬Èϰϳ°¤Î¾?¹ç¤Ï -1 ¤òÊÖ¤·¤Þ¤¹¡£
 676  
 	 * (IndexOutOfBoundsException ¤Ç¤Ï¤Ê¤¤)
 677  
 	 * @param source ¸¡º÷¤¹¤?ʸ»úÎ?
 678  
 	 * @param startIndex ¸¡º÷³«»Ï°ÌÃÖ
 679  
 	 * @param decrease true¤Ç¸åÊ?¸¡º?
 680  
 	 * @param additionalDelimiter LWSP°Ê³°¤Ë¶èÀÚ¤ê¤È¤ß¤Ê¤¹Ê¸»?(1»ú¤Î¤ß)
 681  
 	 * @return ¸¡½Ð¤·¤¿ÈóASCIIʸ»úIndex¡£¸«¤Ä¤«¤é¤Ê¤±¤?¤Ð-1¡£
 682  
 	 */
 683  
 	public static int indexOfLWSP(String source, class="keyword">int startIndex, boolean decrease,
 684  
 									char additionalDelimiter) {
 685  
 		char c;
 686  0
 		int inc = 1;
 687  0
 		if (decrease)
 688  0
 			inc = -1;
 689  0
 		for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {
 690  0
 			c = source.charAt(i);
 691  0
 			if (isLWSP(c) || c == additionalDelimiter) {
 692  0
 				return i;
 693  
 			}
 694  
 		}
 695  0
 		return -1;
 696  
 	}
 697  
 
 698  
 	public static boolean isLWSP(char c) {
 699  0
 		return c == '\r' || c == '\n' || c == ' ' || c == '\t';
 700  
 	}
 701  
 
 702  
 	//////////////////////////////////////////////////////////////////////////
 703  
 	/**
 704  
 	 * This method set Content-Disposition: with RFC2231 encoding. It is
 705  
 	 * required JavaMail1.2.
 706  
 	 */
 707  
 	/**
 708  
 	 * Part#setFileName()¤Î¥Þ¥?¥Á¥Ð¥¤¥ÈÂб?ÈǤǤ¹¡£ JavaMail1.2¤Ç¤Ê¤±¤?¤Ð¥³¥ó¥Ñ¥¤¥?¤Ç¤­¤Þ¤»¤?
 709  
 	 */
 710  
 	public static void setFileName(Part part, String filename, String charset, String lang)
 711  
 																							throws MessagingException {
 712  
 		// Set the Content-Disposition "filename" parameter
 713  
 		ContentDisposition disposition;
 714  0
 		String[] strings = part.getHeader("Content-Disposition");
 715  0
 		if (strings == null || strings.length < 1) {
 716  0
 			disposition = new ContentDisposition(Part.ATTACHMENT);
 717  
 		} else {
 718  0
 			disposition = new ContentDisposition(strings[0]);
 719  0
 			disposition.getParameterList().remove("filename");
 720  
 		}
 721  0
 		part.setHeader("Content-Disposition", disposition.toString()
 722  0
 				+ encodeParameter("filename", filename, charset, lang));
 723  
 		ContentType cType;
 724  0
 		strings = part.getHeader("Content-Type");
 725  0
 		if (strings == null || strings.length < 1) {
 726  0
 			cType = new ContentType(part.getDataHandler().getContentType());
 727  
 		} else {
 728  0
 			cType = new ContentType(strings[0]);
 729  
 		}
 730  
 		try {
 731  
 			// I want to public the MimeUtility#doEncode()!!!
 732  0
 			String mimeString = MimeUtility.encodeWord(filename, charset, "B");
 733  
 			// cut <CRLF>...
 734  0
 			StringBuffer sb = new StringBuffer();
 735  
 			int i;
 736  0
 			while ((i = mimeString.indexOf('\r')) != -1) {
 737  0
 				sb.append(mimeString.substring(0, i));
 738  0
 				mimeString = mimeString.substring(i + 2);
 739  
 			}
 740  0
 			sb.append(mimeString);
 741  0
 			cType.setParameter("name", new String(sb));
 742  0
 		} catch (UnsupportedEncodingException e) {
 743  0
 			throw new MessagingException("Encoding error", e);
 744  
 		}
 745  0
 		part.setHeader("Content-Type", cType.toString());
 746  0
 	}
 747  
 
 748  
 	/**
 749  
 	 * This method encodes the parameter.
 750  
 	 * <P>
 751  
 	 * But most MUA cannot decode the encoded parameters by this method. <BR>
 752  
 	 * I recommend using the "Content-Type:"'s name parameter both.
 753  
 	 * </P>
 754  
 	 */
 755  
 	/**
 756  
 	 * ¥Ø¥Ã¥À¤Î¥Ñ¥é¥á¥¿Éô¤Î¥¨¥ó¥³¡¼¥É¤ò¹Ô¤¤¤Þ¤¹¡£
 757  
 	 * <P>
 758  
 	 * ¸½¾õ¤Ï¼õ¿®¤Ç¤­¤Ê¤¤¤â¤Î¤¬Â¿¤¤¤Î¤Ç¤³¤Î¥á¥½¥Ã¥É¤À¤±¤Ç¤Ï»È¤¨¤Þ¤»¤ó¡£ <BR>
 759  
 	 * Content-Disposition:¤Îfilename¤Î¤ß¤Ë»ÈÍѤ·¡¢¤µ¤é¤Ë Content-Type:¤Îname¤ËMIME
 760  
 	 * encoding¤Ç¤Îµ­½Ò¤â¹Ô¤¦¤Î¤¬ÂÅÅö¤Ç¤·¤ç¤¦¡£ <BR>
 761  
 	 * ¥Ñ¥é¥á¥¿¤Ïɬ¤º¹ÔƬ¤«¤é»Ï¤Þ¤?¤â¤Î¤È¤·¤Þ¤¹¡£ (¥Ø¥Ã¥À¤Î³«»Ï¹Ô¤«¤éÀÞ¤?ÊÖ¤µ¤?¤¿°ÌÃÖ¤ò³«»Ï°ÌÃ֤Ȥ·¤Þ¤¹)
 762  
 	 * </P>
 763  
 	 * <P>
 764  
 	 * folding¤ÎÊ?¿Ë¤Ïascii/non ascii¶­³¦¤Î¤ß¤ò¥Á¥§¥Ã¥¯¤·¤Þ¤¹¡£ ¸½¾õ¤ÏϢ³¤¹¤?ascii/non
 765  
 	 * ascii¤ÎŤµ¤Î¥Á¥§¥Ã¥¯¤Ï¸½¾õ¹Ô¤Ã¤Æ¤¤¤Þ¤»¤ó¡£ (¥¨¥ó¥³¡¼¥É¸å¤Î¥Ð¥¤¥È¿ô¤Ç¥Á¥§¥Ã¥¯¤·¤Ê¤±¤?¤Ð¤Ê¤é¤Ê¤¤¤Î¤Ç¤«¤Ê¤?ÌÌÅÝ)
 766  
 	 * </P>
 767  
 	 * @param name ¥Ñ¥é¥á¥¿Ì¾
 768  
 	 * @param value ¥¨¥ó¥³¡¼¥ÉÂоݤΥѥé¥á¥¿ÃÍ
 769  
 	 * @param encoding ʸ»ú¥¨¥ó¥³¡¼¥Ç¥£¥ó¥°
 770  
 	 * @param lang ¸À¸?»ØÄ?»Ò
 771  
 	 * @return ¥¨¥ó¥³¡¼¥ÉºÑ¤ßʸ»úÎ? ";\r\n name*0*=ISO-8859-2''¡¦¡¦¡¦;\r\n name*1*=¡¦¡¦"
 772  
 	 */
 773  
 	// 1.Á´ÂΤò¥¨¥ó¥³¡¼¥É¤·¤ÆÄ¹¤«¤Ã¤¿¤éȾʬ¤ËÀڤäƥ¨¥ó¥³¡¼¥É¤ò·«¤?ÊÖ¤¹
 774  
 	public static String encodeParameter(String name, String value, String encoding, String lang) {
 775  0
 		StringBuffer result = new StringBuffer();
 776  0
 		StringBuffer encodedPart = new StringBuffer();
 777  0
 		boolean needWriteCES = !isAllAscii(value);
 778  0
 		boolean CESWasWritten = false;
 779  
 		boolean encoded;
 780  0
 		boolean needFolding = false;
 781  0
 		int sequenceNo = 0;
 782  
 		int column;
 783  0
 		while (value.length() > 0) {
 784  
 			// index of boundary of ascii/non ascii
 785  
 			int lastIndex;
 786  0
 			boolean isAscii = value.charAt(0) < 0x80;
 787  0
 			for (lastIndex = 1; lastIndex < value.length(); lastIndex++) {
 788  0
 				if (value.charAt(lastIndex) < 0x80) {
 789  0
 					if (!isAscii)
 790  0
 						break;
 791  
 				} else {
 792  0
 					if (isAscii)
 793  0
 						break;
 794  
 				}
 795  
 			}
 796  0
 			if (lastIndex != value.length())
 797  0
 				needFolding = true;
 798  0
 			RETRY: while (true) {
 799  0
 				encodedPart.setLength(0);
 800  0
 				String target = value.substring(0, lastIndex);
 801  
 				byte[] bytes;
 802  
 				try {
 803  0
 					if (isAscii) {
 804  0
 						bytes = target.getBytes("us-ascii");
 805  
 					} else {
 806  0
 						bytes = target.getBytes(encoding);
 807  
 					}
 808  0
 				} catch (UnsupportedEncodingException e) {
 809  0
 					bytes = target.getBytes(); // use default encoding
 810  0
 					encoding = MimeUtility.mimeCharset(MimeUtility.getDefaultJavaCharset());
 811  
 				}
 812  0
 				encoded = false;
 813  
 				// It is not strict.
 814  0
 				column = name.length() + 7; // size of " " and "*nn*=" and ";"
 815  0
 				for (int i = 0; i < bytes.length; i++) {
 816  0
 					if ((bytes[i] >= '0' && bytes[i] <= '9')
 817  0
 							|| (bytes[i] >= 'A' && bytes[i] <= 'Z')
 818  0
 							|| (bytes[i] >= 'a' && bytes[i] <= 'z') || bytes[i] == '$'
 819  0
 							|| bytes[i] == '.' || bytes[i] == '!') {
 820  
 						// 2001/09/01 ¤·¤«¤?¤Ù¤­Ê¸»ú¤¬É乿²½¤µ¤?¤Ê¤¤ÌäÂ?½¤Àµ
 821  
 						// attribute-char(É乿²½¤·¤Ê¤¯¤Æ¤â¤è¤¤Ê¸»?)¤ÎÄ?µÁ¤Ï
 822  
 						// <any (US-ASCII) CHAR except SPACE, CTLs,
 823  
 						// "*", "'", "%", or tspecials>
 824  
 						// ¤À¤¬¡¢¤ä¤ä¤³¤·¤¤¤Î¤Ç±Ñ¿ô»ú¤Î¤ß¤È¤·¤Æ¤ª¤¯
 825  
 						// "$.!"¤Ï¤ª¤Þ¤±^^¡£¥¨¥ó¥³¡¼¥É»?¤ÏÂ礷¤Æ°Õ¼±¤Ï¤¤¤é¤Ê¤¤
 826  0
 						encodedPart.append((char)bytes[i]);
 827  0
 						column++;
 828  
 					} else {
 829  0
 						encoded = true;
 830  0
 						encodedPart.append('%');
 831  0
 						String hex = Integer.toString(bytes[i] & 0xff, 16);
 832  0
 						if (hex.length() == 1) {
 833  0
 							encodedPart.append('0');
 834  
 						}
 835  0
 						encodedPart.append(hex);
 836  0
 						column += 3;
 837  
 					}
 838  0
 					if (column > 76) {
 839  0
 						needFolding = true;
 840  0
 						lastIndex /= 2;
 841  0
 						continue RETRY;
 842  
 					}
 843  
 				}
 844  0
 				result.append(";\r\n ").append(name);
 845  0
 				if (needFolding) {
 846  0
 					result.append('*').append(sequenceNo);
 847  0
 					sequenceNo++;
 848  
 				}
 849  0
 				if (!CESWasWritten && needWriteCES) {
 850  0
 					result.append("*=");
 851  0
 					CESWasWritten = true;
 852  0
 					result.append(encoding).append('\'');
 853  0
 					if (lang != null)
 854  0
 						result.append(lang);
 855  0
 					result.append('\'');
 856  0
 				} else if (encoded) {
 857  0
 					result.append("*=");
 858  
 					/*
 859  
 					 * ËÜÅö¤Ëcharacter encoding¤ÏÀèÆ¬¥Ñ¡¼¥È¤Ë½ñ¤«¤Ê¤¤¤È¤À¤á¤Ê¤Î¤«? if (encoded) {
 860  
 					 * result.append("*="); if (!CESWasWritten && needWriteCES) {
 861  
 					 * CESWasWritten = true;
 862  
 					 * result.append(encoding).append('\''); if (lang != null)
 863  
 					 * result.append(lang); result.append('\''); }
 864  
 					 */
 865  
 				} else {
 866  0
 					result.append('=');
 867  
 				}
 868  0
 				result.append(new String(encodedPart));
 869  0
 				value = value.substring(lastIndex);
 870  0
 				break;
 871  
 			}
 872  
 		}
 873  0
 		return new String(result);
 874  
 	}
 875  
 
 876  
 	/** check if contains only ascii characters in text. */
 877  
 	public static boolean isAllAscii(String text) {
 878  0
 		for (int i = 0; i < text.length(); i++) {
 879  0
 			if (text.charAt(i) > 0x7f) { // non-ascii
 880  0
 				return false;
 881  
 			}
 882  
 		}
 883  0
 		return true;
 884  
 	}
 885  
 
 886  
 	//////////////////////////////////////////////////////////////////////////
 887  
 	/**
 888  
 	 * This method decode the RFC2231 encoded filename parameter instead of
 889  
 	 * Part#getFileName().
 890  
 	 */
 891  
 	/**
 892  
 	 * Part#getFileName()¤Î¥Þ¥?¥Á¥Ð¥¤¥ÈÂб?ÈǤǤ¹¡£
 893  
 	 */
 894  
 	public static String getFileName(Part part) throws MessagingException {
 895  0
 		String[] disposition = part.getHeader("Content-Disposition");
 896  
 		// A patch by YOSI (Thanx)
 897  
 		// http://www.sk-jp.com/cgibin/treebbs.cgi?kako=1&all=227&s=227
 898  
 		String filename;
 899  0
 		if (disposition == null || disposition.length < 1
 900  0
 				|| (filename = getParameter(disposition[0], "filename")) == null) {
 901  0
 			filename = part.getFileName();
 902  0
 			if (filename != null) {
 903  0
 				return decodeParameterSpciallyJapanese(filename);
 904  
 			}
 905  0
 			return null;
 906  
 		}
 907  0
 		return filename;
 908  
 	}
 909  
 
 910  
 	static class Encoding {
 911  
 
 912  
 		String encoding = "us-ascii";
 913  
 
 914  
 		String lang = "";
 915  
 	}
 916  
 
 917  
 	/**
 918  
 	 * This method decodes the parameter which be encoded (folded) by RFC2231
 919  
 	 * method.
 920  
 	 * <P>
 921  
 	 * The parameter's order should be considered.
 922  
 	 * </P>
 923  
 	 */
 924  
 	/**
 925  
 	 * ¥Ø¥Ã¥À¤Î¥Ñ¥é¥á¥¿Éô¤Î¥Ç¥³¡¼¥É¤ò¹Ô¤¤¤Þ¤¹¡£
 926  
 	 * <P>
 927  
 	 * RFC2231·Á¼°¤Çfolding(ʬ³?)¤µ¤?¤¿¥Ñ¥é¥á¥¿¤ò·?¹ç¤·¡¢¥Ç¥³¡¼¥É¤·¤Þ¤¹¡£
 928  
 	 * ¾°¡¢RFC2231¤Ë¤Ï¥Ñ¥é¥á¥¿¤Î½çÈ֤˰͸¤¹¤?¤Ê¤È½ñ¤«¤?¤Æ¤¤¤Þ¤¹¤¬¡¢ ¤½¤?¤ò¼ÂÁõ¤¹¤?¤ÈÂçÊÑÌÌÅÝ(°?ÅÙʬ³ä¤µ¤?¤¿Á´¤Æ¤Î¥Ñ¡¼¥È¤?
 929  
 	 * ÊÝ»?¤·¤Æ¥½¡¼¥È¤·¤Ê¤±¤?¤Ð¤Ê¤é¤Ê¤¤)¤Ê¤Î¤Ç¡¢ ¥·¡¼¥±¥ó¥¹ÈÖ¹æ¤Ë´Ø·¸¤Ê¤¯(0¤«¤?)½çÈÖ¤Ë Ê¤ó¤Ç¤¤¤?¤â¤Î¤È¤ß¤Ê¤·¤Æ½èÍ?¤¹¤?¤³¤È¤Ë¤·¤Þ¤¹¡£
 930  
 	 * </P>
 931  
 	 * @param header ¥Ø¥Ã¥À¤ÎÃÍÁ´ÂÎ
 932  
 	 * @param name ¼èÆÀ¤·¤¿¤¤¥Ñ¥é¥á¥¿Ì¾
 933  
 	 * @return ¥Ç¥³¡¼¥ÉºÑ¤ßʸ»úÎ? (¥Ñ¥é¥á¥¿¤¬Â¸ºß¤·¤Ê¤¤¾?¹ç¤Ï null)
 934  
 	 */
 935  
 	public static String getParameter(String header, String name) throws ParseException {
 936  0
 		if (header == null)
 937  0
 			return null;
 938  
 		// ËÜÍ褳¤?¤ÏÉÔÍס£Æ?ËܸÇÍ­¤Î¥Ç¥³¡¼¥É½èÍ?¤Ç¤¹¡£
 939  
 		// 2001/07/22 ½ñÀÒÈǤǤÏ"¤¢.txt"¤ÎÀ¸JIS¥Ñ¥é¥á¥¿Ãͤ¬¥Ç¥³¡¼¥É¤Ç¤­¤Ê¤¤
 940  
 		// ¤³¤?¤Ï¡¢ISO-2022-JP¥Ð¥¤¥ÈÎó¤Î¤Þ¤ÞHeaderTokenizer¤Ë¤«¤±¤?¤È¡¢
 941  
 		// "¤¢"¤Î¥Ð¥¤¥È¥·¡¼¥±¥ó¥¹¤Ë´Þ¤Þ¤?¤?0x22¤¬¥À¥Ö¥?¥¯¥©¡¼¥È¤È
 942  
 		// ²ò¼á¤µ¤?¤?¤¿¤á¡£
 943  
 		// JIS/Shift_JIS¤ÎÀ¸¥Ð¥¤¥È¤È»×¤?¤?¤?¤â¤Î¤Î¥Ç¥³¡¼¥É¤òÀè¤Ë¹Ô¤¦»ö¤Ç²óÈ?
 944  0
 		header = decodeParameterSpciallyJapanese(header);
 945  0
 		HeaderTokenizer tokenizer = new HeaderTokenizer(header, ";=\t ", true);
 946  
 		HeaderTokenizer.Token token;
 947  0
 		StringBuffer sb = new StringBuffer();
 948  
 		// It is specified in first encoded-part.
 949  0
 		Encoding encoding = new Encoding();
 950  
 		String n;
 951  
 		String v;
 952  
 		try {
 953  0
 			while (true) {
 954  0
 				token = tokenizer.next();
 955  0
 				if (token.getType() == HeaderTokenizer.Token.EOF)
 956  0
 					break;
 957  0
 				if (token.getType() != ';')
 958  0
 					continue;
 959  0
 				token = tokenizer.next();
 960  0
 				checkType(token);
 961  0
 				n = token.getValue();
 962  0
 				token = tokenizer.next();
 963  0
 				if (token.getType() != '=') {
 964  0
 					throw new ParseException("Illegal token : " + token.getValue());
 965  
 				}
 966  0
 				token = tokenizer.next();
 967  0
 				checkType(token);
 968  0
 				v = token.getValue();
 969  0
 				if (n.equalsIgnoreCase(name)) {
 970  
 					// It is not divided and is not encoded.
 971  0
 					return v;
 972  
 				}
 973  0
 				int index = name.length();
 974  0
 				if (!n.startsWith(name) || n.charAt(index) != '*') {
 975  
 					// another parameter
 976  0
 					continue;
 977  
 				}
 978  
 				// be folded, or be encoded
 979  0
 				int lastIndex = n.length() - 1;
 980  0
 				if (n.charAt(lastIndex) == '*') {
 981  
 					// http://www.sk-jp.com/cgibin/treebbs.cgi?all=399&s=399
 982  0
 					if (index == lastIndex || n.charAt(index + 1) == '0') {
 983  
 						// decode as initial-section
 984  0
 						sb.append(decodeRFC2231(v, encoding, true));
 985  
 					} else {
 986  
 						// decode as other-sections
 987  0
 						sb.append(decodeRFC2231(v, encoding, false));
 988  
 					}
 989  
 				} else {
 990  0
 					sb.append(v);
 991  
 				}
 992  0
 				if (index == lastIndex) {
 993  
 					// not folding
 994  0
 					break;
 995  
 				}
 996  
 			}
 997  0
 			if (sb.length() == 0)
 998  0
 				return null;
 999  0
 			return new String(sb);
 1000  0
 		} catch (UnsupportedEncodingException e) {
 1001  0
 			throw new ParseException(e.toString());
 1002  
 		}
 1003  
 	}
 1004  
 
 1005  
 	private static void checkType(HeaderTokenizer.Token token) throws ParseException {
 1006  0
 		int t = token.getType();
 1007  0
 		if (t != HeaderTokenizer.Token.ATOM && t != HeaderTokenizer.Token.QUOTEDSTRING) {
 1008  0
 			throw new ParseException("Illegal token : " + token.getValue());
 1009  
 		}
 1010  0
 	}
 1011  
 
 1012  
 	// "lang" tag is ignored...
 1013  
 	private static String decodeRFC2231(String s, Encoding encoding, boolean isInitialSection)
 1014  
 																								throws ParseException,
 1015  
 																								UnsupportedEncodingException {
 1016  0
 		StringBuffer sb = new StringBuffer();
 1017  0
 		int i = 0;
 1018  0
 		if (isInitialSection) {
 1019  0
 			int work = s.indexOf('\'');
 1020  0
 			if (work > 0) {
 1021  0
 				encoding.encoding = s.substring(0, work);
 1022  0
 				work++;
 1023  0
 				i = s.indexOf('\'', work);
 1024  0
 				if (i < 0) {
 1025  0
 					throw new ParseException("lang tag area was missing.");
 1026  
 				}
 1027  0
 				encoding.lang = s.substring(work, i);
 1028  0
 				i++;
 1029  
 			}
 1030  
 		}
 1031  
 		try {
 1032  0
 			for (; i < s.length(); i++) {
 1033  0
 				if (s.charAt(i) == '%') {
 1034  0
 					sb.append((char)Integer.parseInt(s.substring(i + 1, i + 3), 16));
 1035  0
 					i += 2;
 1036  0
 					continue;
 1037  
 				}
 1038  0
 				sb.append(s.charAt(i));
 1039  
 			}
 1040  0
 			return new String(class="keyword">new String(sb).getBytes("ISO-8859-1"), encoding.encoding);
 1041  0
 		} catch (IndexOutOfBoundsException e) {
 1042  0
 			throw new ParseException(s + " :: this string were not decoded.");
 1043  
 		}
 1044  
 	}
 1045  
 
 1046  
 	// Æ?Ëܸ?¸?¤±¥Ç¥³¡¼¥É
 1047  
 	private static String decodeParameterSpciallyJapanese(String s) throws ParseException {
 1048  
 		try {
 1049  
 			// decode by character encoding.
 1050  
 			// if string are all ASCII, it is not translated.
 1051  0
 			s = new String(s.getBytes("ISO-8859-1"), "JISAutoDetect");
 1052  
 			// decode by RFC2047.
 1053  
 			// if string doesn't contain encoded-word, it is not translated.
 1054  0
 			return decodeText(s);
 1055  0
 		} catch (UnsupportedEncodingException e) {}
 1056  0
 		throw new ParseException("Unsupported Encoding");
 1057  
 	}
 1058  
 
 1059  0
 	private MailUtility() {}
 1060  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.