1 |
|
|
2 |
|
|
3 |
|
|
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 |
|
|
36 |
|
|
37 |
|
|
38 |
|
|
39 |
|
|
40 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
119 |
|
|
120 |
|
|
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; |
129 |
0 |
current = mp.getParent(); |
130 |
0 |
if (current == null) |
131 |
0 |
return null; |
132 |
|
} |
133 |
0 |
return (Message)current; |
134 |
|
} |
135 |
|
|
136 |
|
|
137 |
|
|
138 |
0 |
private static MailDateFormat mailDateFormat = new MailDateFormat(); |
139 |
|
|
140 |
|
|
141 |
|
|
142 |
|
|
143 |
|
|
144 |
|
|
145 |
|
|
146 |
|
|
147 |
|
|
148 |
|
|
149 |
|
|
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 |
|
|
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 |
|
|
176 |
|
|
177 |
|
|
178 |
|
|
179 |
|
|
180 |
|
|
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 |
|
|
209 |
|
|
210 |
|
|
211 |
|
|
212 |
|
|
213 |
|
|
214 |
|
|
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 |
|
|
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 |
|
|
237 |
|
|
238 |
|
|
239 |
|
|
240 |
|
|
241 |
|
|
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 |
|
|
250 |
|
|
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 |
|
|
271 |
|
|
272 |
|
|
273 |
|
|
274 |
|
|
275 |
|
|
276 |
|
|
277 |
|
|
278 |
|
|
279 |
|
|
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 |
|
|
303 |
|
|
304 |
|
|
305 |
|
|
306 |
|
public static void setTextContent(Part p, String s) throws MessagingException { |
307 |
|
|
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 |
|
|
315 |
|
|
316 |
|
|
317 |
|
|
318 |
|
|
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 |
|
|
336 |
|
|
337 |
|
|
338 |
|
|
339 |
|
|
340 |
|
|
341 |
|
|
342 |
|
|
343 |
|
|
344 |
|
|
345 |
|
|
346 |
|
|
347 |
|
|
348 |
|
|
349 |
|
|
350 |
|
|
351 |
|
|
352 |
|
|
353 |
|
|
354 |
|
|
355 |
|
|
356 |
|
|
357 |
|
|
358 |
|
public static String decodeText(String source) { |
359 |
0 |
if (source == null) |
360 |
0 |
return null; |
361 |
|
|
362 |
0 |
if (source.indexOf('\u001b') >= 0) { |
363 |
|
|
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 |
|
|
382 |
|
|
383 |
|
|
384 |
|
|
385 |
|
|
386 |
|
|
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 |
|
} |
416 |
|
parseWord(); |
417 |
|
} |
418 |
|
sweepPooledBytes(); |
419 |
|
buf.append(source.substring(pos)); |
420 |
|
} |
421 |
|
|
422 |
|
|
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 |
|
|
431 |
|
|
432 |
|
|
433 |
|
|
434 |
|
|
435 |
|
|
436 |
|
|
437 |
|
|
438 |
|
|
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); |
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
504 |
|
|
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 |
|
|
548 |
|
|
549 |
|
|
550 |
|
|
551 |
|
|
552 |
|
|
553 |
|
|
554 |
|
|
555 |
|
|
556 |
|
|
557 |
|
|
558 |
|
|
559 |
|
|
560 |
|
|
561 |
|
|
562 |
|
|
563 |
|
|
564 |
|
|
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 |
|
|
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 |
|
|
582 |
0 |
lastLWSPIndex = indexOfLWSP(source, boundaryIndex, true, '('); |
583 |
0 |
startIndex = indexOfNonLWSP(source, lastLWSPIndex, true) + 1; |
584 |
|
|
585 |
|
|
586 |
0 |
startIndex = (endIndex > startIndex) ? endIndex : startIndex; |
587 |
0 |
if (startIndex > endIndex) { |
588 |
|
|
589 |
0 |
buf.append(source.substring(endIndex, startIndex)); |
590 |
|
|
591 |
|
|
592 |
0 |
if (isLWSP(source.charAt(startIndex))) { |
593 |
|
|
594 |
0 |
buf.append("\r\n "); |
595 |
0 |
startIndex++; |
596 |
|
|
597 |
|
} |
598 |
|
} |
599 |
|
|
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 |
|
|
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 |
|
|
630 |
0 |
endIndex++; |
631 |
0 |
buf.append("\r\n "); |
632 |
|
} |
633 |
|
} |
634 |
|
} |
635 |
|
|
636 |
|
|
637 |
|
|
638 |
|
|
639 |
|
|
640 |
|
|
641 |
|
|
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 |
|
|
654 |
|
|
655 |
|
|
656 |
|
|
657 |
|
|
658 |
|
|
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 |
|
|
676 |
|
|
677 |
|
|
678 |
|
|
679 |
|
|
680 |
|
|
681 |
|
|
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 |
|
|
705 |
|
|
706 |
|
|
707 |
|
|
708 |
|
|
709 |
|
|
710 |
|
public static void setFileName(Part part, String filename, String charset, String lang) |
711 |
|
throws MessagingException { |
712 |
|
|
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 |
|
|
732 |
0 |
String mimeString = MimeUtility.encodeWord(filename, charset, "B"); |
733 |
|
|
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 |
|
|
750 |
|
|
751 |
|
|
752 |
|
|
753 |
|
|
754 |
|
|
755 |
|
|
756 |
|
|
757 |
|
|
758 |
|
|
759 |
|
|
760 |
|
|
761 |
|
|
762 |
|
|
763 |
|
|
764 |
|
|
765 |
|
|
766 |
|
|
767 |
|
|
768 |
|
|
769 |
|
|
770 |
|
|
771 |
|
|
772 |
|
|
773 |
|
|
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 |
|
|
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(); |
810 |
0 |
encoding = MimeUtility.mimeCharset(MimeUtility.getDefaultJavaCharset()); |
811 |
|
} |
812 |
0 |
encoded = false; |
813 |
|
|
814 |
0 |
column = name.length() + 7; |
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 |
|
|
821 |
|
|
822 |
|
|
823 |
|
|
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 |
|
|
860 |
|
|
861 |
|
|
862 |
|
|
863 |
|
|
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 |
|
|
877 |
|
public static boolean isAllAscii(String text) { |
878 |
0 |
for (int i = 0; i < text.length(); i++) { |
879 |
0 |
if (text.charAt(i) > 0x7f) { |
880 |
0 |
return false; |
881 |
|
} |
882 |
|
} |
883 |
0 |
return true; |
884 |
|
} |
885 |
|
|
886 |
|
|
887 |
|
|
888 |
|
|
889 |
|
|
890 |
|
|
891 |
|
|
892 |
|
|
893 |
|
|
894 |
|
public static String getFileName(Part part) throws MessagingException { |
895 |
0 |
String[] disposition = part.getHeader("Content-Disposition"); |
896 |
|
|
897 |
|
|
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 |
|
|
919 |
|
|
920 |
|
|
921 |
|
|
922 |
|
|
923 |
|
|
924 |
|
|
925 |
|
|
926 |
|
|
927 |
|
|
928 |
|
|
929 |
|
|
930 |
|
|
931 |
|
|
932 |
|
|
933 |
|
|
934 |
|
|
935 |
|
public static String getParameter(String header, String name) throws ParseException { |
936 |
0 |
if (header == null) |
937 |
0 |
return null; |
938 |
|
|
939 |
|
|
940 |
|
|
941 |
|
|
942 |
|
|
943 |
|
|
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 |
|
|
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 |
|
|
971 |
0 |
return v; |
972 |
|
} |
973 |
0 |
int index = name.length(); |
974 |
0 |
if (!n.startsWith(name) || n.charAt(index) != '*') { |
975 |
|
|
976 |
0 |
continue; |
977 |
|
} |
978 |
|
|
979 |
0 |
int lastIndex = n.length() - 1; |
980 |
0 |
if (n.charAt(lastIndex) == '*') { |
981 |
|
|
982 |
0 |
if (index == lastIndex || n.charAt(index + 1) == '0') { |
983 |
|
|
984 |
0 |
sb.append(decodeRFC2231(v, encoding, true)); |
985 |
|
} else { |
986 |
|
|
987 |
0 |
sb.append(decodeRFC2231(v, encoding, false)); |
988 |
|
} |
989 |
|
} else { |
990 |
0 |
sb.append(v); |
991 |
|
} |
992 |
0 |
if (index == lastIndex) { |
993 |
|
|
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 |
|
|
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 |
|
|
1050 |
|
|
1051 |
0 |
s = new String(s.getBytes("ISO-8859-1"), "JISAutoDetect"); |
1052 |
|
|
1053 |
|
|
1054 |
0 |
return decodeText(s); |
1055 |
0 |
} catch (UnsupportedEncodingException e) {} |
1056 |
0 |
throw new ParseException("Unsupported Encoding"); |
1057 |
|
} |
1058 |
|
|
1059 |
0 |
private MailUtility() {} |
1060 |
|
} |