KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > SmtpStream


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.vfs;
30
31 import com.caucho.server.util.CauchoSystem;
32 import com.caucho.util.Alarm;
33 import com.caucho.util.CharBuffer;
34 import com.caucho.util.Log;
35 import com.caucho.util.NullIterator;
36 import com.caucho.util.QDate;
37 import com.caucho.util.StringCharCursor;
38
39 import java.io.IOException JavaDoc;
40 import java.io.InputStream JavaDoc;
41 import java.net.Socket JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.HashMap JavaDoc;
44 import java.util.Iterator JavaDoc;
45 import java.util.logging.Level JavaDoc;
46 import java.util.logging.Logger JavaDoc;
47
48 /**
49  * Implementation of the SMTP/RFC822 protocol to handle mailto:
50  *
51  * <p>SmtpStream extends MemoryStream so the results will be buffered
52  * until the close(). When the stream is finally closed, it will
53  * send the results to the SMTP server.
54  */

55 class SmtpStream extends MemoryStream {
56   static final Logger JavaDoc log = Log.open(SmtpStream.class);
57
58   // list of recipients
59
ArrayList JavaDoc<MailtoPath.Recipient> _to;
60   // list of copied recipients
61
ArrayList JavaDoc<MailtoPath.Recipient> _cc;
62   // list of blind copied recipients
63
ArrayList JavaDoc<MailtoPath.Recipient> _bcc;
64
65   private String JavaDoc _from;
66   private String JavaDoc _sender;
67   
68   private HashMap JavaDoc<String JavaDoc,Object JavaDoc> _attributes;
69
70   // true if it's already been written
71
boolean _isClosed;
72
73
74   /**
75    * Creates a new SmtpStream
76    *
77    * @param to list of recipients
78    * @param attributes list of attributes set in the path
79    */

80   SmtpStream(ArrayList JavaDoc<MailtoPath.Recipient> to,
81              HashMap JavaDoc<String JavaDoc,Object JavaDoc> attributes)
82     throws IOException JavaDoc
83   {
84     if (to.size() <= 0)
85       throw new IOException JavaDoc("No recipients in mailto");
86     
87     _to = new ArrayList JavaDoc<MailtoPath.Recipient>();
88     for (int i = 0; i < to.size(); i++)
89       _to.add(to.get(i));
90
91     // the 'to' and 'cc' attributes need to be converted.
92
// so set them as if they were set by setAttribute
93
if (attributes != null) {
94       Iterator<String JavaDoc> iter = attributes.keySet().iterator();
95  
96       while (iter.hasNext()) {
97     String JavaDoc key = iter.next();
98  
99     try {
100       setAttribute(key, attributes.get(key));
101     } catch (IOException JavaDoc e) {
102     }
103       }
104     }
105   }
106
107   /**
108    * Return the named attribute
109    */

110   public Object JavaDoc getAttribute(String JavaDoc name)
111     throws IOException JavaDoc
112   {
113     if (_attributes != null)
114       return _attributes.get(name.toLowerCase());
115     else
116       return null;
117   }
118
119   public Iterator<String JavaDoc> getAttributeNames()
120   {
121     if (_attributes != null)
122       return _attributes.keySet().iterator();
123     else
124       return NullIterator.create();
125   }
126
127   /**
128    * Sets an attribute. Some attributes, like "date" and "sender" cannot
129    * be set. Any unknown attribute will be treated as a user RFC822
130    * header.
131    */

132   public void setAttribute(String JavaDoc name, Object JavaDoc value)
133     throws IOException JavaDoc
134   {
135     name = name.toLowerCase();
136     if (name.equals("date") ||
137     name.equals("received") || name.equals("return-path") ||
138     name.equals("message-id"))
139       throw new IOException JavaDoc("cannot set property `" + name + "'");
140
141     if (name.equals("to")) {
142       addTo((String JavaDoc) value);
143       return;
144     }
145     if (name.equals("cc")) {
146       addCc((String JavaDoc) value);
147       return;
148     }
149     
150     if (name.equals("bcc")) {
151       addBcc((String JavaDoc) value);
152       return;
153     }
154     
155     if (name.equals("from")) {
156       _from = (String JavaDoc) value;
157       return;
158     }
159
160     if (name.equals("sender")) {
161       _sender = (String JavaDoc) value;
162       return;
163     }
164
165     if (_attributes == null)
166       _attributes = new HashMap JavaDoc<String JavaDoc,Object JavaDoc>();
167
168     _attributes.put(name, value);
169   }
170
171   /**
172    * Add new recipients
173    *
174    * @param to a list of new recipients
175    */

176   public void addTo(String JavaDoc to)
177     throws IOException JavaDoc
178   {
179     StringCharCursor cursor = new StringCharCursor(to);
180
181     ArrayList JavaDoc<MailtoPath.Recipient> list = MailtoPath.parseAddressList(cursor);
182
183     for (int i = 0; i < list.size(); i++)
184       _to.add(list.get(i));
185   }
186
187   /**
188    * Add new copied recipients
189    *
190    * @param to a list of new recipients
191    */

192   public void addCc(String JavaDoc to)
193     throws IOException JavaDoc
194   {
195     StringCharCursor cursor = new StringCharCursor(to);
196
197     ArrayList JavaDoc<MailtoPath.Recipient> list = MailtoPath.parseAddressList(cursor);
198
199     if (_cc == null)
200       _cc = list;
201     else {
202       for (int i = 0; i < list.size(); i++)
203     _cc.add(list.get(i));
204     }
205   }
206
207   /**
208    * Add new blind copied recipients
209    *
210    * @param to a list of new recipients
211    */

212   public void addBcc(String JavaDoc to)
213     throws IOException JavaDoc
214   {
215     StringCharCursor cursor = new StringCharCursor(to);
216
217     ArrayList JavaDoc<MailtoPath.Recipient> list = MailtoPath.parseAddressList(cursor);
218
219     if (_bcc == null)
220       _bcc = list;
221     else {
222       for (int i = 0; i < list.size(); i++)
223     _bcc.add(list.get(i));
224     }
225   }
226
227   /**
228    * Returns the "from" user
229    */

230   public String JavaDoc getFrom()
231   {
232     if (_from != null)
233       return _from;
234     else
235       return null;
236     
237     // return Registry.getString("/caucho.com/smtp.vfs/sender", null);
238
}
239
240   /**
241    * Returns the sender
242    */

243   public String JavaDoc getSender()
244   {
245     if (_sender != null)
246       return _sender;
247     else
248       return null;
249     
250     // return Registry.getString("/caucho.com/smtp.vfs/sender", null);
251
}
252
253   /**
254    * Reads a response from the SMTP server, returning the status code.
255    *
256    * @param is the input stream to read the response from
257    * @param msg CharBuffer holding the server's response
258    * @return the status code read from the server
259    */

260   private int readResponse(InputStream JavaDoc is, CharBuffer msg)
261     throws IOException JavaDoc
262   {
263     int value;
264
265     do {
266       msg.clear();
267       value = 0;
268       int ch;
269       if ((ch = is.read()) >= '0' && ch <= '9') {
270     for (; ch >= '0' && ch <= '9'; ch = is.read()) {
271       msg.append((char) ch);
272       value = 10 * value + ch - '0';
273     }
274       }
275
276       // Multiline responses, e.g. "200-Foo", indicate there will be a
277
// following line. So the value should be zeroed to force another
278
// iteration. (fixed by Michael Kolfman)
279
if (ch == '-')
280         value = 0;
281
282       for (; ch != '\r' && ch != '\n'; ch = is.read())
283     msg.append((char) ch);
284
285       if (ch == '\r')
286     ch = is.read();
287     } while (value == 0);
288
289     if (log.isLoggable(Level.FINE))
290       log.fine(msg.toString());
291
292     return value;
293   }
294
295   /**
296    * Send the recipient list to the server.
297    *
298    * @param is ReadStream from the server
299    * @param os WriteStream to the server
300    * @param to list of recipients
301    * @param msg CharBuffer to receive the response
302    */

303   void sendRecipients(ReadStream is, WriteStream os,
304                       ArrayList JavaDoc<MailtoPath.Recipient> to, CharBuffer msg)
305     throws IOException JavaDoc
306   {
307     if (to == null)
308       return;
309
310     for (int i = 0; i < to.size(); i++) {
311       MailtoPath.Recipient rcpt = to.get(i);
312
313       os.print("RCPT TO: ");
314       os.print(rcpt.user);
315       if (rcpt.host != null) {
316     os.print("@");
317     os.print(rcpt.host);
318       }
319       os.print("\r\n");
320
321       if (log.isLoggable(Level.FINE))
322         log.fine("RCPT TO: " + rcpt.user + "@" + rcpt.host);
323
324       if (readResponse(is, msg) / 100 != 2)
325     throw new IOException JavaDoc("Expected '221' from SMTP: " + msg);
326     }
327   }
328
329   /**
330    * Writes the message body to the server, escaping as necessary.
331    *
332    * @param os WriteStream to the server
333    */

334   private void writeMessageBody(WriteStream os)
335     throws IOException JavaDoc
336   {
337     int ch;
338
339     ReadStream is = openRead();
340
341     ch = is.read();
342     if (ch < 0) {
343       os.print(".\r\n");
344       return;
345     }
346
347     while (ch >= 0) {
348       if (ch == '\n') {
349     ch = is.read();
350     if (ch == '.') {
351       os.print("\r\n..");
352           ch = is.read();
353         }
354     else if (ch <= 0) {
355       os.print("\r\n.\r\n");
356       return;
357     } else {
358       os.print("\r\n");
359     }
360       } else {
361     os.write(ch);
362         ch = is.read();
363       }
364     }
365
366     os.print("\r\n.\r\n");
367   }
368
369   /**
370    * Writes the message and the RFC822 headers to the SMTP server.
371    */

372   private void writeMessage(WriteStream os)
373     throws IOException JavaDoc
374   {
375     String JavaDoc from = getFrom();
376     os.print("From: ");
377     if (from != null)
378       os.print(from);
379     else {
380       os.print(CauchoSystem.getUserName());
381       os.print("@");
382       os.print(CauchoSystem.getLocalHost());
383     }
384     os.print("\r\n");
385     
386     String JavaDoc date = QDate.formatLocal(Alarm.getCurrentTime(),
387                                     "%a, %d %b %Y %H:%M:%S %z");
388
389     os.print("Date: " + date + "\r\n");
390       
391     os.print("To: ");
392     writeMessageRecipients(os, _to);
393
394     if (_cc != null && _cc.size() > 0) {
395       os.print("Cc: ");
396       writeMessageRecipients(os, _cc);
397     }
398
399     Iterator<String JavaDoc> iter = getAttributeNames();
400     while (iter != null && iter.hasNext()) {
401       String JavaDoc key = iter.next();
402       Object JavaDoc value = getAttribute(key);
403
404       if (value != null) {
405     os.print(key);
406     os.print(": ");
407     os.print(String.valueOf(value));
408     os.print("\r\n");
409       }
410     }
411
412     String JavaDoc sender = getSender();
413     if (_from != sender && ! _from.equals(sender)) {
414       os.print("Sender: ");
415
416       if (sender != null)
417         os.print(sender);
418       else {
419         os.print(CauchoSystem.getUserName());
420         os.print("@");
421         os.print(CauchoSystem.getLocalHost());
422       }
423       os.print("\r\n");
424     }
425
426     os.print("\r\n");
427
428     writeMessageBody(os);
429   }
430
431   /**
432    * Utility to write a list of mail addresses
433    */

434   private void writeMessageRecipients(WriteStream os,
435                                       ArrayList JavaDoc<MailtoPath.Recipient> list)
436     throws IOException JavaDoc
437   {
438     for (int i = 0; i < list.size(); i++) {
439       MailtoPath.Recipient rcpt = list.get(i);
440
441       if (i != 0)
442     os.print(", ");
443       
444       os.print(rcpt.user);
445       if (rcpt.host != null) {
446     os.print("@");
447     os.print(rcpt.host);
448       }
449     }
450
451     os.print("\r\n");
452   }
453
454   /**
455    * On close, send the mail.
456    */

457   public void close()
458     throws IOException JavaDoc
459   {
460     if (_isClosed)
461       return;
462
463     _isClosed = true;
464
465     String JavaDoc host = System.getProperty("mail.smtp.host");
466     if (host == null)
467       host = "127.0.0.1";
468     
469     String JavaDoc portName = System.getProperty("mail.smtp.port");
470     
471     int port = 25;
472     if (portName != null)
473       port = Integer.parseInt(portName);
474
475     Socket JavaDoc sock = new Socket JavaDoc(host, port);
476     CharBuffer msg = new CharBuffer();
477     ReadStream is = null;
478     WriteStream os = null;
479     try {
480       ReadWritePair s = VfsStream.openReadWrite(sock.getInputStream(),
481                         sock.getOutputStream());
482       is = s.getReadStream();
483       os = s.getWriteStream();
484
485       if (readResponse(is, msg) / 100 != 2)
486     throw new IOException JavaDoc("Expected '220' from SMTP");
487
488       os.print("HELO ");
489       os.print(CauchoSystem.getLocalHost());
490       os.print("\r\n");
491       if (readResponse(is, msg) / 100 != 2)
492     throw new IOException JavaDoc("Expected '220' from SMTP");
493
494       os.print("MAIL FROM: ");
495       String JavaDoc sender = getSender();
496       if (sender != null)
497         os.print(sender);
498       else {
499         os.print(CauchoSystem.getUserName());
500         os.print("@");
501         os.print(CauchoSystem.getLocalHost());
502       }
503       os.print("\r\n");
504       if (readResponse(is, msg) / 100 != 2)
505     throw new IOException JavaDoc("Expected '250' from SMTP: " + msg);
506
507       sendRecipients(is, os, _to, msg);
508       if (_cc != null)
509     sendRecipients(is, os, _cc, msg);
510       if (_bcc != null)
511     sendRecipients(is, os, _bcc, msg);
512
513       os.print("DATA\r\n");
514       if (readResponse(is, msg) / 100 != 3)
515     throw new IOException JavaDoc("Expected '354' from SMTP: " + msg);
516
517       writeMessage(os);
518
519       if (readResponse(is, msg) / 100 != 2)
520     throw new IOException JavaDoc("Expected '200' from SMTP: " + msg);
521
522       os.print("QUIT\r\n");
523       if (readResponse(is, msg) / 100 != 2)
524         throw new IOException JavaDoc("Expected '250' from SMTP: " + msg);
525     } finally {
526       try {
527     if (is != null)
528       is.close();
529     if (os != null)
530       os.close();
531       } finally {
532     sock.close();
533       }
534       destroy();
535     }
536   }
537 }
538
Popular Tags