0
|
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 }
|