1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.geronimo.javamail.transport.smtp;
21
22 import java.io.IOException;
23 import java.io.UnsupportedEncodingException;
24 import java.net.Socket;
25 import java.util.ArrayList;
26
27 import javax.mail.Address;
28 import javax.mail.AuthenticationFailedException;
29 import javax.mail.Message;
30 import javax.mail.MessagingException;
31 import javax.mail.Session;
32 import javax.mail.Transport;
33 import javax.mail.URLName;
34 import javax.mail.event.TransportEvent;
35 import javax.mail.internet.InternetAddress;
36 import javax.mail.internet.MimeMessage;
37 import javax.mail.internet.MimeMultipart;
38 import javax.mail.internet.MimePart;
39
40 import org.apache.geronimo.javamail.util.ProtocolProperties;
41 import org.apache.geronimo.javamail.transport.smtp.SMTPConnection.SendStatus;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class SMTPTransport extends Transport {
59
60
61
62
63
64 protected static final String MAIL_SMTP_DSN_NOTIFY = "dsn.notify";
65 protected static final String MAIL_SMTP_SENDPARTIAL = "sendpartial";
66 protected static final String MAIL_SMTP_EXTENSION = "mailextension";
67 protected static final String DEFAULT_MAIL_HOST = "localhost";
68
69 protected static final int DEFAULT_MAIL_SMTP_PORT = 25;
70 protected static final int DEFAULT_MAIL_SMTPS_PORT = 465;
71
72
73
74 protected boolean sslConnection = false;
75
76
77
78 protected ProtocolProperties props;
79
80 protected SMTPConnection connection;
81
82
83 protected SMTPReply lastServerResponse = null;
84
85
86
87
88
89
90
91
92
93
94 public SMTPTransport(Session session, URLName name) {
95 this(session, name, "smtp", DEFAULT_MAIL_SMTP_PORT, false);
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 protected SMTPTransport(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
120 super(session, name);
121
122
123
124 props = new ProtocolProperties(session, protocol, sslConnection, defaultPort);
125
126 connection = new SMTPConnection(props);
127 }
128
129
130
131
132
133
134
135
136
137 public void connect(Socket socket) throws MessagingException {
138 connection.connect(socket);
139 super.connect();
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 protected boolean protocolConnect(String host, int port, String username, String password)
163 throws MessagingException {
164
165 return connection.protocolConnect(host, port, username, password);
166 }
167
168
169
170
171
172
173
174
175
176
177
178 public void sendMessage(Message message, Address[] addresses) throws MessagingException {
179 if (!isConnected()) {
180 throw new IllegalStateException("Not connected");
181 }
182
183 if (message == null) {
184 throw new MessagingException("Null message");
185 }
186
187
188
189 if (!(message instanceof MimeMessage)) {
190 throw new MessagingException("SMTP can only send MimeMessages");
191 }
192
193
194 if (addresses == null || addresses.length == 0) {
195 throw new MessagingException("Null or empty address array");
196 }
197
198 boolean reportSuccess = getReportSuccess();
199
200
201 boolean partialSends = false;
202
203
204 if (message instanceof SMTPMessage) {
205 partialSends = ((SMTPMessage) message).getSendPartial();
206 }
207
208
209
210 if (!partialSends) {
211 partialSends = props.getBooleanProperty(MAIL_SMTP_SENDPARTIAL, false);
212 }
213
214 boolean haveGroup = false;
215
216
217
218 for (int i = 0; i < addresses.length; i++) {
219 if (addresses[i] instanceof InternetAddress) {
220
221
222
223 if (((InternetAddress) addresses[i]).isGroup()) {
224 haveGroup = true;
225 }
226 } else {
227 throw new MessagingException("Illegal InternetAddress " + addresses[i]);
228 }
229 }
230
231
232 if (haveGroup) {
233 addresses = expandGroups(addresses);
234 }
235
236 SendStatus[] stats = new SendStatus[addresses.length];
237
238
239 Address[] sent = null;
240 Address[] unsent = null;
241 Address[] invalid = null;
242
243 try {
244
245
246
247 if (!connection.sendMailFrom(message)) {
248 unsent = addresses;
249 sent = new Address[0];
250 invalid = new Address[0];
251
252 notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
253
254
255 SMTPReply last = connection.getLastServerResponse();
256
257 throw new SMTPSendFailedException("MAIL FROM", last.getCode(), last.getMessage(), null, sent, unsent,
258 invalid);
259 }
260
261
262 String dsn = getDeliveryStatusNotification(message);
263
264
265
266
267 boolean sendFailure = false;
268
269
270
271
272
273
274
275
276
277
278 ArrayList sentAddresses = new ArrayList();
279 ArrayList unsentAddresses = new ArrayList();
280 ArrayList invalidAddresses = new ArrayList();
281
282
283
284 for (int i = 0; i < addresses.length; i++) {
285 InternetAddress target = (InternetAddress) addresses[i];
286
287
288 SendStatus status = connection.sendRcptTo(target, dsn);
289 stats[i] = status;
290
291 switch (status.getStatus()) {
292
293 case SendStatus.SUCCESS:
294 sentAddresses.add(target);
295 break;
296
297
298
299
300 case SendStatus.INVALID_ADDRESS:
301 case SendStatus.GENERAL_ERROR:
302 sendFailure = true;
303 invalidAddresses.add(target);
304 break;
305
306
307 case SendStatus.SEND_FAILURE:
308 sendFailure = true;
309 unsentAddresses.add(target);
310 break;
311 }
312 }
313
314
315
316
317 if (sendFailure) {
318
319
320
321 if (!partialSends || sentAddresses.isEmpty()) {
322
323
324
325
326
327
328 unsentAddresses.addAll(sentAddresses);
329
330
331 sent = new Address[0];
332 unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
333 invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
334
335
336
337 connection.resetConnection();
338
339
340 MessagingException failures = generateExceptionChain(stats, false);
341
342
343 throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
344 }
345 }
346
347 try {
348
349 connection.sendData(message);
350 } catch (MessagingException e) {
351
352
353
354
355
356
357
358
359 unsentAddresses.addAll(sentAddresses);
360
361
362 sent = new Address[0];
363 unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
364 invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
365
366 notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
367
368 throw new SMTPSendFailedException("DATA", 0, "Send failure", e, sent, unsent, invalid);
369 }
370
371
372
373 sent = (Address[]) sentAddresses.toArray(new Address[0]);
374 unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
375 invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
376
377
378
379
380
381
382 if (sendFailure) {
383
384 notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
385
386
387
388
389 MessagingException failures = generateExceptionChain(stats, reportSuccess);
390
391
392 throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
393 }
394
395
396 notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
397
398
399
400
401 if (reportSuccess) {
402
403
404 MessagingException successes = generateExceptionChain(stats, reportSuccess);
405 if (successes != null) {
406 throw successes;
407 }
408 }
409 } catch (SMTPSendFailedException e) {
410
411
412 throw e;
413 } catch (MessagingException e) {
414
415 notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
416 throw e;
417 }
418 }
419
420
421
422
423
424
425
426
427
428
429 protected String getDeliveryStatusNotification(Message message) {
430 String dsn = null;
431
432
433
434
435
436
437 if (message instanceof SMTPMessage) {
438
439 int options = ((SMTPMessage) message).getNotifyOptions();
440
441 switch (options) {
442
443 case 0:
444 break;
445
446 case SMTPMessage.NOTIFY_NEVER:
447 dsn = "NEVER";
448 break;
449
450 case SMTPMessage.NOTIFY_SUCCESS:
451 dsn = "SUCCESS";
452 break;
453
454 case SMTPMessage.NOTIFY_FAILURE:
455 dsn = "FAILURE";
456 break;
457
458 case SMTPMessage.NOTIFY_DELAY:
459 dsn = "DELAY";
460 break;
461
462
463
464
465
466 case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_FAILURE):
467 dsn = "SUCCESS,FAILURE";
468 break;
469
470 case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_DELAY):
471 dsn = "SUCCESS,DELAY";
472 break;
473
474 case (SMTPMessage.NOTIFY_FAILURE + SMTPMessage.NOTIFY_DELAY):
475 dsn = "FAILURE,DELAY";
476 break;
477
478 case (SMTPMessage.NOTIFY_SUCCESS + SMTPMessage.NOTIFY_FAILURE + SMTPMessage.NOTIFY_DELAY):
479 dsn = "SUCCESS,FAILURE,DELAY";
480 break;
481 }
482 }
483
484
485 if (dsn == null) {
486 dsn = props.getProperty(MAIL_SMTP_DSN_NOTIFY);
487 }
488 return dsn;
489 }
490
491
492
493
494
495
496
497
498
499 public void close() throws MessagingException {
500
501 super.close();
502
503 connection.close();
504 }
505
506
507
508
509
510
511
512
513
514
515
516
517
518 protected MessagingException generateExceptionChain(SendStatus[] stats, boolean reportSuccess) {
519 MessagingException current = null;
520
521 for (int i = 0; i < stats.length; i++) {
522 SendStatus status = stats[i];
523
524 if (status != null) {
525 MessagingException nextException = stats[i].getException(reportSuccess);
526
527
528 if (nextException != null) {
529 if (current == null) {
530 current = nextException;
531 } else {
532 current.setNextException(nextException);
533 current = nextException;
534 }
535 }
536 }
537 }
538 return current;
539 }
540
541
542
543
544
545
546
547
548
549
550
551 protected Address[] expandGroups(Address[] addresses) throws MessagingException {
552 ArrayList expandedAddresses = new ArrayList();
553
554
555
556 for (int i = 0; i < addresses.length; i++) {
557 InternetAddress address = (InternetAddress) addresses[i];
558
559 if (!address.isGroup()) {
560 expandedAddresses.add(address);
561 } else {
562
563
564 InternetAddress[] groupAddresses = address.getGroup(true);
565 for (int j = 1; j < groupAddresses.length; j++) {
566 expandedAddresses.add(groupAddresses[j]);
567 }
568 }
569 }
570
571
572 return (Address[]) expandedAddresses.toArray(new Address[0]);
573 }
574
575
576
577
578
579
580
581
582 public String getLocalHost() throws MessagingException {
583 return connection.getLocalHost();
584 }
585
586
587
588
589
590
591
592
593 public void setLocalHost(String localHost) {
594 connection.setLocalHost(localHost);
595 }
596
597
598
599
600
601
602
603 public boolean getReportSuccess() {
604 return connection.getReportSuccess();
605 }
606
607
608
609
610
611
612
613 public void setReportSuccess(boolean report) {
614 connection.setReportSuccess(report);
615 }
616
617
618
619
620
621
622 public boolean getStartTLS() {
623 return connection.getStartTLS();
624 }
625
626
627
628
629
630
631
632 public void setStartTLS(boolean start) {
633 connection.setStartTLS(start);
634 }
635
636
637
638
639
640
641
642
643 public String getSASLRealm() {
644 return connection.getSASLRealm();
645 }
646
647
648
649
650
651
652
653 public void setSASLRealm(String name) {
654 connection.setSASLRealm(name);
655 }
656 }