0
|
1 package nabble.view.lib;
|
|
2
|
|
3 import fschmidt.util.java.HtmlUtils;
|
|
4 import fschmidt.util.servlet.ServletUtils;
|
|
5 import nabble.model.DailyNumber;
|
|
6 import nabble.model.Init;
|
|
7 import nabble.model.ModelHome;
|
|
8 import nabble.model.Node;
|
|
9 import nabble.model.NodeIterator;
|
|
10 import nabble.model.NodeSearcher;
|
|
11 import nabble.model.Person;
|
|
12 import nabble.model.Site;
|
|
13 import nabble.model.User;
|
|
14 import nabble.naml.compiler.Template;
|
|
15 import nabble.naml.compiler.TemplatePrintWriter;
|
|
16 import nabble.naml.namespaces.BasicNamespace;
|
|
17 import nabble.view.web.forum.Permalink;
|
|
18 import nabble.view.web.template.NabbleNamespace;
|
|
19 import nabble.view.web.template.NodeNamespace;
|
|
20 import nabble.view.web.template.UserNamespace;
|
|
21 import org.apache.commons.fileupload.DiskFileUpload;
|
|
22 import org.apache.commons.fileupload.FileItem;
|
|
23 import org.apache.commons.fileupload.FileUploadException;
|
|
24 import org.slf4j.Logger;
|
|
25 import org.slf4j.LoggerFactory;
|
|
26
|
|
27 import javax.servlet.ServletException;
|
|
28 import javax.servlet.http.HttpServletRequest;
|
|
29 import javax.servlet.http.HttpServletResponse;
|
|
30 import java.io.IOException;
|
|
31 import java.io.StringWriter;
|
|
32 import java.util.ArrayList;
|
|
33 import java.util.Collection;
|
|
34 import java.util.Collections;
|
|
35 import java.util.Date;
|
|
36 import java.util.HashMap;
|
|
37 import java.util.LinkedHashSet;
|
|
38 import java.util.List;
|
|
39 import java.util.Map;
|
|
40 import java.util.Set;
|
|
41 import java.util.StringTokenizer;
|
|
42 import java.util.TreeSet;
|
|
43 import java.util.regex.Matcher;
|
|
44 import java.util.regex.Pattern;
|
|
45
|
|
46
|
|
47 public final class Jtp {
|
|
48
|
|
49 private static final Logger logger = LoggerFactory.getLogger(Jtp.class);
|
|
50
|
|
51 private static final String ANONYMOUS_COOKIE_ID = "anonymousId";
|
|
52 private static final String ANONYMOUS_COOKIE_NAME = "anonymousName";
|
|
53
|
|
54 private Jtp() {} // never
|
|
55
|
|
56 public static Person getVisitor(HttpServletRequest request, HttpServletResponse response)
|
|
57 throws ServletException
|
|
58 {
|
|
59 Person visitor = getUser(request,response);
|
|
60 if( visitor != null )
|
|
61 return visitor;
|
|
62 return getOrCreateAnonymous(request,response);
|
|
63 }
|
|
64
|
|
65 private static Person getAnonymous(HttpServletRequest request) {
|
|
66 String anonymousId = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_ID);
|
|
67 if (anonymousId == null)
|
|
68 return null;
|
|
69 String anonymousName = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_NAME);
|
|
70 return getSite(request).getAnonymous(anonymousId, anonymousName == null? null : HtmlUtils.urlDecode(anonymousName));
|
|
71 }
|
|
72
|
|
73 private static Person getOrCreateAnonymous(HttpServletRequest request, HttpServletResponse response)
|
|
74 throws ServletException
|
|
75 {
|
|
76 Person anonymous = getAnonymous(request);
|
|
77 if (anonymous == null) {
|
|
78 Site site = getSiteNotNull(request);
|
|
79 String cookie = site.newAnonymousCookie();
|
|
80 anonymous = site.getAnonymous(cookie,null);
|
|
81 ServletUtils.setCookie(request, response, ANONYMOUS_COOKIE_ID, cookie, true, null);
|
|
82 }
|
|
83 return anonymous;
|
|
84 }
|
|
85
|
|
86 public static User getUser(HttpServletRequest request, HttpServletResponse response)
|
|
87 throws ServletException
|
|
88 {
|
|
89 return getUser(request);
|
|
90 }
|
|
91
|
|
92 public static User getUser(HttpServletRequest request)
|
|
93 throws ServletException
|
|
94 {
|
|
95 User user = null;
|
|
96 String userId = ServletUtils.getCookieValue(request,"userId");
|
|
97 if (userId != null && userId.length() > 0) {
|
|
98 user = getSiteNotNull(request).getUser(Long.valueOf(userId));
|
|
99 }
|
|
100 if( user==null )
|
|
101 return null;
|
|
102 if( ServletUtils.getCookieValue(request,"username")==null)
|
|
103 return null;
|
|
104 String pwd = ServletUtils.getCookieValue(request,"password");
|
|
105 if ( pwd==null )
|
|
106 return null;
|
|
107 String passcookie = HtmlUtils.urlDecode(pwd);
|
|
108 if( ! (user.isRegistered() && user.checkPasscookie(passcookie)) )
|
|
109 return null;
|
|
110 trackUser(request, user);
|
|
111 return user;
|
|
112 }
|
|
113
|
|
114 public static void doLogin(HttpServletRequest request, HttpServletResponse response, User user, boolean save)
|
|
115 throws IOException
|
|
116 {
|
|
117 if( !user.isRegistered() )
|
|
118 throw new RuntimeException("user must be registered to login");
|
|
119
|
|
120 ServletUtils.setCookie(request,response,"userId", String.valueOf(user.getId()), save, null);
|
|
121 ServletUtils.setCookie(request,response,"password", HtmlUtils.urlEncode(user.getPasscookie()), save, null);
|
|
122 ServletUtils.setCookie(request,response,"username", HtmlUtils.urlEncode(user.getName()), save, null);
|
|
123 dontCache(response);
|
|
124
|
|
125 DailyNumber.logins.inc();
|
|
126 trackUser(request, user);
|
|
127
|
|
128 // fix anonymous nodes from this user
|
|
129 String anonymousId = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_ID);
|
|
130 if (anonymousId != null) {
|
|
131 user.moveToRegisteredAccount(anonymousId);
|
|
132 ServletUtils.removeCookie(request, response, ANONYMOUS_COOKIE_ID, null);
|
|
133 ServletUtils.removeCookie(request, response, ANONYMOUS_COOKIE_NAME, null);
|
|
134 }
|
|
135 }
|
|
136
|
|
137 private static Set trackUsers = Init.get("trackUsers", Collections.EMPTY_SET);
|
|
138
|
|
139 private static void trackUser(HttpServletRequest request, User user) {
|
|
140 long siteId = user.getSite().getId();
|
|
141 String siteAndUser = siteId + "|" + user.getId();
|
|
142 if (trackUsers.contains(siteAndUser))
|
|
143 logger.error("Track User [site=" + siteId + " | user = " + user.getId() + "] = " + getClientIpAddr(request));
|
|
144 }
|
|
145
|
|
146 public static String getClientIpAddr(HttpServletRequest request) {
|
|
147 return JtpContextServlet.getClientIpAddr(request);
|
|
148 }
|
|
149
|
|
150 public static void logout(HttpServletRequest request,HttpServletResponse response) {
|
|
151 request.getSession().removeAttribute("nextUrl");
|
|
152 ServletUtils.removeCookie(request,response,"userId", null);
|
|
153 ServletUtils.removeCookie(request,response,"password", null);
|
|
154 ServletUtils.removeCookie(request,response,"username", null);
|
|
155 }
|
|
156
|
|
157 public static void login(String msg, HttpServletRequest request, HttpServletResponse response)
|
|
158 throws IOException, ServletException
|
|
159 {
|
|
160 String nextUrl = getCurrentPath(request);
|
|
161 //if(nextUrl.endsWith("NamlServlet.jtp")) logger.error("nextUrl = "+nextUrl);
|
|
162 response.sendRedirect(loginPath(getSiteNotNull(request),msg,nextUrl));
|
|
163 }
|
|
164
|
|
165 public static String getCurrentPath(HttpServletRequest request) {
|
|
166 String s = request.getServletPath();
|
|
167 String q = request.getQueryString();
|
|
168 if( q != null )
|
|
169 s += "?" + q;
|
|
170 return s;
|
|
171 }
|
|
172
|
|
173 public static String loginPath(Site site,String message,String nextUrl) {
|
|
174 Map<String,Object> args = new HashMap<String,Object>();
|
|
175 if( message != null )
|
|
176 args.put("message",message);
|
|
177 if( nextUrl != null )
|
|
178 args.put("nextUrl",nextUrl);
|
|
179 Template template = site.getTemplate( "login_path",
|
|
180 BasicNamespace.class, NabbleNamespace.class
|
|
181 );
|
|
182 StringWriter sw = new StringWriter();
|
|
183 template.run( new TemplatePrintWriter(sw), args,
|
|
184 new BasicNamespace(template), new NabbleNamespace(site)
|
|
185 );
|
|
186 return sw.toString();
|
|
187 }
|
|
188
|
|
189
|
|
190 public static String hideNull(Object obj) {
|
|
191 return obj==null ? "" : obj.toString();
|
|
192 }
|
|
193
|
|
194 public static String userUrl(Person user) {
|
|
195 String base = user.getSite().getBaseUrl();
|
|
196 return base + "/template/NamlServlet.jtp?macro=user_nodes&user=" + user.getIdString();
|
|
197 }
|
|
198
|
|
199 public static String userLink(Person user) {
|
|
200 return "<a href=\"" + userUrl(user) + "\" rel=\"nofollow\" target=\"_top\">"
|
|
201 +user.getNameHtml()+"</a>";
|
|
202 }
|
|
203
|
|
204 public static String userLinkJs(HttpServletRequest request,User user) {
|
|
205 StringBuilder buf = new StringBuilder();
|
|
206 buf.append("<script>document.write('<a href=\\\"");
|
|
207 buf.append(request.getContextPath());
|
|
208 buf.append("' +'/user/UserNodes' + '.jtp?'+'user=");
|
|
209 buf.append(user.getId());
|
|
210 buf.append("\\\" rel=\\\"nofollow\\\" ");
|
|
211 buf.append("' + Nabble.embeddedTarget('_top') + '");
|
|
212 buf.append(">');</script>");
|
|
213 buf.append(user.getNameHtml());
|
|
214 buf.append("<script>document.write('</a>');</script>");
|
|
215 return buf.toString();
|
|
216 }
|
|
217
|
|
218
|
|
219 public static String subjectEncode(String s) {
|
|
220 return ViewUtils.subjectEncode(s);
|
|
221 }
|
|
222
|
|
223 public static String link(Node node) {
|
|
224 if (node == null)
|
|
225 return "";
|
|
226 return "<a href=\"" + url(node) + "\">" + node.getSubjectHtml() + "</a>";
|
|
227 }
|
|
228
|
|
229 public static String url(Node node) {
|
|
230 return node.getSite().getBaseUrl() + path(node);
|
|
231 }
|
|
232
|
|
233 public static String path(Node node) {
|
|
234 switch( node.getKind() ) {
|
|
235 case POST:
|
|
236 return Permalink.path(null,node);
|
|
237 case APP:
|
|
238 Site site = node.getSite();
|
|
239 Template template = site.getTemplate( "app_path",
|
|
240 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class
|
|
241 );
|
|
242 StringWriter sw = new StringWriter();
|
|
243 template.run( new TemplatePrintWriter(sw), Collections.<String,Object>emptyMap(),
|
|
244 new BasicNamespace(template), new NabbleNamespace(site), new NodeNamespace(node)
|
|
245 );
|
|
246 return sw.toString();
|
|
247 }
|
|
248 throw new RuntimeException("never");
|
|
249 }
|
|
250
|
|
251 public static String topicViewPath(Node node, long selectedId, TopicView topicView) {
|
|
252 Site site = node.getSite();
|
|
253 Template template = site.getTemplate( "topic_path",
|
|
254 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class
|
|
255 );
|
|
256 StringWriter sw = new StringWriter();
|
|
257 Map<String,Object> args = new HashMap<String,Object>();
|
|
258 args.put( "view", topicView == TopicView.CLASSIC? "classic" : topicView == TopicView.THREADED? "threaded" : "list");
|
|
259 args.put( "selected_id", selectedId);
|
|
260 template.run( new TemplatePrintWriter(sw), args,
|
|
261 new BasicNamespace(template), new NabbleNamespace(site), new NodeNamespace(node)
|
|
262 );
|
|
263 return sw.toString();
|
|
264 }
|
|
265
|
|
266 public static String link(User user) {
|
|
267 return "<a href=\"" + url(user) + "\">" + user.getNameHtml() + "</a>";
|
|
268 }
|
|
269
|
|
270 public static String url(User user) {
|
|
271 return user.getSite().getBaseUrl() + path(user);
|
|
272 }
|
|
273
|
|
274 public static String path(User user) {
|
|
275 Site site = user.getSite();
|
|
276 Template template = site.getTemplate( "path",
|
|
277 BasicNamespace.class, NabbleNamespace.class, UserNamespace.class
|
|
278 );
|
|
279 StringWriter sw = new StringWriter();
|
|
280 template.run( new TemplatePrintWriter(sw), Collections.<String,Object>emptyMap(),
|
|
281 new BasicNamespace(template), new NabbleNamespace(site), new UserNamespace(user)
|
|
282 );
|
|
283 return sw.toString();
|
|
284 }
|
|
285
|
|
286 public static void javascriptRedirect(HttpServletRequest request,HttpServletResponse response,Node node)
|
|
287 throws IOException, ServletException
|
|
288 {
|
|
289 Shared.javascriptRedirect(request,response,url(node));
|
|
290 }
|
|
291
|
|
292 public static String getStartFragment(String text,int minSize,int maxSize) {
|
|
293 if( text.length() <= maxSize )
|
|
294 return text;
|
|
295 int i = maxSize;
|
|
296 while( !Character.isWhitespace(text.charAt(i)) ) {
|
|
297 i--;
|
|
298 if( i <= minSize )
|
|
299 return text.substring(0,maxSize);
|
|
300 }
|
|
301 while( Character.isWhitespace(text.charAt(i)) ) {
|
|
302 if( i < minSize )
|
|
303 return text.substring(0,maxSize);
|
|
304 i--;
|
|
305 }
|
|
306 return text.substring(0,i+1);
|
|
307 }
|
|
308
|
|
309 public static String truncate(String text,int len,String dotdotdot) {
|
|
310 return text.length() <= len ? text : text.substring(0,len) + dotdotdot;
|
|
311 }
|
|
312
|
|
313 public static String breakUp(final String text) {
|
|
314 return HtmlUtils.breakUp(text,30,true);
|
|
315 }
|
|
316
|
|
317 private static final String PUNCTUATION = "[\\!\"\\&\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\_\\`\\{\\|\\}\\~]";
|
|
318
|
|
319 private static void metaKeywords(final String text,Set<String> words) {
|
|
320 StringTokenizer str = new StringTokenizer(text.toLowerCase().replaceAll(PUNCTUATION," "));
|
|
321 while( str.hasMoreTokens() ) {
|
|
322 String s = str.nextToken();
|
|
323 if( "re".equals(s) )
|
|
324 continue;
|
|
325 words.add(s);
|
|
326 }
|
|
327 }
|
|
328
|
|
329 private static final Set<String> invalidKeywords = new TreeSet<String>();
|
|
330 static {
|
|
331 String[] words = {
|
|
332 "hi", "a", "an", "in", "i", "hello", "please",
|
|
333 "to", "on", "by", "t", "m", "but", "the", "of",
|
|
334 "it", "so", "at", "am", "dear", "me", "and", "are",
|
|
335 "wonder", "from", "be", "been", "is",
|
|
336 "was", "if", "this", "that", "there"
|
|
337 };
|
|
338 for (String word : words) {
|
|
339 invalidKeywords.add(word);
|
|
340 }
|
|
341 }
|
|
342
|
|
343 private static String metaKeywords(Set<String> words) {
|
|
344 StringBuilder buf = new StringBuilder();
|
|
345 for( String s : words ) {
|
|
346 if (invalidKeywords.contains(s.toLowerCase()))
|
|
347 continue;
|
|
348 if (buf.length() > 0) buf.append(", ");
|
|
349 buf.append(s);
|
|
350 }
|
|
351 return buf.toString();
|
|
352 }
|
|
353
|
|
354 public static String metaKeywords(Node node) {
|
|
355 return metaKeywords(node, true);
|
|
356 }
|
|
357
|
|
358 public static String metaKeywords(Node node, boolean includeMessage) {
|
|
359 Set<String> words = new LinkedHashSet<String>();
|
|
360 for( Node n = node; n!=null; n = n.getParent() ) {
|
|
361 metaKeywords(n.getSubjectHtml(),words);
|
|
362 if (includeMessage) {
|
|
363 metaKeywords(getFragment(n.getMessage().getText(), 200), words);
|
|
364 includeMessage = false;
|
|
365 }
|
|
366 }
|
|
367 if( node.getKind() == Node.Kind.APP ) {
|
|
368 String type = node.getType();
|
|
369 if (type.equals(Node.Type.GALLERY)) {
|
|
370 words.add("photo");
|
|
371 words.add("pictures");
|
|
372 words.add("gallery");
|
|
373 words.add("images");
|
|
374 } else if (type.equals(Node.Type.BLOG)) {
|
|
375 words.add("blog");
|
|
376 } else if (type.equals(Node.Type.NEWS)) {
|
|
377 words.add("news");
|
|
378 words.add("newspaper");
|
|
379 words.add("magazine");
|
|
380 } else {
|
|
381 words.add("forum");
|
|
382 words.add("board");
|
|
383 words.add("community");
|
|
384 }
|
|
385 if (node.getAssociatedMailingList() != null)
|
|
386 words.add("mailing list archive");
|
|
387 }
|
|
388 return metaKeywords(words);
|
|
389 }
|
|
390
|
|
391 public static String metaDescription(Node node) {
|
|
392 StringBuilder buf = new StringBuilder();
|
|
393 String name = node.getSubjectHtml();
|
|
394 buf.append(name);
|
|
395 if( node.getKind() == Node.Kind.APP ) {
|
|
396 String viewName = viewName(node).toLowerCase();
|
|
397 if (! (name.toLowerCase().indexOf(viewName) >= 0)) {
|
|
398 buf.append(' ').append(viewName);
|
|
399 }
|
|
400 if (node.getAssociatedMailingList()!=null)
|
|
401 buf.append(" and mailing list archive");
|
|
402 buf.append(".");
|
|
403 }
|
|
404 appendSnippet(node.getMessage().getText(), buf);
|
|
405 return buf.toString();
|
|
406 }
|
|
407
|
|
408 private static void appendSnippet(String text, StringBuilder buf) {
|
|
409 if (buf.length() >= 200 || text.length() == 0) return;
|
|
410 String fragment = getFragment(text, 200 - buf.length());
|
|
411 if (buf.charAt(buf.length() - 1) != '.') buf.append('.');
|
|
412 buf.append(' ');
|
|
413 buf.append(ModelHome.hideAllEmails(HtmlUtils.htmlEncode(fragment).replaceAll("\\s+"," ")));
|
|
414 if (fragment.length() < text.length()) buf.append("...");
|
|
415 }
|
|
416
|
|
417 private static String getFragment(String text, int size) {
|
|
418 if (text.length() <= size) return text;
|
|
419 int end = text.lastIndexOf(' ', size);
|
|
420 if (end < 0) end = size;
|
|
421 return text.substring(0, end);
|
|
422 }
|
|
423
|
|
424
|
|
425 public static String formatDateOnly(String label, Date date) {
|
|
426 return "<script>document.write('" + label + "' + Nabble.formatDateOnly(new Date("+date.getTime()+")));</script>";
|
|
427 }
|
|
428
|
|
429 public static String formatDateOnly(Date date) {
|
|
430 return "<script>document.write(Nabble.formatDateOnly(new Date("+date.getTime()+")));</script>";
|
|
431 }
|
|
432
|
|
433 public static String formatDateLong(String label, Date date) {
|
|
434 return "<script>document.write('" + label + "' + Nabble.formatDateLong(new Date("+date.getTime()+")));</script>";
|
|
435 }
|
|
436
|
|
437 public static String formatDateLong(Date date) {
|
|
438 return "<script>document.write(Nabble.formatDateLong(new Date("+date.getTime()+")));</script>";
|
|
439 }
|
|
440
|
|
441 public static String formatDateShort(Date date) {
|
|
442 return "<script>document.write(Nabble.formatDateShort(new Date("+date.getTime()+")));</script>";
|
|
443 }
|
|
444
|
|
445
|
|
446 private static final String jsWriteStart = "<script>document.write(";
|
|
447 private static final String jsWriteEnd = ");</script>";
|
|
448
|
|
449 public static String javascriptQuote(String s) {
|
|
450 StringBuilder buf = new StringBuilder();
|
|
451 buf.append( "'" );
|
|
452 int i = 0;
|
|
453 while(true) {
|
|
454 int i2 = s.indexOf(jsWriteStart,i);
|
|
455 if( i2 == -1 )
|
|
456 break;
|
|
457 int i3 = s.indexOf(jsWriteEnd,i2);
|
|
458 if( i3 == -1 )
|
|
459 throw new RuntimeException();
|
|
460 buf.append( HtmlUtils.javascriptStringEncode(s.substring(i,i2)) );
|
|
461 buf.append( "'+(" );
|
|
462 buf.append( s.substring(i2+jsWriteStart.length(),i3) );
|
|
463 buf.append( ")+'" );
|
|
464 i = i3 + jsWriteEnd.length();
|
|
465 }
|
|
466 buf.append( HtmlUtils.javascriptStringEncode(s.substring(i)) );
|
|
467 buf.append( "'" );
|
|
468 return buf.toString();
|
|
469 }
|
|
470
|
|
471 public static int getYear(Date date) {
|
|
472 return date.getYear() + 1900;
|
|
473 }
|
|
474
|
|
475 public static int thisYear() {
|
|
476 return getYear(new Date());
|
|
477 }
|
|
478
|
|
479 public static String capitalize(String s) {
|
|
480 return s.substring(0,1).toUpperCase() + s.substring(1);
|
|
481 }
|
|
482
|
|
483 public static void dontCache(HttpServletResponse response) {
|
|
484 response.setHeader("Cache-Control","no-cache, max-age=0");
|
|
485 }
|
|
486
|
|
487
|
|
488 public static Map<String,FileItem> getFileItems(HttpServletRequest request)
|
|
489 throws FileUploadException
|
|
490 {
|
|
491 DiskFileUpload fu = new DiskFileUpload();
|
|
492 Map<String,FileItem> map = new HashMap<String,FileItem>();
|
|
493 @SuppressWarnings("unchecked")
|
|
494 List<FileItem> fileItems = fu.parseRequest(request);
|
|
495 for( FileItem fi : fileItems ) {
|
|
496 map.put(fi.getFieldName(),fi);
|
|
497 }
|
|
498 return map;
|
|
499 }
|
|
500
|
|
501 public static String helpIndexUrl(HttpServletRequest request,HttpServletResponse response)
|
|
502 throws IOException
|
|
503 {
|
|
504 StringBuilder url = new StringBuilder();
|
|
505 url.append( request.getContextPath() );
|
|
506 url.append( "/help/Index.jtp" );
|
|
507 return url.toString();
|
|
508 }
|
|
509
|
|
510 public static long getLong(HttpServletRequest request,String param)
|
|
511 throws ServletException
|
|
512 {
|
|
513 return parseLong( request, request.getParameter(param) );
|
|
514 }
|
|
515
|
|
516 public static long parseLong(HttpServletRequest request,String s)
|
|
517 throws ServletException
|
|
518 {
|
|
519 try {
|
|
520 return Long.parseLong(s);
|
|
521 } catch(NumberFormatException e) {
|
|
522 if (invalidReferer(request) || s.contains(" Result: ")) {
|
|
523 logger.warn("Bad URL", e);
|
|
524 throw new MinorServletException(e);
|
|
525 } else
|
|
526 throw e;
|
|
527 }
|
|
528 }
|
|
529
|
|
530 public static int getInt(HttpServletRequest request,String param)
|
|
531 throws ServletException
|
|
532 {
|
|
533 return parseInt( request, request.getParameter(param) );
|
|
534 }
|
|
535
|
|
536 public static int parseInt(HttpServletRequest request,String s)
|
|
537 throws ServletException
|
|
538 {
|
|
539 try {
|
|
540 return Integer.parseInt(s);
|
|
541 } catch(NumberFormatException e) {
|
|
542 if (invalidReferer(request))
|
|
543 throw new ServletException(e);
|
|
544 else
|
|
545 throw e;
|
|
546 }
|
|
547 }
|
|
548
|
|
549 /*
|
|
550 The User-Agent should not be considered. If a browser doesn't send the referer, then we have no way
|
|
551 to know if the referer is valid or not. So we should assume it isn't to avoid having cases
|
|
552 where it really isn't valid go into error.log . If the referer really was valid, then the same problem
|
|
553 should show up again in a good browser. We shouldn't worry so much about taking care of broken browsers.
|
|
554 -fschmidt
|
|
555 */
|
|
556 public static boolean invalidReferer(HttpServletRequest request) {
|
|
557 String referer = request.getHeader("referer");
|
|
558 if( referer == null
|
|
559 || !referer.startsWith("http://" + request.getHeader("host"))
|
|
560 )
|
|
561 return true;
|
|
562 if( request.getMethod().equals("GET") ) {
|
|
563 StringBuffer url = request.getRequestURL();
|
|
564 String query = request.getQueryString();
|
|
565 if( query != null )
|
|
566 url.append( '?' ).append( query );
|
|
567 if( url.toString().equals(referer) )
|
|
568 return true;
|
|
569 }
|
|
570 return false;
|
|
571 }
|
|
572
|
|
573 public static ServletException servletException(HttpServletRequest request,String msg) {
|
|
574 if( invalidReferer(request) )
|
|
575 return new ServletException(msg);
|
|
576 throw new RuntimeException(msg);
|
|
577 }
|
|
578
|
|
579 public static String getString(HttpServletRequest request, String param)
|
|
580 throws ServletException
|
|
581 {
|
|
582 String value = request.getParameter(param);
|
|
583 if (value == null && invalidReferer(request)) {
|
|
584 throw new ServletException("Parameter is null: " + param + " [referer is null]");
|
|
585 }
|
|
586 return value;
|
|
587 }
|
|
588
|
|
589 // should be removed
|
|
590 public static String getReadAuthorizationKey(Node node) {
|
|
591 if( node==null )
|
|
592 return null;
|
|
593 node = Permissions.getPrivateNode(node);
|
|
594 return node!=null ? Long.toString(node.getId()) : null;
|
|
595 }
|
|
596
|
|
597 // should be removed
|
|
598 public static boolean authorizeForRead(String key,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
|
|
599 Node node = getSiteNotNull(request).getNode( Long.parseLong(key) );
|
|
600 User user = getUser(request,response);
|
|
601 if( user==null ) {
|
|
602 login("You must login to view " + node.getSubject(), request, response);
|
|
603 return false;
|
|
604 }
|
|
605
|
|
606 if( !Jtp.canBeViewedBy(node,user) ) {
|
|
607 response.sendRedirect(getUnauthorizedPath(node));
|
|
608 return false;
|
|
609 }
|
|
610 return true;
|
|
611 }
|
|
612
|
|
613 private static String getUnauthorizedPath(Node node) {
|
|
614 Template template = node.getSite().getTemplate( "unauthorized_path",
|
|
615 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class
|
|
616 );
|
|
617 StringWriter sw = new StringWriter();
|
|
618 template.run( new TemplatePrintWriter(sw), Collections.<String, Object>emptyMap(),
|
|
619 new BasicNamespace(template), new NabbleNamespace(node.getSite()), new NodeNamespace(Permissions.getPrivateNode(node))
|
|
620 );
|
|
621 return sw.toString();
|
|
622 }
|
|
623
|
|
624 private static final long startTime = System.currentTimeMillis()/1000*1000;
|
|
625
|
|
626 public static boolean cacheMe(HttpServletRequest request,HttpServletResponse response)
|
|
627 throws ServletException, IOException
|
|
628 {
|
|
629 if( startTime <= request.getDateHeader("If-Modified-Since") ) {
|
|
630 response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
|
631 return true;
|
|
632 }
|
|
633 response.setDateHeader("Last-Modified",startTime);
|
|
634 return false;
|
|
635 }
|
|
636
|
|
637
|
|
638 public static void addBreadCrumbEvents(Collection<String> events,Node node) {
|
|
639 if( node==null ) return;
|
|
640 for( Node f = node.getApp(); f != null; f = f.getParent() ) {
|
|
641 events.add( Cache.nodeChangeEvent(f) );
|
|
642 }
|
|
643 }
|
|
644
|
|
645
|
|
646 public static List<Node> getPinnedChildren(Node parent) {
|
|
647 List<Node> pinned = new ArrayList<Node>();
|
|
648 NodeIterator<? extends Node> iter = parent.getChildren();
|
|
649 try {
|
|
650 while( iter.hasNext() ) {
|
|
651 Node node = iter.next();
|
|
652 if( !node.isPinned() )
|
|
653 break;
|
|
654 pinned.add(node);
|
|
655 }
|
|
656 } finally {
|
|
657 iter.close();
|
|
658 }
|
|
659 return pinned;
|
|
660 }
|
|
661
|
|
662 public static void addPinnedChild(Node parent, Node child) {
|
|
663 List<Node> pinned = getPinnedChildren(parent);
|
|
664 if (child.getKind() == Node.Kind.APP) {
|
|
665 // Insert after the last pinned forum (before pinned threads)
|
|
666 boolean added = false;
|
|
667 for (int i = 0; i < pinned.size(); i++) {
|
|
668 Node node = pinned.get(i);
|
|
669 if (node.getKind() == Node.Kind.POST) {
|
|
670 pinned.add(i, child);
|
|
671 added = true;
|
|
672 break;
|
|
673 }
|
|
674 }
|
|
675 if (!added)
|
|
676 pinned.add(child);
|
|
677 }
|
|
678 else {
|
|
679 pinned.add(child);
|
|
680 }
|
|
681 parent.pin(pinned.toArray(new Node[0]));
|
|
682 }
|
|
683
|
|
684 public static void unpinChild(Node parent, Node child) {
|
|
685 List<Node> pinned = getPinnedChildren(parent);
|
|
686 pinned.remove(child);
|
|
687 parent.pin(pinned.toArray(new Node[0]));
|
|
688 }
|
|
689
|
|
690
|
|
691 public static String snippet(Node node,int len) {
|
|
692 String text = node.getMessage().getTextWithoutQuotes();
|
|
693 return HtmlUtils.htmlEncode(ModelHome.hideAllEmails(NodeSearcher.getStartingFragment(text, len, "...")));
|
|
694 }
|
|
695
|
|
696
|
|
697 public static String noCid(String url) {
|
|
698 return url.replaceAll("(;|&)cid=(\\d|\\-)+", "");
|
|
699 }
|
|
700
|
|
701 // should go away -fschmidt
|
|
702 public static String viewName(Node node) {
|
|
703 Node forum = node.getApp();
|
|
704 if (forum == null)
|
|
705 return "Forum";
|
|
706 String type = forum.getType();
|
|
707 if (type.equals(Node.Type.GALLERY))
|
|
708 return "Gallery";
|
|
709 else if (type.equals(Node.Type.NEWS))
|
|
710 return "Newspaper";
|
|
711 else if (type.equals(Node.Type.BLOG))
|
|
712 return "Blog";
|
|
713 else
|
|
714 return "Forum";
|
|
715 }
|
|
716
|
|
717 // should go away -fschmidt
|
|
718 public static String childName(Node node, boolean plural) {
|
|
719 Node forum = node.getApp();
|
|
720 if (forum == null)
|
|
721 return plural? "Sub-Forums" : "Sub-Forum";
|
|
722 String type = forum.getType();
|
|
723 if (type.equals(Node.Type.GALLERY) ||
|
|
724 type.equals(Node.Type.NEWS) ||
|
|
725 type.equals(Node.Type.BLOG) ||
|
|
726 type.equals(Node.Type.BOARD))
|
|
727 return plural? "Subcategories" : "Subcategory";
|
|
728 else
|
|
729 return plural? "Sub-Forums" : "Sub-Forum";
|
|
730 }
|
|
731
|
|
732 public static String parentName(Node node) {
|
|
733 if (node.getParent() == null)
|
|
734 return "Current Parent";
|
|
735 return "Parent " + viewName(node.getParent());
|
|
736 }
|
|
737
|
|
738 // what do you plan for this? -fschmidt
|
|
739 public static String getSmallLogo(String type) {
|
|
740 if (type.equals(Node.Type.GALLERY))
|
|
741 return "<img src=\"/images/homepage/gallery_sm.png\" width=20 height=15 alt=\"Free photo gallery\">";
|
|
742 else if (type.equals(Node.Type.BLOG))
|
|
743 return "<img src=\"/images/homepage/blog_sm.png\" width=20 height=17 alt=\"Free blog\">";
|
|
744 else if (type.equals(Node.Type.NEWS))
|
|
745 return "<img src=\"/images/homepage/news_sm.png\" width=20 height=15 alt=\"Free newspaper\">";
|
|
746 else
|
|
747 return "";
|
|
748 }
|
|
749
|
|
750 static String getCanonicalUrl(HttpServletRequest request) {
|
|
751 String current = ServletUtils.getCurrentURL(request);
|
|
752 if (current.startsWith(Lazy.homeContextUrl))
|
|
753 return null;
|
|
754 String host = request.getHeader("host");
|
|
755 return current.replace("http://"+host, Lazy.homeContextUrl);
|
|
756 }
|
|
757
|
|
758 private static final Pattern NUMBER_PATTERN = Pattern.compile("^[0-9]+$");
|
|
759 public static boolean isInteger(String s) {
|
|
760 return s != null && NUMBER_PATTERN.matcher(s).find();
|
|
761 }
|
|
762
|
|
763 /**
|
|
764 * Maximum number of rows an app page can have.
|
|
765 * This limit must exist because there are physical restrictions:
|
|
766 * - the $Js URL can't support too many nodes at the same time.
|
|
767 * - div tags have a limit of 32,768 pixels of height.
|
|
768 */
|
|
769 public static int getMaxRowsPerPage(String type) {
|
|
770 int n = 100;
|
|
771 if (type.equals(Node.Type.BLOG))
|
|
772 n = 20;
|
|
773 return n;
|
|
774 }
|
|
775
|
|
776 /** Default number of rows an app page has. */
|
|
777 public static int getDefaultRowsPerPage(String type) {
|
|
778 int n = 35;
|
|
779 if (type.equals(Node.Type.BLOG))
|
|
780 n = 10;
|
|
781 else if (type.equals(Node.Type.GALLERY))
|
|
782 n = 12;
|
|
783 else if (type.equals(Node.Type.GALLERY))
|
|
784 n = 25;
|
|
785 return n;
|
|
786 }
|
|
787
|
|
788 public static int getDefaultMixedLength() { return 6;}
|
|
789 public static int getMaxMixedLength() { return 20;}
|
|
790
|
|
791 private static final String defaultHost = (String)Init.get("defaultHost");
|
|
792 static {
|
|
793 if( defaultHost==null ) {
|
|
794 logger.error("no defaultHost");
|
|
795 System.exit(-1);
|
|
796 }
|
|
797 }
|
|
798
|
|
799 public static String getDefaultHost() {
|
|
800 return defaultHost;
|
|
801 }
|
|
802
|
|
803 public static String getBaseUrl(HttpServletRequest request) {
|
|
804 String host = request.getHeader("host");
|
|
805 String scheme = request.getHeader("X-Forwarded-Proto");
|
|
806 if( scheme == null )
|
|
807 scheme = request.getScheme();
|
|
808 return scheme + "://" + host;
|
|
809 }
|
|
810
|
|
811 // fix jetty bug
|
|
812 public static void sendRedirect(HttpServletRequest request,HttpServletResponse response,String url)
|
|
813 throws IOException
|
|
814 {
|
|
815 if( url.startsWith("/") )
|
|
816 url = Jtp.getBaseUrl(request) + url;
|
|
817 response.sendRedirect(url);
|
|
818 }
|
|
819
|
|
820 private static class Lazy {
|
|
821 static final Pattern ROOT_URL_PATTERN;
|
|
822 static final String defaultContextUrl;
|
|
823 static final String homeContextUrl;
|
|
824 static {
|
|
825 defaultContextUrl = "http://" + defaultHost;
|
|
826 homeContextUrl = Init.get("homeContextUrl",defaultContextUrl);
|
|
827 ROOT_URL_PATTERN = Pattern.compile( ".*\\.(\\d+)\\."+Pattern.quote(defaultHost) );
|
|
828 }
|
|
829 }
|
|
830
|
|
831 public static String homePage() {
|
|
832 return nabble.view.web.Index.url();
|
|
833 }
|
|
834
|
|
835 public static String supportUrl() {
|
|
836 return "http://support.nabble.com/";
|
|
837 }
|
|
838
|
|
839 public static String supportLink() {
|
|
840 String supportUrl = supportUrl();
|
|
841 return supportUrl==null ? null : "<a href='"+supportUrl+"' target='_top'>Nabble Support</a>";
|
|
842 }
|
|
843
|
|
844 public static String defaultContextUrl() {
|
|
845 return Lazy.defaultContextUrl;
|
|
846 }
|
|
847
|
|
848 public static String homeContextUrl() {
|
|
849 return Lazy.homeContextUrl;
|
|
850 }
|
|
851
|
|
852 // doesn't return null
|
|
853 public static Site getSiteNotNull(HttpServletRequest request)
|
|
854 throws ServletException
|
|
855 {
|
|
856 Site site = getSite(request);
|
|
857 if( site == null )
|
|
858 throw servletException(request,"site not found");
|
|
859 return site;
|
|
860 }
|
|
861
|
|
862 private static final Object noSite = new Object();
|
|
863
|
|
864 public static Site getSite(HttpServletRequest request) {
|
|
865 Object obj = request.getAttribute("site");
|
|
866 if( obj == null ) {
|
|
867 Long siteId = getSiteIdFromDomain( ServletUtils.getHost(request) );
|
|
868 if( siteId != null )
|
|
869 obj = ModelHome.getSite(siteId);
|
|
870 if( obj == null )
|
|
871 obj = noSite;
|
|
872 request.setAttribute("root",obj);
|
|
873 }
|
|
874 return obj==noSite ? null : (Site)obj;
|
|
875 }
|
|
876
|
|
877 public static Site getSiteFromUrl(String url) {
|
|
878 String domain = extractDomain(url);
|
|
879 if( domain == null )
|
|
880 return null;
|
|
881 Long siteId = getSiteIdFromDomain(domain);
|
|
882 if( siteId == null )
|
|
883 return null;
|
|
884 return ModelHome.getSite(siteId);
|
|
885 }
|
|
886
|
|
887 public static Long getSiteIdFromDomain(String domain) {
|
|
888 if( domain.equals(defaultHost) )
|
|
889 return null;
|
|
890 Matcher m = Lazy.ROOT_URL_PATTERN.matcher(domain);
|
|
891 if( m.matches() ) {
|
|
892 return Long.parseLong(m.group(1));
|
|
893 } else {
|
|
894 return ModelHome.getSiteIdFromDomain(domain);
|
|
895 }
|
|
896 }
|
|
897
|
|
898 public static String getDefaultBaseUrl(Site site) {
|
|
899 return ViewUtils.getDefaultBaseUrl( site.getId(), site.getRootNode().getSubject(), defaultHost );
|
|
900 }
|
|
901
|
|
902 public static String extractDomain(String url) {
|
|
903 int posDoubleSlash = url.indexOf("//");
|
|
904 int posDomainEnd = url.indexOf('/', posDoubleSlash+2);
|
|
905 // if the last slash was not found, we can assume the domain ends at the end of the string.
|
|
906 posDomainEnd = posDomainEnd == -1? url.length() : posDomainEnd;
|
|
907 return posDoubleSlash > 0 && posDoubleSlash < 8 && posDomainEnd > posDoubleSlash? url.substring(posDoubleSlash+2, posDomainEnd) : null;
|
|
908 }
|
|
909
|
|
910 // permissions hacks
|
|
911
|
|
912 private static boolean can(Person person,String macro,Node node) {
|
|
913 Template template = node.getSite().getTemplate( macro,
|
|
914 BasicNamespace.class, NabbleNamespace.class, UserNamespace.class
|
|
915 );
|
|
916 StringWriter sw = new StringWriter();
|
|
917 Map<String,Object> args = new HashMap<String,Object>();
|
|
918 args.put( "node_attr", new NodeNamespace(node) );
|
|
919 template.run( new TemplatePrintWriter(sw), args,
|
|
920 new BasicNamespace(template), new NabbleNamespace(node.getSite()), new UserNamespace(person)
|
|
921 );
|
|
922 return sw.toString().equals("true");
|
|
923 }
|
|
924
|
|
925 public static boolean canBeEditedBy(Node node,Person person) {
|
|
926 return can(person,"can_edit",node);
|
|
927 }
|
|
928
|
|
929 public static boolean canBeRemovedBy(Node node,Person person) {
|
|
930 return can(person,"can_move",node);
|
|
931 }
|
|
932
|
|
933 public static boolean canBeDeletedBy(Node node,Person person) {
|
|
934 return can(person,"can_delete",node);
|
|
935 }
|
|
936
|
|
937 public static boolean canBeViewedBy(Node node,Person person) {
|
|
938 return can(person,"can_view",node);
|
|
939 }
|
|
940
|
|
941 public static boolean canMove(Node node,Person person) {
|
|
942 return can(person,"can_move",node);
|
|
943 }
|
|
944
|
|
945 public static boolean canAssign(Node node,Person person) {
|
|
946 return can(person,"can_be_assigned_to",node);
|
|
947 }
|
|
948
|
|
949 public static boolean canChangePostDateOf(Node node,Person person) {
|
|
950 return can(person,"can_change_post_date_of",node);
|
|
951 }
|
|
952
|
|
953 public static boolean isSiteAdmin(Site site,Person person) {
|
|
954 return person instanceof User && site.getRootNode().getOwner().equals(person); // for now
|
|
955 }
|
|
956
|
|
957 public static final String CACHED = "cached";
|
|
958
|
|
959 public static boolean isCached(HttpServletRequest request,HttpServletResponse response) {
|
|
960 return response.containsHeader("Etag") || request.getAttribute(CACHED) != null;
|
|
961 }
|
|
962
|
|
963 public static String termsUrl(boolean back) {
|
|
964 return homeContextUrl() + "/Terms.jtp";
|
|
965 }
|
|
966 }
|