comparison src/nabble/view/web/template/HtmlListNamespace.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:7ecd1a4ef557
1 package nabble.view.web.template;
2
3 import fschmidt.html.Html;
4 import fschmidt.html.HtmlCdata;
5 import fschmidt.html.HtmlScript;
6 import fschmidt.html.HtmlStyle;
7 import fschmidt.html.HtmlTag;
8 import fschmidt.html.HtmlTextContainer;
9 import fschmidt.util.java.HtmlUtils;
10 import fschmidt.util.java.ObjectUtils;
11 import nabble.model.FileUpload;
12 import nabble.model.Message;
13 import nabble.model.ModelHome;
14 import nabble.model.Node;
15 import nabble.model.User;
16 import nabble.naml.compiler.Command;
17 import nabble.naml.compiler.CommandSpec;
18 import nabble.naml.compiler.IPrintWriter;
19 import nabble.naml.compiler.Interpreter;
20 import nabble.naml.compiler.Namespace;
21 import nabble.naml.compiler.ScopedInterpreter;
22 import nabble.naml.namespaces.ListSequence;
23 import nabble.view.lib.Jtp;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import java.net.MalformedURLException;
28 import java.net.URISyntaxException;
29 import java.net.URL;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.ListIterator;
35 import java.util.Set;
36 import java.util.regex.Pattern;
37
38
39 @Namespace (
40 name = "html_list",
41 global = true
42 )
43 public final class HtmlListNamespace extends ListSequence<Object> {
44 private static final Logger logger = LoggerFactory.getLogger(HtmlListNamespace.class);
45
46 private final Message.Source source;
47 private final Message.Format format;
48
49 public HtmlListNamespace(Html list,Message.Source source, Message.Format format) {
50 super(list);
51 this.source = source;
52 this.format = format;
53 }
54
55 public String toString() {
56 return ObjectUtils.join(elements);
57 }
58
59 public String toMailText() {
60 return htmlToTextMail2(elements);
61 }
62
63 public static final CommandSpec process_raw_tags = CommandSpec.NO_OUTPUT;
64
65 @Command public void process_raw_tags(IPrintWriter out,Interpreter interp) {
66 processRaw(elements);
67 }
68
69 public static final CommandSpec process_cdata_tags = CommandSpec.NO_OUTPUT;
70
71 @Command public void process_cdata_tags(IPrintWriter out,Interpreter interp) {
72 processCDATA(elements);
73 }
74
75 public static final CommandSpec process_quotes = CommandSpec.NO_OUTPUT()
76 .scopedParameters("wrote")
77 .optionalParameters("max_quoted_lines")
78 .build()
79 ;
80
81 @Command public void process_quotes(IPrintWriter out,ScopedInterpreter<AuthorWroteNamespace> interp) {
82 int maxQuotedLines = interp.getArgAsInt("max_quoted_lines",10);
83 processQuotes(elements,maxQuotedLines, interp);
84 }
85
86 public static final CommandSpec process_quotes_as_text = CommandSpec.NO_OUTPUT()
87 .scopedParameters("wrote")
88 .build()
89 ;
90
91 @Command public void process_quotes_as_text(IPrintWriter out,ScopedInterpreter<AuthorWroteNamespace> interp) {
92 // In order to avoid complexity, the algorithm should process
93 // just one type of line breaks. Since the desired output is TEXT,
94 // the chosen line break is CRLF. Thus we have to convert <br>
95 // into CRLF below.
96 if (format == Message.Format.HTML) {
97 convertBRIntoCRLF(elements);
98 }
99 // All lines breaks are CRLF now...
100 processQuotesText(elements, 0, interp);
101 }
102
103 private void convertBRIntoCRLF(List<Object> elements) {
104 for (int i = 0; i < elements.size(); i++) {
105 Object o = elements.get(i);
106 if (o instanceof HtmlTag && ((HtmlTag) o).getName().equals("br"))
107 elements.set(i, "\n");
108 else if (o instanceof String) {
109 elements.set(i, ((String)o).replaceAll("\r?\n", ""));
110 }
111 }
112 }
113
114 public static final CommandSpec process_email = CommandSpec.NO_OUTPUT;
115
116 @Command public void process_email(IPrintWriter out,Interpreter interp) {
117 processEmail(elements, source);
118 }
119
120 public static final CommandSpec set_target_to_top = CommandSpec.NO_OUTPUT;
121
122 @Command public void set_target_to_top(IPrintWriter out,Interpreter interp) {
123 setTargetToTop(elements);
124 }
125
126 public static final CommandSpec add_nofollow = CommandSpec.NO_OUTPUT()
127 .optionalParameters("accept_rel_follow")
128 .build()
129 ;
130
131 @Command public void add_nofollow(IPrintWriter out,Interpreter interp) {
132 boolean acceptRelFollow = interp.getArgAsBoolean("accept_rel_follow", false);
133 addNofollow(elements, acceptRelFollow);
134 }
135
136 public static final CommandSpec process_smilies = CommandSpec.NO_OUTPUT;
137
138 @Command public void process_smilies(IPrintWriter out,Interpreter interp) {
139 processSmilies(elements,true);
140 }
141
142 public static final CommandSpec process_smilies_as_text = CommandSpec.NO_OUTPUT;
143
144 @Command public void process_smilies_as_text(IPrintWriter out,Interpreter interp) {
145 processSmilies(elements,false);
146 }
147
148 public static final CommandSpec process_file_tags = CommandSpec.NO_OUTPUT;
149
150 @Command public void process_file_tags(IPrintWriter out,Interpreter interp) {
151 FileUpload.processFileTags(elements,source);
152 }
153
154
155
156 // security
157
158 public static final CommandSpec disable_banned_tags = CommandSpec.NO_OUTPUT()
159 .dotParameter("banned_tags")
160 .optionalParameters("remove")
161 .build()
162 ;
163
164 @Command public void disable_banned_tags(IPrintWriter out,Interpreter interp) {
165 String[] tags = splitAndTrim(interp.getArgString("banned_tags"));
166 boolean remove = interp.getArgAsBoolean("remove", format.isMail());
167 disableBannedTags(elements,remove,tags);
168 }
169
170 public static final CommandSpec disable_invalid_urls = CommandSpec.NO_OUTPUT()
171 .dotParameter("url_attributes")
172 .build()
173 ;
174
175 @Command public void disable_invalid_urls(IPrintWriter out,Interpreter interp) {
176 String[] attrs = splitAndTrim(interp.getArgString("url_attributes"));
177 disableInvalidUrls(elements,false,attrs);
178 }
179
180 public static final CommandSpec disable_javascript_urls = CommandSpec.NO_OUTPUT()
181 .dotParameter("url_attributes")
182 .build()
183 ;
184
185 @Command public void disable_javascript_urls(IPrintWriter out,Interpreter interp) {
186 String[] attrs = splitAndTrim(interp.getArgString("url_attributes"));
187 disableJavascriptUrls(elements,false,attrs);
188 }
189
190 public static final CommandSpec disable_on_event = CommandSpec.NO_OUTPUT;
191
192 @Command public void disable_on_event(IPrintWriter out,Interpreter interp) {
193 disableOnEvent(elements,false);
194 }
195
196 public static final CommandSpec disable_scripts = CommandSpec.NO_OUTPUT;
197
198 @Command public void disable_scripts(IPrintWriter out,Interpreter interp) {
199 disableScripts(elements,false);
200 }
201
202 public static final CommandSpec disable_style_blocks = CommandSpec.NO_OUTPUT()
203 .optionalParameters("remove")
204 .build()
205 ;
206
207 @Command public void disable_style_blocks(IPrintWriter out,Interpreter interp) {
208 boolean remove = interp.getArgAsBoolean("remove", format.isMail());
209 disableStyleBlocks(elements,remove);
210 }
211
212
213 static String[] splitAndTrim(String s) {
214 final String[] a = s.split(",");
215 for( int i=0; i<a.length; i++ ) {
216 a[i] = a[i].trim();
217 }
218 return a;
219 }
220
221
222
223
224 public static final CommandSpec process_tag = CommandSpec.NO_OUTPUT()
225 .parameters("tag")
226 .scopedParameters("do")
227 .dotParameter("do")
228 .build()
229 ;
230
231 @Command public void process_tag(IPrintWriter out,ScopedInterpreter<TagNamespace> interp) {
232 String tagName = interp.getArgString("tag");
233 for( ListIterator<Object> i = elements.listIterator(); i.hasNext(); ) {
234 Object curr = i.next();
235 if( curr instanceof HtmlTag ) {
236 HtmlTag tag = (HtmlTag)curr;
237 if( tag.getName().equals(tagName) ) {
238 TagNamespace ns = new TagNamespace(tag);
239 String replacement = interp.getArgString(ns,"do");
240 i.set(replacement);
241 }
242 }
243 }
244 }
245
246 public static final CommandSpec process_text = CommandSpec.NO_OUTPUT()
247 .parameters("text")
248 .dotParameter("replacement")
249 .build()
250 ;
251
252 @Command public void process_text(IPrintWriter out,Interpreter interp) {
253 String text = interp.getArgString("text");
254 String replacement = interp.getArgString("replacement");
255 for( ListIterator<Object> i = elements.listIterator(); i.hasNext(); ) {
256 Object curr = i.next();
257 if( curr instanceof String ) {
258 String s = (String) curr;
259 if (text.equals(s)) {
260 i.set(replacement);
261 }
262 }
263 }
264 }
265
266 @Namespace (
267 name = "html_tag",
268 global = false,
269 transparent = true
270 )
271 public static final class TagNamespace {
272 private final HtmlTag tag;
273
274 private TagNamespace(HtmlTag tag) {
275 this.tag = tag;
276 }
277
278 @Command public void tag_as_string(IPrintWriter out,Interpreter interp) {
279 out.print(tag);
280 }
281
282 public static final CommandSpec tag_attribute = new CommandSpec.Builder()
283 .dotParameter("name")
284 .build()
285 ;
286
287 @Command public void tag_attribute(IPrintWriter out,Interpreter interp) {
288 out.print( HtmlTag.unquote(tag.getAttributeValue(interp.getArgString("name"))) );
289 }
290
291
292 public static final CommandSpec tag_has_attribute = new CommandSpec.Builder()
293 .dotParameter("name")
294 .build()
295 ;
296
297 @Command public void tag_has_attribute(IPrintWriter out,Interpreter interp) {
298 out.print( tag.getAttributeValue(interp.getArgString("name")) != null );
299 }
300
301 }
302
303
304 public static final CommandSpec process_embed = new CommandSpec.Builder()
305 .dotParameter("regex")
306 .build()
307 ;
308
309 @Command public void process_embed(IPrintWriter out,Interpreter interp) {
310 Pattern ptn = Pattern.compile( interp.getArgString("regex") );
311 for( ListIterator<Object> i = elements.listIterator(); i.hasNext(); ) {
312 Object curr = i.next();
313 if( curr instanceof HtmlTag ) {
314 HtmlTag tag = (HtmlTag)curr;
315 if( tag.getName().equals("nabble_embed") ) {
316 if( tag.isEmpty() ) { // no good
317 i.set( new Embedded(HtmlUtils.htmlEncode(tag.toString())) );
318 } else {
319 List<Object> list = new ArrayList<Object>();
320 while(true) {
321 i.remove();
322 if( !i.hasNext() ) { // no closing tag
323 list.add(0,tag);
324 elements.add( new Embedded(HtmlUtils.htmlEncode(ObjectUtils.join(list))) );
325 return;
326 }
327 curr = i.next();
328 if( curr instanceof HtmlTag ) {
329 HtmlTag tag2 = (HtmlTag)curr;
330 if( tag2.getName().equals("/nabble_embed") ) // done
331 break;
332 }
333 list.add(curr);
334 }
335 String text = ObjectUtils.join(list);
336 if( ptn.matcher(text).matches() ) { // ok
337 i.set( new Embedded(text) );
338 } else { // not ok
339 list.add(0,tag);
340 list.add(curr);
341 i.set( new Embedded(HtmlUtils.htmlEncode(ObjectUtils.join(list))) );
342 }
343 }
344 }
345 }
346 }
347 }
348
349 private static class Embedded {
350 private final String text;
351
352 Embedded(String text) {
353 this.text = text;
354 }
355
356 public String toString() {
357 return text;
358 }
359
360 }
361
362
363
364 // from MessageFormatImpls
365
366
367 private static void processRaw(List<Object> list) {
368 for( ListIterator<Object> i=list.listIterator(); i.hasNext(); ) {
369 Object o = i.next();
370 if( o instanceof HtmlTextContainer ) {
371 HtmlTextContainer container = (HtmlTextContainer)o;
372 if( container.startTag.getName().equalsIgnoreCase("raw") ) {
373 HtmlTag preTag = new HtmlTag(container.startTag);
374 preTag.setName("pre");
375 i.set( new Embedded(
376 preTag.toString() + HtmlUtils.htmlEncode(container.text) + "</pre>"
377 ) );
378 }
379 }
380 }
381 }
382
383 private static void processCDATA(List<Object> list) {
384 for( ListIterator<Object> i=list.listIterator(); i.hasNext(); ) {
385 Object o = i.next();
386 if (o instanceof HtmlCdata) {
387 HtmlCdata container = (HtmlCdata) o;
388 container.toString();
389 i.set("<pre>" + HtmlUtils.htmlEncode("<![CDATA[" + container.text + "]]>") + "</pre>");
390 }
391 }
392 }
393
394
395 private static final HtmlTag blockquote = new HtmlTag("blockquote");
396 private static final HtmlTag _blockquote = new HtmlTag("/blockquote");
397 private static final HtmlTag divQuote = new HtmlTag("div");
398 private static final HtmlTag divQuoteAuthor = new HtmlTag("div");
399 private static final HtmlTag divQuoteMessage = new HtmlTag("div");
400 private static final HtmlTag divQuoteMessageHidden = new HtmlTag("div");
401 private static final HtmlTag _div = new HtmlTag("/div");
402 static {
403 blockquote.setAttribute("class",HtmlTag.quote("quote dark-border-color"));
404 divQuote.setAttribute("class",HtmlTag.quote("quote light-border-color"));
405 divQuoteAuthor.setAttribute("class",HtmlTag.quote("quote-author"));
406 divQuoteAuthor.setAttribute("style",HtmlTag.quote("font-weight: bold;"));
407 divQuoteMessage.setAttribute("class",HtmlTag.quote("quote-message"));
408 divQuoteMessageHidden.setAttribute("class",HtmlTag.quote("quote-message shrinkable-quote"));
409 }
410
411
412 @Namespace (
413 name = "html_list_author_wrote",
414 global = true
415 )
416 public static class AuthorWroteNamespace {
417 private final String authorStr;
418
419 private AuthorWroteNamespace(String authorStr) {
420 this.authorStr = authorStr;
421 }
422
423 @Command public void author(IPrintWriter out,Interpreter interp) {
424 out.print(authorStr);
425 }
426 }
427
428 private static class QuoteInfo {
429 final int i;
430 final int nBreaks;
431
432 QuoteInfo(int i,int nBreaks) {
433 this.i = i;
434 this.nBreaks = nBreaks;
435 }
436 }
437
438 private static final Set<String> breakingTags = new HashSet<String>(Arrays.asList(
439 "br",
440 "div",
441 "p"
442 ));
443
444 private static void processQuotes(List<Object> list, int maxQuotedLines, ScopedInterpreter<AuthorWroteNamespace> interp) {
445 processQuotes(list,maxQuotedLines,0, interp);
446 }
447
448 private static QuoteInfo processQuotes(List<Object> list,int maxQuotedLines,int start, ScopedInterpreter<AuthorWroteNamespace> interp) {
449 int nBreaks = 0;
450 int n = list.size();
451 for( int i=start; i<n; i++ ) {
452 Object o = list.get(i);
453 if( !(o instanceof HtmlTag) )
454 continue;
455 HtmlTag tag = (HtmlTag)o;
456 String tagName = tag.getName().toLowerCase();
457 if( tagName.equals("/quote") )
458 return new QuoteInfo(i,nBreaks);
459 if( tagName.equals("quote") ) {
460 QuoteInfo qi = processQuotes(list,maxQuotedLines,i+1, interp);
461 if( qi==null ) {
462 list.set(i,HtmlUtils.htmlEncode(tag.toString()));
463 return null;
464 }
465 int closeQuote = qi.i;
466 nBreaks += qi.nBreaks + 1;
467 int fromEnd = list.size() - closeQuote;
468
469 list.remove(i);
470 nBreaks -= removeBr(list,i);
471 list.add(i++,blockquote);
472 list.add(i++,divQuote);
473 list.add(i++,"\n");
474 String author = HtmlTag.unquote(tag.getAttributeValue("author"));
475 if( author != null ) {
476 list.add(i++,divQuoteAuthor);
477 list.add(i++,interp.getArgString(new AuthorWroteNamespace(author),"wrote"));
478 list.add(i++,_div);
479 list.add(i++,"\n");
480 }
481 list.add(i++, qi.nBreaks < maxQuotedLines ? divQuoteMessage : divQuoteMessageHidden );
482 // list.add(i++, divQuoteMessage );
483
484 i = list.size() - fromEnd;
485 int brs = removeBrUp(list,i-1);
486 nBreaks -= brs;
487 i -= brs;
488 list.remove(i);
489 nBreaks -= removeBr(list,i);
490 list.add(i++,_div);
491 list.add(i++,"\n");
492 list.add(i++,_div);
493 list.add(i++,_blockquote);
494 list.add(i++,"\n");
495 i--;
496
497 n = list.size();
498 }
499 else if( breakingTags.contains(tagName) ) {
500 nBreaks++;
501 }
502 }
503 return null;
504 }
505
506
507 private static final HtmlTag _a = new HtmlTag("/a");
508
509 private static void processEmail(List<Object> list,Message.Source src) {
510 int count = 0;
511 int n = list.size() - 3 + 1;
512 for( int i=0; i<n; i++ ) {
513 Object o = list.get(i);
514 if( !(o instanceof HtmlTag) )
515 continue;
516 HtmlTag tag = (HtmlTag)o;
517 if( !tag.getName().toLowerCase().equals("email") )
518 continue;
519 o = list.get(i+1);
520 if( !(o instanceof String) )
521 continue;
522 String email = (String)o;
523 o = list.get(i+2);
524 if( !(o instanceof HtmlTag) )
525 continue;
526 HtmlTag endTag = (HtmlTag)o;
527 if( !endTag.getName().toLowerCase().equals("/email") )
528 continue;
529 list.remove(i);
530 list.remove(i);
531 list.remove(i);
532 HtmlTag a = new HtmlTag("a");
533 StringBuilder href = new StringBuilder();
534 href.append( "/user/SendEmail.jtp" );
535 if( src==null || src instanceof Message.TempSource) {
536 href.append( "?type=email&email=" ).append( HtmlUtils.urlEncode(email) );
537 } else {
538 if( src instanceof Node ) {
539 Node node = (Node)src;
540 href.append( "?type=node&node=" ).append( node.getId() );
541 } else if( src instanceof User ) {
542 User user = (User)src;
543 href.append( "?type=sig&user=" ).append( user.getId() );
544 } else {
545 throw new RuntimeException("src="+src);
546 }
547 href.append( "&i=" ).append( count++ );
548 }
549 a.setAttribute("href",HtmlTag.quote(href.toString()));
550 a.setAttribute("target","\"_top\"");
551 a.setAttribute("rel","\"nofollow\"");
552 list.add(i++,a);
553 list.add(i++,ModelHome.hideEmail(email));
554 list.add(i,_a);
555 }
556 }
557
558
559 private static void setTargetToTop(List<Object> list) {
560 for( Object o : list ) {
561 if( o instanceof HtmlTag ) {
562 HtmlTag tag = (HtmlTag)o;
563 if( tag.getName().toLowerCase().equals("a") ) {
564 if( tag.getAttributeValue("target") == null ) {
565 tag.setAttribute("target","\"_top\"");
566 }
567 }
568 }
569 }
570 }
571
572 private static void addNofollow(List<Object> list, boolean acceptRelFollow) {
573 for( Object o : list ) {
574 if( o instanceof HtmlTag ) {
575 HtmlTag tag = (HtmlTag)o;
576 String tagName = tag.getName().toLowerCase();
577 if (tagName.equals("a")) {
578 if (acceptRelFollow && "\"follow\"".equals(tag.getAttributeValue("rel")))
579 continue;
580 tag.setAttribute("rel","\"nofollow\"");
581 tag.setAttribute("link","\"external\"");
582 }
583 }
584 }
585 }
586
587
588
589 // from HtmlSecurity
590
591 private static void disable(ListIterator<Object> i,Object curr,boolean removeViolation) {
592 if( removeViolation ) {
593 i.remove();
594 } else {
595 i.set(HtmlUtils.htmlEncode(curr.toString()));
596 }
597 }
598
599 public static void disableBannedTags(List<Object> html,boolean removeViolation,String... tagNames) {
600 Set<String> tagNameSet = new HashSet<String>();
601 for( String tagName : tagNames ) {
602 tagNameSet.add(tagName);
603 tagNameSet.add("/"+tagName);
604 }
605 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
606 Object curr = i.next();
607 if( curr instanceof HtmlTag ) {
608 HtmlTag tag = (HtmlTag)curr;
609 if( tagNameSet.contains(tag.getName().toLowerCase()) ) {
610 disable(i,tag,removeViolation);
611 }
612 }
613 }
614 }
615
616 private static final URL baseUrl;
617 static {
618 try {
619 baseUrl = new URL("https://www.nabble.com/"); // any valid URL is fine here
620 } catch(MalformedURLException e) {
621 logger.error("",e);
622 System.exit(-1);
623 throw new RuntimeException(e);
624 }
625 }
626
627 private static void disableInvalidUrls(List<Object> html,boolean removeViolation,String... parameters) {
628 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
629 Object curr = i.next();
630 if( curr instanceof HtmlTag ) {
631 HtmlTag tag = (HtmlTag)curr;
632 for (String attrName: parameters) {
633 String val = HtmlTag.unquote(tag.getAttributeValue(attrName));
634 if (val != null) {
635 try {
636 new URL(baseUrl,val).toURI();
637 } catch(MalformedURLException e) {
638 disable(i,tag,removeViolation);
639 break;
640 } catch(URISyntaxException e) {
641 disable(i,tag,removeViolation);
642 break;
643 }
644 }
645 }
646 }
647 }
648 }
649
650 private static void disableJavascriptUrls(List<Object> html,boolean removeViolation,String... parameters) {
651 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
652 Object curr = i.next();
653 if( curr instanceof HtmlTag ) {
654 HtmlTag tag = (HtmlTag)curr;
655 for (String attrName: parameters) {
656 String val = HtmlTag.unquote(tag.getAttributeValue(attrName));
657 if (val != null && val.toLowerCase().startsWith("javascript:")) {
658 disable(i,tag,removeViolation);
659 break;
660 }
661 }
662 }
663 }
664 }
665
666 private static void disableOnEvent(List<Object> html,boolean removeViolation) {
667 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
668 Object curr = i.next();
669 if( curr instanceof HtmlTag ) {
670 HtmlTag tag = (HtmlTag)curr;
671 for( String attrName : tag.getAttributeNames() ) {
672 if (HtmlTag.unquote(attrName).toLowerCase().startsWith("on")) {
673 disable(i,tag,removeViolation);
674 break;
675 }
676 }
677 }
678 }
679 }
680
681 private static final Pattern urchin = Pattern.compile("\\s*_uacct\\s*=\\s*[\"'][^\"']+[\"']\\s*;\\s*urchinTracker\\(\\);\\s*",Pattern.MULTILINE);
682
683 private static void disableScripts(List<Object> html,boolean removeViolation) {
684 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
685 Object curr = i.next();
686 if( curr instanceof HtmlScript ) {
687 HtmlScript script = (HtmlScript)curr;
688 String src = HtmlTag.unquote(script.startTag.getAttributeValue("src"));
689 if( src != null ) {
690 if( script.text.trim().length()==0 ) {
691 if( src.equals("http://www.google-analytics.com/urchin.js") )
692 continue;
693 }
694 } else {
695 if( urchin.matcher(script.text).matches() )
696 continue;
697 }
698 disable(i,script,removeViolation);
699 }
700 }
701 }
702
703 private static void disableStyleBlocks(List<Object> html,boolean removeViolation) {
704 for( ListIterator<Object> i = html.listIterator(); i.hasNext(); ) {
705 Object curr = i.next();
706 if( curr instanceof HtmlStyle ) {
707 disable(i,curr,removeViolation);
708 }
709 }
710 }
711
712
713 private static String htmlToTextMail2(List<Object> html) {
714 StringBuilder buf = new StringBuilder();
715 for (int i = 0; i < html.size(); i++) {
716 Object next = html.get(i);
717 if( next instanceof String ) {
718 buf.append(next);
719 } else if( next instanceof HtmlTag) {
720 HtmlTag tag = (HtmlTag)next;
721 String tagName = tag.getName().toLowerCase();
722 String separator = Message.htmlSeparators.get(tagName);
723 if (separator!=null && buf.length()>0 && buf.charAt(buf.length()-1)!='\n')
724 buf.append(separator);
725 if (tagName.equals("img")) {
726 String src = tag.getAttributeValue("src");
727 if (src!=null) {
728 buf.append('<');
729 buf.append(HtmlTag.unquote(src));
730 buf.append("> ");
731 }
732 }
733 else if (tagName.equals("a")) {
734 String src = tag.getAttributeValue("href");
735 if (src!=null) {
736 String anchorText = html.get(i+1).toString();
737 buf.append(anchorText.trim());
738 buf.append(" <");
739 buf.append(HtmlTag.unquote(src));
740 buf.append("> ");
741 i++;
742 }
743 }
744 }
745 }
746 Message.wrapQuoteText(buf);
747 return buf.toString();
748 }
749
750
751 private static int processQuotesText(List<Object> list, int start, ScopedInterpreter<AuthorWroteNamespace> interp) {
752 int n = list.size();
753 for( int i=start; i<n; i++ ) {
754 Object o = list.get(i);
755 if( !(o instanceof HtmlTag) )
756 continue;
757 HtmlTag tag = (HtmlTag)o;
758 String tagName = tag.getName().toLowerCase();
759 if( tagName.equals("/quote") )
760 return i;
761 if( tagName.equals("quote") ) {
762 int closeQuote = processQuotesText(list, i+1, interp);
763 if( closeQuote == -1 )
764 return -1;
765 int fromEnd = list.size() - closeQuote;
766
767 list.remove(i);
768 removeCRLF(list, i, 0);
769 if (i > 0)
770 list.add(i++,"\n");
771
772 String author = HtmlTag.unquote(tag.getAttributeValue("author"));
773 if( author != null ) {
774 list.add(i++,interp.getArgString(new AuthorWroteNamespace(author),"wrote"));
775 }
776 int begin = i;
777 i = list.size() - fromEnd;
778 removeCRLFUp(list, i-1, -1);
779 list.remove(i);
780 removeCRLF(list, i, 0);
781 for (int j=begin;j<i;j++)
782 quoteText(list,j);
783
784 list.add(i, "\n");
785 n = list.size();
786 }
787 }
788 return -1;
789 }
790
791 private static void quoteText(List<Object> list,int i) {
792 Object obj = list.get(i);
793 if( !(obj instanceof String) )
794 return;
795 String s = (String) obj;
796 if (s.length() == 0)
797 return;
798
799 removeCRLFUp(list, i-1, -1);
800 s = CRLF_UP_PTN.matcher(s).replaceAll("");
801
802 if (!s.startsWith("\n") && !s.startsWith("\r\n") )
803 s = "\n" + s;
804
805 s = s.replaceAll("\n", "\n> ");
806 s = s.replaceAll("\n> >", "\n>>");
807
808 list.set(i, s + '\n');
809 removeCRLF(list, i+1, +1);
810 }
811
812 private static final Pattern CRLF_PTN = Pattern.compile("^(\\r?\\n){1}");
813 private static final Pattern CRLF_UP_PTN = Pattern.compile("(\\r?\\n){1}$");
814 private static final Pattern BR_PTN = Pattern.compile("^(\\s*<br/?>){1,2}");
815 private static final Pattern BR_UP_PTN = Pattern.compile("(<br/?>\\s*){1,2}$");
816
817 private static void removeCRLF(List<Object> list, int i, int increment) {
818 Object obj = i >= 0 && i < list.size()? list.get(i) : null;
819 if (obj instanceof String) {
820 String s = (String) obj;
821 while (s.length() == 0 && increment != 0) {
822 i += increment;
823 obj = i >= 0 && i < list.size()? list.get(i) : null;
824 if (obj == null || !(obj instanceof String))
825 return;
826 s = (String) obj;
827 }
828 s = CRLF_PTN.matcher(s).replaceFirst("");
829 list.set(i, s);
830 }
831 }
832
833 private static void removeCRLFUp(List<Object> list, int i, int increment) {
834 Object obj = i >= 0 && i < list.size()? list.get(i) : null;
835 if (obj instanceof String) {
836 String s = (String) obj;
837 while (s.length() == 0 && increment != 0) {
838 i += increment;
839 obj = i >= 0 && i < list.size()? list.get(i) : null;
840 if (obj == null || !(obj instanceof String))
841 return;
842 s = (String) obj;
843 }
844 s = CRLF_UP_PTN.matcher(s).replaceFirst("");
845 list.set(i, s);
846 }
847 }
848
849 private static int removeBr(List<Object> list,int i) {
850 return removeBr(list,i,0);
851 }
852
853 private static int removeBr(List<Object> list,int i,int count) {
854 if( count==2 )
855 return count;
856 if( i >= list.size() )
857 return count;
858 Object obj = list.get(i);
859 if( obj instanceof String ) {
860 String s = (String)obj;
861 s = BR_PTN.matcher(s).replaceAll("");
862 list.set(i,s);
863 } else if( obj instanceof HtmlTag ) {
864 HtmlTag tag = (HtmlTag)obj;
865 if( tag.getName().equalsIgnoreCase("br") ) {
866 list.remove(i);
867 return removeBr(list,i,count+1);
868 }
869 }
870 return count;
871 }
872
873 private static int removeBrUp(List<Object> list,int i) {
874 return removeBrUp(list,i,0);
875 }
876
877 private static int removeBrUp(List<Object> list,int i,int count) {
878 if( count==2 )
879 return count;
880 if( i < 0 )
881 return count;
882 Object obj = list.get(i);
883 if( obj instanceof String ) {
884 String s = (String)obj;
885 s = BR_UP_PTN.matcher(s).replaceAll("");
886 list.set(i,s);
887 } else if( obj instanceof HtmlTag ) {
888 HtmlTag tag = (HtmlTag)obj;
889 if( tag.getName().equalsIgnoreCase("br") ) {
890 list.remove(i);
891 return removeBrUp(list,i-1,count+1);
892 }
893 }
894 return count;
895 }
896
897
898 // private static final String smiliesDir = "http://" + Jtp.getDefaultHost() + "/images/smiley/";
899 private static final String smiliesDir = "/images/smiley/";
900
901 private static void processSmilies(List<Object> list,boolean isHtml) {
902 for( ListIterator<Object> i=list.listIterator(); i.hasNext(); ) {
903 Object o = i.next();
904 if( o instanceof HtmlTag ) {
905 HtmlTag tag = (HtmlTag)o;
906 if( tag.getName().toLowerCase().equals("smiley") ) {
907 if( isHtml ) {
908 String s = HtmlTag.unquote(tag.getAttributeValue("image"));
909 if( s != null ) {
910 HtmlTag img = new HtmlTag("img class='smiley' src='"+smiliesDir+s+"' /");
911 i.set(img);
912 }
913 }
914 }
915 }
916 }
917 }
918
919
920 }