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