view src/nabble/model/MessageFormatImpls.java @ 47:72765b66e2c3

remove mailing list code
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 18 Jun 2021 17:44:24 -0600
parents 7ecd1a4ef557
children
line wrap: on
line source

package nabble.model;

import fschmidt.html.Html;
import fschmidt.html.HtmlTag;
import fschmidt.html.HtmlTextContainer;
import fschmidt.util.java.HtmlUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


final class MessageFormatImpls {
	private MessageFormatImpls() {}  // never

	private static final Pattern urlPtn = Pattern.compile(
		"\\bhttps?://[^\\s\\p{Z}<>\"{}|\\\\^\\[\\]`]*[^\\s\\p{Z}<>\"{}|\\\\^\\[\\]`~,.?\\)]"
	);


	private static abstract class InputFormat extends Message.Format {

		InputFormat(char code,String name) {
			super(code,name);
		}

		protected final String getText(String msg, Message.Source source) {
		    return MessageUtils.htmlToText(MessageUtils.newHtml(msg));
		}

		protected final String getTextWithoutQuotes(String msg, Message.Source source) {
			return MessageUtils.getTextWithoutQuotes(msg);
		}

		protected final String getEmail(String msg,int i) {
			Html list = MessageUtils.newHtml(msg);
			return MessageFormatImpls.getEmail(list,i);
		}

	}

	static final Message.Format TEXT = new InputFormat('t',"text") {

		protected Html parse(String msg,Message.Source source) {
			Html html = MessageUtils.newHtml(msg);
			nestIgnoredBlocks(html);
			encodeUnallowedTags(html);
			convertLinks(html);
			textToHtml(html);
			unnestIgnoredBlocks(html);
			return html;
		}

		protected Html parseForMailText(String msg,Message.Source source) {
			Html html = MessageUtils.newHtml(msg);
			changeUnallowedTagsToStrings(html);
			return html;
		}

	};

	static void convertLinks(Html list) {
		boolean convertLinks = true;
		for( ListIterator<Object> i=list.listIterator(); i.hasNext(); ) {
			Object o = i.next();
			if( o instanceof HtmlTag ) {
				HtmlTag tag = (HtmlTag)o;
				String name = tag.getName().toLowerCase();
				if( name.equals("a") || name.equals("nabble_a") ) {
					convertLinks = false;
				} else if( name.equals("/a") || name.equals("/nabble_a") ) {
					convertLinks = true;
				}
			} else if( convertLinks && o instanceof String ) {
				String s = (String)o;
				i.remove();
				for( Object o2 : convertLinks(s) ) {
					i.add(o2);
				}
			}
		}
	}

	static Html convertLinks(String s) {
		Html list = new Html();
		Matcher m = urlPtn.matcher(s);
		int i = 0;
		while( m.find() ) {
			String s2 = s.substring(i,m.start());
			if( s2.length() > 0 )
				list.add(s2);
			String url = m.group();
			HtmlTag tag = new HtmlTag("a");
			tag.setAttribute("href",HtmlTag.quote(url));
			list.add(tag);
			list.add(url);
			list.add(_a);
			i = m.end();
		}
		String s2 = s.substring(i);
		if( s2.length() > 0 )
			list.add(s2);
		return list;
	}

	private static final Set<String> asBlock = new HashSet<String>(Arrays.asList(
		"table",
		"/table",
		"thead",
		"/thead",
		"tbody",
		"/tbody",
		"tr",
		"/tr",
		"th",
		"/th",
		"td",
		"/td"
	));

	static void textToHtml(Html list) {
		boolean isBlock = false;
		int n = list.size();
		for( int i=0; i<n; i++ ) {
			Object o = list.get(i);
			if( o instanceof String ) {
				String s = (String)o;
				Html l = textToHtml(s,isBlock);
				list.remove(i);
				list.addAll(i,l);
				int d = l.size() - 1;
				i += d;
				n += d;
			}
			isBlock = false;
			if( o instanceof HtmlTag ) {
				HtmlTag tag = (HtmlTag)o;
				String name = tag.getName().toLowerCase();
				if( name.charAt(0) == '/' && Message.htmlSeparators.get(name) == "\n"
					|| asBlock.contains(name)
				) {
					isBlock = true;
				}
			} else if( o instanceof HtmlTextContainer ) {
				isBlock = true;
//			} else if( o instanceof Html ) {
//				isBlock = true;
			}
		}
	}

	private static final HtmlTag br = new HtmlTag("br/");

	private static Html textToHtml(String s,boolean isAfterBlock) {
		s = encodeHtml(s);
		Html list = new Html();
		StringBuilder buf = new StringBuilder();
		final char[] a = s.toCharArray();
		boolean isLineStart = true;
		for (char c : a) {
			switch (c) {
			case '\r':
				buf.append( c );
				break;
			case '\n':
				buf.append( c );
				String t = buf.toString();
				if( t.trim().length() > 0 )
					list.add(t);
				if( isAfterBlock )
					isAfterBlock = false;
				else
					list.add( br );
				isLineStart = true;
				buf.setLength(0);
				break;
			case '\t':
				if( isLineStart ) {
					buf.append( "&nbsp; &nbsp; &nbsp; &nbsp; " );
				} else {
					buf.append( '\t' );
				}
				break;
			case ' ':
				if( isLineStart ) {
					buf.append( "&nbsp;" );
					isLineStart = false;
				} else if( buf.charAt(buf.length()-1)==' ' ) {
					buf.append( "&nbsp;" );
				} else {
					buf.append( ' ' );
				}
				break;
			default:
				buf.append( c );
				isLineStart = false;
				isAfterBlock = false;
			}
		}
		if( buf.length() > 0 )
			list.add( buf.toString() );
		return list;
	}


