View Javadoc

1   /***
2    * 
3    */
4   package net.sf.mailservice.mailhttp;
5   
6   import java.io.ByteArrayOutputStream;
7   import java.io.IOException;
8   import java.io.InputStream;
9   import java.io.OutputStream;
10  import java.io.PrintStream;
11  import java.net.HttpURLConnection;
12  import java.net.MalformedURLException;
13  import java.net.URL;
14  import java.net.URLConnection;
15  import java.net.URLDecoder;
16  import java.util.Enumeration;
17  
18  import javax.mail.Address;
19  import javax.mail.Header;
20  import javax.mail.Message;
21  import javax.mail.MessagingException;
22  import javax.mail.Session;
23  import javax.mail.Transport;
24  import javax.mail.URLName;
25  import javax.mail.event.TransportEvent;
26  import javax.mail.internet.InternetAddress;
27  import javax.mail.internet.MimeMessage;
28  import javax.mail.internet.MimeUtility;
29  
30  /***
31   * An HTTP protocol provider for the JavaMail API that provides access to an
32   * SMTP server via HTTP or HTTPS.
33   *
34   * <table>
35   * <tr>
36   * <td>mail.http.reportsuccess</td>
37   * <td>Throw a HttpTransportSuccessException on success.</td>
38   * </tr>
39   * <tr>
40   * <td>mail.http.url</td>
41   * <td>URL of the restful mail service.</td>
42   * </tr>
43   * </table>
44   *
45   * If a username and password is added to the URL, it will be added to the
46   * request using basic authentication.
47   *
48   * Both http and https URLs are supported.
49   *
50   * @author drewyrr
51   * @version $Id: $
52   */
53  public class HttpTransport extends Transport {
54  
55  	private PrintStream out;
56  	private MimeMessage message;
57  	private Address[] addresses;
58  	private String name;
59  	private boolean reportSuccess;
60  	private URLConnection conn;
61  
62  	/***
63  	 * Create a new transport.
64  	 *
65  	 * @param session
66  	 *            a {@link javax.mail.Session} object.
67  	 * @param urlname
68  	 *            a {@link javax.mail.URLName} object.
69  	 */
70  	public HttpTransport(Session session, URLName urlname) {
71  		super(session, urlname);
72  		String name;
73  		if (urlname != null) {
74  			name = urlname.getProtocol();
75  		} else {
76  			name = "http";
77  		}
78  		this.name = name;
79  
80  		out = session.getDebugOut();
81  
82  		String reportSuccessString = session.getProperty("mail." + name
83  				+ ".reportsuccess");
84  		reportSuccess = reportSuccessString != null
85  				&& reportSuccessString.equalsIgnoreCase("true");
86  
87  	}
88  
89  	/***
90  	 * {@inheritDoc}
91  	 *
92  	 * Send the given message.
93  	 * @see javax.mail.Transport#sendMessage(javax.mail.Message,
94  	 *      javax.mail.Address[])
95  	 */
96  	@Override
97  	public void sendMessage(Message message, Address[] addresses)
98  			throws MessagingException {
99  
100 		String location;
101 
102 		if (!(message instanceof MimeMessage)) {
103 			if (debug) {
104 				out.println("DEBUG HTTP: Can only send RFC822 msgs");
105 			}
106 			throw new MessagingException("HTTP can only send RFC822 messages");
107 		}
108 		for (int i = 0; i < addresses.length; i++) {
109 			if (!(addresses[i] instanceof InternetAddress)) {
110 				throw new MessagingException(addresses[i]
111 						+ " is not an InternetAddress");
112 			}
113 		}
114 
115 		this.message = (MimeMessage) message;
116 		this.addresses = addresses;
117 		Address[] empty = new Address[0];
118 
119 		try {
120 			Enumeration<?> enumeration = message.getAllHeaders();
121 			while (enumeration.hasMoreElements()) {
122 				Header header = (Header) enumeration.nextElement();
123 				if (debug) {
124 					out.println("DEBUG HTTP: Header " + header.getName() + ":"
125 							+ MimeUtility.unfold(header.getValue()));
126 				}
127 				conn.addRequestProperty(header.getName(), MimeUtility
128 						.unfold(header.getValue()));
129 			}
130 
131 			/*
132 			 * suppress the headers by not using writeTo.
133 			 */
134 			InputStream is = this.message.getInputStream();
135 			OutputStream os = conn.getOutputStream();
136 			int c;
137 			while ((c = is.read()) != -1) {
138 				os.write(c);
139 			}
140 
141 			if (debug) {
142 				out.print("DEBUG HTTP: Response: ");
143 			}
144 			is = conn.getInputStream();
145 			while ((c = is.read()) != -1) {
146 				if (debug) {
147 					out.write(c);
148 				}
149 			}
150 			is.close();
151 			if (debug) {
152 				out.println();
153 			}
154 
155 			location = conn.getHeaderField("Location");
156 			this.message.addHeader("Location", location);
157 
158 			notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED,
159 					this.addresses, empty, empty, this.message);
160 		} catch (MessagingException mex) {
161 			if (debug)
162 				mex.printStackTrace(out);
163 			notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
164 					empty, this.addresses, empty, this.message);
165 
166 			throw mex;
167 		} catch (IOException ioe) {
168 			int response = 0;
169 			if (debug)
170 				ioe.printStackTrace(out);
171 			try {
172 				if (conn instanceof HttpURLConnection) {
173 					response = ((HttpURLConnection) conn).getResponseCode();
174 					InputStream es = ((HttpURLConnection) conn)
175 							.getErrorStream();
176 					int c;
177 					while ((c = es.read()) != -1) {
178 						if (debug) {
179 							out.write(c);
180 						}
181 					}
182 					es.close();
183 					if (debug) {
184 						out.println();
185 					}
186 				}
187 			} catch (IOException e) {
188 				/* we're doomed at this point */
189 			}
190 			notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
191 					empty, this.addresses, empty, this.message);
192 
193 			throw new MessagingException(
194 					"IOException while sending message, with HTTP response code "
195 							+ response, ioe);
196 		} finally {
197 			/* no reason to keep this data around */
198 			this.addresses = null;
199 			this.message = null;
200 		}
201 
202 		if (reportSuccess) {
203 			throw new HttpTransportSuccessException(location);
204 		}
205 
206 	}
207 
208 	/***
209 	 * {@inheritDoc}
210 	 *
211 	 * Set up the connection to the mail service.
212 	 * @see javax.mail.Service#protocolConnect(java.lang.String, int,
213 	 *      java.lang.String, java.lang.String)
214 	 */
215 	@Override
216 	protected boolean protocolConnect(String host, int port, String user,
217 			String password) throws MessagingException {
218 
219 		URL url;
220 		String p = session.getProperty("mail." + name + ".url");
221 		try {
222 			if (p != null) {
223 				url = new URL(p);
224 			} else {
225 				url = new URL(name + "://localhost/");
226 			}
227 		} catch (MalformedURLException e) {
228 			throw new MessagingException(e.getMessage());
229 		}
230 
231 		try {
232 			conn = url.openConnection();
233 			conn.setDoOutput(true);
234 			if (conn instanceof HttpURLConnection) {
235 				HttpURLConnection httpUrlConnection = (HttpURLConnection) conn;
236 				httpUrlConnection.setInstanceFollowRedirects(false);
237 			}
238 
239 			/*
240 			 * Add basic auth from provided user and password, otherwise from
241 			 * the URL, manually
242 			 */
243 			String authority = url.getAuthority();
244 			int offset;
245 			if (user != null && password != null) {
246 				String userpassword = user.concat(":").concat(password);
247 				ByteArrayOutputStream baos = new ByteArrayOutputStream();
248 				OutputStream stream = MimeUtility.encode(baos, "base64");
249 				stream.write(userpassword.getBytes());
250 
251 				conn.setRequestProperty("Authorization", "Basic "
252 						+ baos.toString("UTF-8"));
253 			} else if ((offset = authority.indexOf('@')) != -1) {
254 				String userpassword = URLDecoder.decode(authority.substring(0,
255 						offset), "UTF-8");
256 				ByteArrayOutputStream baos = new ByteArrayOutputStream();
257 				OutputStream stream = MimeUtility.encode(baos, "base64");
258 				stream.write(userpassword.getBytes());
259 
260 				conn.setRequestProperty("Authorization", "Basic "
261 						+ baos.toString("UTF-8"));
262 			}
263 
264 		} catch (MalformedURLException e) {
265 			throw new MessagingException(e.getLocalizedMessage());
266 		} catch (IOException e) {
267 			throw new MessagingException(e.getLocalizedMessage());
268 		}
269 
270 		return true;
271 	}
272 
273 }