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