Mercurial Hosting > nabble
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 } |