/********************************************************************************* * The contents of this file are subject to the Common Public Attribution * License Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.openemm.org/cpal1.html. The License is based on the Mozilla * Public License Version 1.1 but Sections 14 and 15 have been added to cover * use of software over a computer network and provide for limited attribution * for the Original Developer. In addition, Exhibit A has been modified to be * consistent with Exhibit B. * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is OpenEMM. * The Original Developer is the Initial Developer. * The Initial Developer of the Original Code is AGNITAS AG. All portions of * the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights * Reserved. * * Contributor(s): AGNITAS AG. ********************************************************************************/ package org.agnitas.preview; import java.util.Arrays; import java.util.ArrayList; import java.util.Hashtable; import java.util.Enumeration; import java.util.regex.Pattern; import java.util.regex.Matcher; public class PageImpl implements Page { private Hashtable <String, String> content; private Hashtable <String, String[]> header; private StringBuffer error; private Hashtable <String, Object> compat; public PageImpl () { content = new Hashtable <String, String> (); header = null; error = null; compat = null; } public void addContent (String key, String value) { content.put (key, value); } public void setError (String msg) { if (error == null) { error = new StringBuffer (); } error.append (msg); error.append ('\n'); } public String getError () { return error != null ? error.toString () : null; } public Hashtable <String, Object> compatibilityRepresentation () { if (compat == null) { compat = new Hashtable <String, Object> (); if (content != null) { for (Enumeration e = content.keys (); e.hasMoreElements (); ) { String key = (String) e.nextElement (); compat.put (key, content.get (key)); } } if (error != null) { compat.put (Preview.ID_ERROR, error.toString ()); } } return compat; } /** Pattern to find entities to escape */ static private Pattern textReplace = Pattern.compile ("[&<>'\"]"); /** Values to escape found entities */ static private Hashtable <String, String> textReplacement = new Hashtable <String, String> (); static { textReplacement.put ("&", "&"); textReplacement.put ("<", "<"); textReplacement.put (">", ">"); textReplacement.put ("'", "'"); textReplacement.put ("\"", """); } /** escapeEntities * This method escapes the HTML entities to be displayed * in a HTML context * @param s the input string * @return null, if input string had been null, * the escaped version of s otherwise */ private String escapeEntities (String s) { if (s != null) { int slen = s.length (); Matcher m = textReplace.matcher (s); StringBuffer buf = new StringBuffer (slen + 128); int pos = 0; while (m.find (pos)) { int next = m.start (); String ch = m.group (); if (pos < next) buf.append (s.substring (pos, next)); buf.append (textReplacement.get (ch)); pos = m.end (); } if (pos != 0) { if (pos < slen) buf.append (s.substring (pos)); s = buf.toString (); } } return s; } /** encode * Encodes a string to a byte stream using the given character set, * if escape is true, HTML entities are escaped prior to encoding * @param s the string to encode * @param charset the character set to convert the string to * @param escape if HTML entities should be escaped * @return the coded string as a byte stream */ private byte[] encode (String s, String charset, boolean escape) { if (escape && (s != null)) { s = "<pre>\n" + escapeEntities (s) + "</pre>\n"; } try { return s == null ? null : s.getBytes (charset); } catch (java.io.UnsupportedEncodingException e) { return null; } } /** get * a null input save conversion variant * @param s the input string * @param escape to escape HTML entities * @return the converted string */ private String convert (String s, boolean escape) { if (escape && (s != null)) { return escapeEntities (s); } return s; } protected String strip (String html) { return null; } /** * Get header-, text- or HTML-part from hashtable created by * createPreview as byte stream */ public byte[] getPartByID (String id, String charset, boolean escape) { return encode (content.get (id), charset, escape); } public byte[] getHeaderPart (String charset, boolean escape) { return getPartByID (Preview.ID_HEAD, charset, escape); } public byte[] getHeaderPart (String charset) { return getHeaderPart (charset, false); } public byte[] getTextPart (String charset, boolean escape) { return getPartByID (Preview.ID_TEXT, charset, escape); } public byte[] getTextPart (String charset) { return getTextPart (charset, false); } public byte[] getHTMLPart (String charset, boolean escape) { return getPartByID (Preview.ID_HTML, charset, escape); } public byte[] getHTMLPart (String charset) { return getHTMLPart (charset, false); } public byte[] getMHTMLPart (String charset, boolean escape) { return getPartByID (Preview.ID_MHTML, charset, escape); } public byte[] getMHTMLPart (String charset) { return getMHTMLPart (charset, false); } /** * Get header-, text- or HTML-part as strings */ public String getByID (String id, boolean escape) { return convert (content.get (id), escape); } public String getStrippedByID (String id, boolean escape) { return convert (strip (content.get (id)), escape); } public String getHeader (boolean escape) { return getByID (Preview.ID_HEAD, escape); } public String getHeader () { return getHeader (false); } public String getText (boolean escape) { return getByID (Preview.ID_TEXT, escape); } public String getText () { return getText (false); } public String getHTML (boolean escape) { return getByID (Preview.ID_HTML, escape); } public String getHTML () { return getHTML (false); } public String getStrippedHTML (boolean escape) { return getStrippedByID (Preview.ID_HTML, escape); } public String getStrippedHTML () { return getStrippedHTML (false); } public String getMHTML (boolean escape) { return getByID (Preview.ID_MHTML, escape); } public String getMHTML () { return getMHTML (false); } public String getStrippedMHTML (boolean escape) { return getStrippedByID (Preview.ID_MHTML, escape); } public String getStrippedMHTML () { return getStrippedMHTML (false); } /** * Get attachment names and content */ private boolean isID (String name) { return name.startsWith ("__") && name.endsWith ("__"); } private String[] getList (boolean asAttachemnts) { ArrayList <String> collect = new ArrayList <String> (); for (String name : content.keySet ()) { if (isID (name) != asAttachemnts) { collect.add (name); } } return collect.toArray (new String[collect.size ()]); } public String[] getIDs () { return getList (false); } public String[] getAttachmentNames () { return getList (true); } public byte[] getAttachment (String name) { if (isID (name) || (! content.containsKey (name))) { return null; } byte[] rc = null; String coded = content.get (name); if (coded != null) { String valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; byte[] temp = new byte[coded.length ()]; int tlen = 0; long val; int count; int pad; byte pos; val = 0; count = 0; pad = 0; for (int n = 0; n < coded.length (); ++n) { char ch = coded.charAt (n); if (ch == '=') { ++pad; ++count; } else if ((pos = (byte) valid.indexOf (ch)) != -1) { switch (count++) { case 0: val = pos << 18; break; case 1: val |= pos << 12; break; case 2: val |= pos << 6; break; case 3: val |= pos; break; } } if (count == 4) { temp[tlen] = (byte) ((val >> 16) & 0xff); temp[tlen + 1] = (byte) ((val >> 8) & 0xff); temp[tlen + 2] = (byte) (val & 0xff); tlen += 3 - pad; count = 0; if (pad > 0) break; } } rc = Arrays.copyOf (temp, tlen); } return rc; } private synchronized void parseHeader () { if (header == null) { String head = content.get (Preview.ID_HEAD); header = new Hashtable <String, String[]> (); if (head != null) { String[] lines = head.split ("\r?\n"); String cur = null; for (int n = 0; n <= lines.length; ++n) { String line = (n < lines.length ? lines[n] : null); if ((line == null) || ((line.indexOf (' ') != 0) && (line.indexOf ('\t') != 0))) { if (cur != null) { String[] parsed = cur.split (": +", 2); if (parsed.length == 2) { String key = parsed[0].toLowerCase (); String[] field = header.get (key); int nlen = (field == null ? 1 : field.length + 1); String[] nfield = new String[nlen]; if (field != null) for (int m = 0; m < field.length; ++m) nfield[m] = field[m]; nfield[nlen - 1] = parsed[1]; header.put (key, nfield); } } cur = line; } else if (cur != null) { cur += '\n' + line; } } } } } /** * Get individual lines from the header */ public String[] getHeaderFields (String field) { parseHeader (); return header.get (field.toLowerCase ()); } public String getHeaderField (String field, boolean escape) { String rc = null; String[] head = getHeaderFields (field); if ((head != null) && (head.length > 0)) { rc = escape ? escapeEntities (head[0]) : head[0]; } return rc; } public String getHeaderField (String field) { return getHeaderField (field, false); } }