1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.mail.templates.impl;
35
36 import freemarker.template.Template;
37 import info.magnolia.cms.core.Path;
38 import info.magnolia.cms.security.User;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.module.mail.MailTemplate;
41 import info.magnolia.module.mail.templates.MailAttachment;
42
43 import org.apache.commons.httpclient.HttpClient;
44 import org.apache.commons.httpclient.NameValuePair;
45 import org.apache.commons.httpclient.cookie.CookiePolicy;
46 import org.apache.commons.httpclient.methods.GetMethod;
47 import org.apache.commons.httpclient.methods.PostMethod;
48 import org.apache.commons.lang.StringUtils;
49 import org.jdom.Attribute;
50 import org.jdom.Document;
51 import org.jdom.Element;
52 import org.jdom.filter.Filter;
53 import org.jdom.input.SAXBuilder;
54 import org.jdom.output.Format;
55 import org.jdom.output.XMLOutputter;
56 import org.xml.sax.EntityResolver;
57 import org.xml.sax.InputSource;
58 import org.xml.sax.SAXException;
59
60 import java.io.BufferedInputStream;
61 import java.io.File;
62 import java.io.FileOutputStream;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.io.StringReader;
66 import java.io.StringWriter;
67 import java.net.MalformedURLException;
68 import java.net.URL;
69 import java.util.ArrayList;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.Map;
73
74
75
76
77
78
79
80 public class MgnlPageEmail extends FreemarkerEmail {
81
82 public static final String SUFFIX = "?mgnlIntercept=PREVIEW&mgnlPreview=true&mail=draw";
83
84 private HttpClient client;
85
86 private int cid = 0;
87
88 private static final String UTF_8 = "UTF-8";
89
90 private static final String MAGNOLIA = "magnolia";
91
92 private static final String IMG = "img";
93
94 private static final String SRC = "src";
95
96 private static final String CID = "cid:";
97
98 private static final String LINK = "link";
99
100 private static final String HREF = "href";
101
102 private static final String STYLE = "style";
103
104 private static final String REL = "rel";
105
106 private static final String ACTION = "action";
107
108 private static final String LOGIN = "login";
109
110 private static final String URL = "url";
111
112 private static final String MGNL_USER_ID = "mgnlUserId";
113
114 private static final String MGNL_USER_PSWD = "mgnlUserPSWD";
115
116 private static final String SLASH = "/";
117
118 private static final String HTTP = "http://";
119
120 private static final String A_LINK = "a";
121
122 private static final String FORM = "form";
123
124 private static final String BODY = "body";
125
126 private static final String FORM_ACTION = "action";
127
128 private static final String DEFAULT_FORM_ACTION = "#";
129
130
131 public MgnlPageEmail(MailTemplate template) {
132 super(template);
133 }
134
135 public void setBodyFromResourceFile() throws Exception {
136 String resourceFile = this.getTemplate().getTemplateFile();
137
138 if(!StringUtils.contains(resourceFile, "http") ) {
139 String oriurl = MgnlContext.getAggregationState().getOriginalURL();
140 String temp = StringUtils.substring(oriurl, 0, StringUtils.indexOf(oriurl, MgnlContext.getContextPath()));
141 resourceFile = temp + MgnlContext.getContextPath() + resourceFile;
142 }
143
144 URL url = new URL(resourceFile);
145
146 String _content = retrieveContentFromMagnolia(resourceFile);
147 StringReader reader = new StringReader(_content);
148
149
150 String urlBasePath = url.getProtocol() + "://" + url.getHost() + (url.getPort() > -1? ":" + url.getPort() : "" + "/");
151 String tmp = filterImages(urlBasePath, reader, url.toString());
152
153 tmp = StringUtils.remove(tmp, "
");
154 super.setBody(tmp);
155 }
156
157
158 public void setBodyFromTemplate(Template template, Map _map) throws Exception {
159 final StringWriter writer = new StringWriter();
160 template.process(_map, writer);
161 writer.flush();
162 setBody(writer.toString());
163 }
164
165
166 private String filterImages(String urlBasePath, StringReader reader, String pageUrl) throws Exception {
167 log.info("Filtering images");
168 SAXBuilder parser = new SAXBuilder();
169 parser.setEntityResolver(new EntityResolver() {
170
171 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
172 return new InputSource(new java.io.ByteArrayInputStream(new byte[0]));
173 }
174
175 });
176 Document doc = parser.build(reader);
177 ArrayList toremove = new ArrayList();
178 ArrayList toadd = new ArrayList();
179 Element body = null;
180
181 Iterator iter = doc.getDescendants(new ContentFilter());
182 while (iter.hasNext()) {
183 Element elem = (Element) iter.next();
184 String name = elem.getName();
185 if (name.equalsIgnoreCase(IMG)) {
186
187 Attribute att = elem.getAttribute(SRC);
188 if (log.isDebugEnabled()) {
189 log.debug("Found new img:" + att.toString());
190 }
191 String value = att.getValue();
192 this.cid++;
193 att.setValue(CID + (this.cid));
194 String url = getUrl(pageUrl, value);
195
196 if (log.isDebugEnabled()) {
197 log.debug("Url is:" + url);
198 }
199 this.getTemplate().addAttachment(new MailAttachment(getAttachmentFile(url).toURL(), String.valueOf(this.cid)));
200 }
201 else if (name.equalsIgnoreCase(LINK)) {
202
203 Attribute att = elem.getAttribute(HREF);
204 Element el = (Element) elem.clone();
205
206 if (log.isDebugEnabled()) {
207 log.debug("Found new css:" + att.toString());
208 }
209 String url = getUrl(pageUrl, att.getValue());
210 el.setName(STYLE);
211 el.removeAttribute(HREF);
212 el.removeAttribute(REL);
213 GetMethod streamCss = new GetMethod(url);
214 getHttpClient(url).executeMethod(streamCss);
215 String tmp = streamCss.getResponseBodyAsString();
216 tmp = processUrls(tmp, url);
217 el.setText(tmp);
218 toremove.add(elem);
219 toadd.add(el);
220
221 } else if(name.equalsIgnoreCase(A_LINK)) {
222 Attribute att = elem.getAttribute(HREF);
223
224 String url = getUrl(pageUrl, att.getValue());
225 if(!att.getValue().startsWith(DEFAULT_FORM_ACTION)) {
226 att.setValue(url);
227 }
228
229 } else if(name.equalsIgnoreCase(FORM)) {
230 Attribute att = elem.getAttribute(FORM_ACTION);
231 String url = att.getValue();
232 if(att.getValue().equals(DEFAULT_FORM_ACTION)) {
233 url = pageUrl;
234 }
235 att.setValue(url);
236
237 } else if(name.equalsIgnoreCase(BODY)) {
238 body = elem;
239 }
240 }
241
242
243
244 for (int i = 0; i < toremove.size(); i++) {
245 Element elem = (Element) toremove.get(i);
246 Element parent = elem.getParentElement();
247
248 body.addContent(0, (Element) toadd.get(i));
249 parent.removeContent(elem);
250
251 }
252
253
254 Format format = Format.getRawFormat();
255 format.setExpandEmptyElements(true);
256
257
258 XMLOutputter outputter = new XMLOutputter(format);
259 StringWriter writer = new StringWriter();
260 return outputter.outputString(doc);
261 }
262
263
264 private String getUrl(String currentPagePath, String path) {
265 String urlBasePath = currentPagePath.substring(0, currentPagePath.indexOf(MgnlContext.getContextPath()) );
266 if(!StringUtils.contains(path, HTTP) && !StringUtils.contains(path, MgnlContext.getContextPath())) {
267 return currentPagePath.substring(0, currentPagePath.lastIndexOf("/") + 1) + path;
268 } else if(!StringUtils.contains(path, HTTP)) {
269 return urlBasePath + path;
270 }
271 return path;
272 }
273
274 private String processUrls(String responseBodyAsString, String cssPath) throws MalformedURLException, Exception {
275 String tmp = "";
276 Map<String, String> map = new HashMap<String, String>();
277
278 int urlIndex = 0;
279 int closeIndex = 0;
280 int begin = 0;
281 int cid;
282
283
284 while(StringUtils.indexOf(responseBodyAsString, "url(", begin) >= 0) {
285 urlIndex = StringUtils.indexOf(responseBodyAsString, "url(", begin) + "url(".length();
286 closeIndex = StringUtils.indexOf(responseBodyAsString, ")", urlIndex) ;
287
288 String url = StringUtils.substring(responseBodyAsString, urlIndex, closeIndex);
289 url = getUrl(cssPath, url).replaceAll("\"", "");
290 url = "\"" + url + "\"";
291 if(!StringUtils.isEmpty(url) && !map.containsKey(url)) {
292 map.put(url, url);
293
294 } else if (map.containsKey(url)) {
295 url = map.get(url);
296
297 }
298 tmp += StringUtils.substring(responseBodyAsString, begin, urlIndex) + url;
299 begin = closeIndex;
300
301 }
302
303 tmp += StringUtils.substring(responseBodyAsString, closeIndex);
304 return tmp;
305 }
306
307
308 private File getAttachmentFile(String url) throws Exception {
309 log.info("Streaming content of url:" + url + " to a temporary file");
310
311
312 GetMethod redirect = new GetMethod(url);
313 getHttpClient(url).executeMethod(redirect);
314
315 URL _url = new URL(url);
316 String file = _url.getFile();
317
318
319 File tempFile = new File(Path.getTempDirectoryPath()
320 + File.separator
321 + file.substring(file.lastIndexOf(SLASH) + 1));
322
323 if (tempFile.exists() && redirect.getResponseContentLength() == tempFile.length()) {
324 redirect.releaseConnection();
325 return tempFile;
326 }
327
328
329 FileOutputStream out = new FileOutputStream(tempFile);
330 final int BUFFER_SIZE = 1 << 10 << 3;
331 byte[] buffer = new byte[BUFFER_SIZE];
332 int bytesRead;
333 InputStream in = new BufferedInputStream(redirect.getResponseBodyAsStream());
334 while (true) {
335 bytesRead = in.read(buffer);
336 if (bytesRead > -1) {
337 out.write(buffer, 0, bytesRead);
338 }
339 else {
340 break;
341 }
342 }
343
344
345 in.close();
346 out.close();
347 redirect.releaseConnection();
348
349 return tempFile;
350 }
351
352
353 private HttpClient getHttpClient(String baseURL) throws Exception {
354 if (this.client == null) {
355 URL location = new URL(baseURL);
356 User user = MgnlContext.getUser();
357 this.client = getHttpClientForUser(location, user);
358 }
359 return this.client;
360 }
361
362
363 private HttpClient getHttpClientForUser(URL location, User _user) throws IOException {
364 HttpClient _client = new HttpClient();
365 _client.getHostConfiguration().setHost(location.getHost(), location.getPort(), location.getProtocol());
366 _client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
367 String user = _user.getName();
368 String pass = _user.getPassword();
369 log.info("Creating http client for user:" + user);
370
371 PostMethod authpost = new PostMethod(location.getPath());
372 NameValuePair action = new NameValuePair(ACTION, LOGIN);
373 NameValuePair url = new NameValuePair(URL, location.getPath());
374 NameValuePair userid = new NameValuePair(MGNL_USER_ID, user);
375 NameValuePair password = new NameValuePair(MGNL_USER_PSWD, pass);
376 authpost.setRequestBody(new NameValuePair[]{action, url, userid, password});
377 _client.executeMethod(authpost);
378 authpost.releaseConnection();
379 return _client;
380 }
381
382 private String retrieveContentFromMagnolia(String _url) throws Exception {
383 log.info("Retrieving content from magnolia:" + _url);
384 GetMethod redirect = new GetMethod(_url + SUFFIX);
385 getHttpClient(_url).executeMethod(redirect);
386 String response = redirect.getResponseBodyAsString();
387 redirect.releaseConnection();
388 return response;
389 }
390
391 static class ContentFilter implements Filter {
392
393
394 private static final long serialVersionUID = 1L;
395
396 public boolean matches(Object object) {
397 if (object instanceof Element) {
398 Element e = (Element) object;
399 return e.getName().equalsIgnoreCase(LINK) || e.getName().equalsIgnoreCase(IMG)
400 || e.getName().equalsIgnoreCase(A_LINK) || e.getName().equalsIgnoreCase(FORM)
401 || e.getName().equalsIgnoreCase(BODY);
402 }
403
404 return false;
405
406 }
407 }
408 }
409