	static final Message.Format HTML = new InputFormat('h',"html") {

		protected Html parse(String msg,Message.Source source) {
			return MessageUtils.newHtml(msg);
		}

		protected Html parseForMailText(String msg,Message.Source source) {
			return MessageUtils.newHtml(msg);
		}

	};

	private static final HtmlTag _a = new HtmlTag("/a");

	static String getEmail(Html list,int iEmail) {
		int count = 0;
		int n = list.size() - 3 + 1;
		for( int i=0; i<n; i++ ) {
			Object o = list.get(i);
			if( !(o instanceof HtmlTag) )
				continue;
			HtmlTag tag = (HtmlTag)o;
			if( !tag.getName().toLowerCase().equals("email") )
				continue;
			o = list.get(i+1);
			if( !(o instanceof String) )
				continue;
			String email = (String)o;
			o = list.get(i+2);
			if( !(o instanceof HtmlTag) )
				continue;
			HtmlTag endTag = (HtmlTag)o;
			if( !endTag.getName().toLowerCase().equals("/email") )
				continue;
			if( count++ == iEmail )
				return email;
			i += 2;
		}
		return null;
	}


	static String encodeHtml(String s) {
		char[] a = s.toCharArray();
		StringBuilder buf = new StringBuilder();
		for( int i=0; i<a.length; i++ ) {
			char c = a[i];
			switch(c) {
/*
			case '&':
				buf.append("&amp;");
				break;
*/
			case '<':
				buf.append("&lt;");
				break;
			case '>':
				buf.append("&gt;");
				break;
			case '"':
				buf.append("&quot;");
				break;
			default:
				buf.append(c);
			}
		}
		return buf.toString();
	}


	private static final List<String> ignoredTags = Arrays.asList(
		"nabble_embed"
	);

	private static void nestIgnoredBlocks(Html html) {
		outer:
		for( String tagName : ignoredTags ) {
			String endTagName = "/" + tagName;
		    for( ListIterator<Object> i=html.listIterator(); i.hasNext(); ) {
		        Object obj = i.next();
				if( obj instanceof HtmlTag ) {
					HtmlTag tag = (HtmlTag)obj;
					if( tag.getName().equalsIgnoreCase(tagName) ) {
						Html block = new Html();
						i.remove();
						block.add(obj);
						while(true) {
							if( !i.hasNext() ) {  // unclosed
								i.add(HtmlUtils.htmlEncode(block.toString()));
								continue outer;
							}
							obj = i.next();
							i.remove();
							block.add(obj);
							if( obj instanceof HtmlTag ) {
								tag = (HtmlTag)obj;
								if( tag.getName().equalsIgnoreCase(endTagName) ) {
									i.add(block);
									break;
								}
							}
						}
					}
				}
			}
		}
	}

	private static void unnestIgnoredBlocks(Html html) {
	    for( ListIterator<Object> i=html.listIterator(); i.hasNext(); ) {
	        Object obj = i.next();
			if( obj instanceof Html ) {
				Html block = (Html)obj;
				i.remove();
				for( Object obj2 : block ) {
					i.add(obj2);
				}
			}
		}
	}

	private static final Set<String> allowedTextTags = new HashSet<String>();
	static {
		for( String tag : new String[]{
			"b",
			"i",
			"quote",
			"email",
			"img",
			"a",
			"font",
			"span",
			"smiley",
			"nabble_img",
			"nabble_a",
			"object",
			"param",
			"embed",
			"raw",
			"nabble_embed",
			"h1",
			"h2",
			"h3",
			"h4",
			"h5",
			"h6",
			"table",
			"thead",
			"tbody",
			"tr",
			"th",
			"td",
		} ) {
			allowedTextTags.add(tag);
			allowedTextTags.add("/"+tag);
		}
	}

	private static void encodeUnallowedTags(Html html) {
		for( ListIterator<Object> i=html.listIterator(); i.hasNext(); ) {
			Object obj = i.next();
			if( obj instanceof HtmlTag ) {
				HtmlTag tag = (HtmlTag)obj;
				if( !allowedTextTags.contains( tag.getName().toLowerCase() ) ) {
					i.remove();
					i.add( HtmlUtils.htmlEncode(tag.toString()) );
				}
			}
		}
	}

	private static void changeUnallowedTagsToStrings(Html html) {
		for( ListIterator<Object> i=html.listIterator(); i.hasNext(); ) {
			Object obj = i.next();
			if( obj instanceof HtmlTag ) {
				HtmlTag tag = (HtmlTag)obj;
				if( !allowedTextTags.contains( tag.getName().toLowerCase() ) ) {
					i.remove();
					i.add( tag.toString() );
				}
			}
		}
	}
}