1 // 2 // WebContext.java 3 // cldrtools 4 // 5 // Created by Steven R. Loomis on 3/11/2005. 6 // Copyright 2005-2012 IBM. All rights reserved. 7 // 8 package org.unicode.cldr.web; 9 10 import java.io.IOException; 11 import java.io.PrintWriter; 12 import java.io.StringWriter; 13 import java.io.UnsupportedEncodingException; 14 import java.io.Writer; 15 import java.net.URLEncoder; 16 import java.sql.SQLException; 17 import java.util.Hashtable; 18 import java.util.Iterator; 19 import java.util.Map; 20 import java.util.Map.Entry; 21 import java.util.Set; 22 import java.util.TreeMap; 23 import java.util.Vector; 24 import java.util.concurrent.ConcurrentHashMap; 25 26 import javax.servlet.RequestDispatcher; 27 import javax.servlet.ServletException; 28 import javax.servlet.ServletRequest; 29 import javax.servlet.ServletResponse; 30 import javax.servlet.http.Cookie; 31 import javax.servlet.http.HttpServletRequest; 32 import javax.servlet.http.HttpServletResponse; 33 import javax.servlet.http.HttpSession; 34 35 import org.unicode.cldr.test.CheckCLDR; 36 import org.unicode.cldr.test.DisplayAndInputProcessor; 37 import org.unicode.cldr.test.HelpMessages; 38 import org.unicode.cldr.util.CLDRFile; 39 import org.unicode.cldr.util.CLDRLocale; 40 import org.unicode.cldr.util.Level; 41 import org.unicode.cldr.util.Organization; 42 import org.unicode.cldr.util.PathHeader; 43 import org.unicode.cldr.util.PathHeader.PageId; 44 import org.unicode.cldr.util.StandardCodes; 45 import org.unicode.cldr.web.CLDRProgressIndicator.CLDRProgressTask; 46 import org.unicode.cldr.web.SurveyAjax.AjaxType; 47 import org.unicode.cldr.web.SurveyMain.Phase; 48 import org.unicode.cldr.web.SurveyMain.UserLocaleStuff; 49 import org.unicode.cldr.web.UserRegistry.LogoutException; 50 import org.unicode.cldr.web.UserRegistry.User; 51 import org.w3c.dom.Document; 52 53 import com.ibm.icu.dev.util.ElapsedTimer; 54 import com.ibm.icu.text.UnicodeSet; 55 import com.ibm.icu.util.ULocale; 56 57 /** 58 * This is the per-client context passed to basically all functions it has 59 * print*() like functions, and so can be written to. 60 */ 61 public class WebContext implements Cloneable, Appendable { 62 public static final String TMPL_PATH = "/WEB-INF/tmpl/"; 63 public static java.util.logging.Logger logger = SurveyLog.logger; 64 // USER fields 65 public SurveyMain sm = null; 66 public Document doc[] = new Document[0]; 67 private CLDRLocale locale = null; 68 public ULocale displayLocale = SurveyMain.TRANS_HINT_LOCALE; 69 public CLDRLocale docLocale[] = new CLDRLocale[0]; 70 public CookieSession session = null; 71 public ElapsedTimer reqTimer = null; 72 public Hashtable<String, Object> temporaryStuff = new Hashtable<>(); 73 public static final String CLDR_WEBCONTEXT = "cldr_webcontext"; 74 75 public static final String TARGET_ZOOMED = "CLDR-ST-ZOOMED"; 76 public static final String TARGET_EXAMPLE = "CLDR-ST-EXAMPLE"; 77 public static final String TARGET_DOCS = "CLDR-ST-DOCS"; 78 79 // private fields 80 protected Writer out = null; 81 private PrintWriter pw = null; 82 String outQuery = null; 83 TreeMap<String, String> outQueryMap = new TreeMap<>(); 84 boolean dontCloseMe = false; 85 HttpServletRequest request; 86 HttpServletResponse response; 87 88 /** 89 * 90 * @return the output PrintWriter 91 */ getOut()92 public PrintWriter getOut() { 93 return pw; 94 } 95 96 /** 97 * Flush output content. This is useful when JSPs are mixed in with servlet 98 * code. 99 * 100 * @see java.io.PrintWriter#flush() 101 */ flush()102 public void flush() { 103 pw.flush(); 104 } 105 106 /** 107 * Return the parameter map of the underlying request. 108 * 109 * @return {@link ServletRequest#getParameterMap()} 110 */ getParameterMap()111 public Map<?, ?> getParameterMap() { 112 return request.getParameterMap(); 113 } 114 115 /** 116 * Construct a new WebContext from the servlet request and response. This is 117 * the normal constructor to use when a top level servlet or JSP spins up. 118 * Embedded JSPs should use fromRequest. 119 * 120 * @see #fromRequest(ServletRequest, ServletResponse, Writer) 121 */ WebContext(HttpServletRequest irq, HttpServletResponse irs)122 public WebContext(HttpServletRequest irq, HttpServletResponse irs) throws IOException { 123 setRequestResponse(irq, irs); 124 setStream(irs.getWriter()); 125 } 126 127 /** 128 * Internal function to setup the WebContext to point at a servlet req/resp. 129 * Also registers the WebContext with the Request. 130 * 131 * @param irq 132 * @param irs 133 * @throws IOException 134 */ setRequestResponse(HttpServletRequest irq, HttpServletResponse irs)135 protected void setRequestResponse(HttpServletRequest irq, HttpServletResponse irs) throws IOException { 136 request = irq; 137 response = irs; 138 // register us - only if another webcontext is not already registered. 139 if (request.getAttribute(CLDR_WEBCONTEXT) == null) { 140 request.setAttribute(CLDR_WEBCONTEXT, this); 141 } 142 } 143 144 /** 145 * Change the output stream to a different writer. If it isn't a 146 * PrintWriter, it will be wrapped in one. The WebContext will assume it 147 * does not own the stream, and will not close it when done. 148 * 149 * @param w 150 */ setStream(Writer w)151 protected void setStream(Writer w) { 152 out = w; 153 if (out instanceof PrintWriter) { 154 pw = (PrintWriter) out; 155 } else { 156 pw = new PrintWriter(out, true); 157 } 158 dontCloseMe = true; // do not close the stream if the Response owns it. 159 } 160 161 /** 162 * Extract (or create) a WebContext from a request/response. Call this from 163 * a .jsp which is embedded in survey tool to extract the WebContext object. 164 * The WebContext will have its output stream set to point to the request 165 * and response, so you can mix write calls from the JSP with ST calls. 166 * 167 * @param request 168 * @param response 169 * @param out 170 * @return the new WebContext, which was cloned from the one posted to the 171 * Request 172 * @throws IOException 173 */ fromRequest(ServletRequest request, ServletResponse response, Writer out)174 public static JspWebContext fromRequest(ServletRequest request, ServletResponse response, Writer out) throws IOException { 175 WebContext ctx = (WebContext) request.getAttribute(CLDR_WEBCONTEXT); 176 if (ctx == null) { 177 throw new InternalError("WebContext: could not load fromRequest. Are you trying to load a JSP directly?"); 178 } 179 JspWebContext subCtx = new JspWebContext(ctx); // clone the important 180 // fields.. 181 subCtx.setRequestResponse((HttpServletRequest) request, // but use the 182 // req/resp of 183 // the current 184 // situation 185 (HttpServletResponse) response); 186 subCtx.setStream(out); 187 return subCtx; 188 } 189 190 /** 191 * Copy one WebContext to another. This is useful when you wish to create a 192 * sub-context which has a different base URL (such as for processing a 193 * certain form or widget). 194 * 195 * @param other 196 * the other WebContext to copy from 197 */ WebContext(WebContext other)198 public WebContext(WebContext other) { 199 if ((other instanceof URLWebContext) && !(this instanceof URLWebContext)) { 200 throw new InternalError("Can't slice a URLWebContext - use clone()"); 201 } 202 init(other); 203 } 204 205 /** 206 * get a field's value as a boolean 207 * 208 * @param x 209 * field name 210 * @param def 211 * default value if field is not found. 212 * @return the field value 213 */ fieldBool(String x, boolean def)214 boolean fieldBool(String x, boolean def) { 215 if (field(x).length() > 0) { 216 if (field(x).charAt(0) == 't') { 217 return true; 218 } else { 219 return false; 220 } 221 } else { 222 return def; 223 } 224 } 225 226 /** 227 * get a field's value as an integer, or -1 if not found 228 * 229 * @param x 230 * field name 231 * @return the field's value as an integer, or -1 if it was not found 232 */ fieldInt(String x)233 public final int fieldInt(String x) { 234 return fieldInt(x, -1); 235 } 236 237 /** 238 * get a field's value, or the default 239 * 240 * @param x 241 * field name 242 * @param def 243 * default value 244 * @return the field's value as an integer, or the default value if the 245 * field was not found. 246 */ fieldInt(String x, int def)247 public int fieldInt(String x, int def) { 248 String f; 249 if ((f = field(x)).length() > 0) { 250 try { 251 return new Integer(f).intValue(); 252 } catch (Throwable t) { 253 return def; 254 } 255 } else { 256 return def; 257 } 258 } 259 260 /** 261 * Return true if the field is present 262 * 263 * @param x 264 * field name 265 * @return true if the field is present 266 */ hasField(String x)267 public boolean hasField(String x) { 268 return (request.getParameter(x) != null); 269 } 270 271 /** 272 * return a field's value, else "" 273 * 274 * @param x 275 * field name 276 * @return the field value, or else "" 277 */ field(String x)278 public final String field(String x) { 279 return field(x, ""); 280 } 281 282 /** 283 * return a field's value, else default 284 * 285 * @param x 286 * field name 287 * @param def 288 * default value 289 * @return the field's value as a string, otherwise the default 290 */ field(String x, String def)291 public String field(String x, String def) { 292 if (request == null) { 293 return def; // support testing 294 } 295 296 String res = request.getParameter(x); 297 if (res == null) { 298 return def; // don't try to transcode null. 299 } 300 return decodeFieldString(res); 301 } 302 303 /* 304 * Decode a single string from URL format into Unicode 305 * 306 * @param res UTF-8 'encoded' bytes (expanded to a string) 307 * 308 * @return Unicode string (will return 'res' if no high bits were detected) 309 */ decodeFieldString(String res)310 public static String decodeFieldString(String res) { 311 if (res == null) 312 return null; 313 byte asBytes[] = new byte[res.length()]; 314 boolean wasHigh = false; 315 int n; 316 for (n = 0; n < res.length(); n++) { 317 asBytes[n] = (byte) (res.charAt(n) & 0x00FF); 318 // println(" n : " + (int)asBytes[n] + " .. "); 319 if (asBytes[n] < 0) { 320 wasHigh = true; 321 } 322 } 323 if (wasHigh == false) { 324 return res; // no utf-8 325 } else { 326 // println("[ trying to decode on: " + res + "]"); 327 } 328 try { 329 res = new String(asBytes, "UTF-8"); 330 } catch (Throwable t) { 331 return res; 332 } 333 334 return res; 335 } 336 337 // preference api 338 /** 339 * get a preference's value as a boolean. defaults to false. 340 * 341 * @param x 342 * pref name 343 * @return preference value (or false) 344 */ prefBool(String x)345 public boolean prefBool(String x) { 346 return prefBool(x, false); 347 } 348 349 /** 350 * Get a preference's value as an integer. Defaults to 'def' 351 * 352 * @param x 353 * field name 354 * @param def 355 * default value 356 * @return the prefence's value 357 */ prefInt(String x, int def)358 int prefInt(String x, int def) { 359 String f; 360 if ((f = pref(x, "")).length() > 0) { 361 try { 362 return new Integer(f).intValue(); 363 } catch (Throwable t) { 364 return def; 365 } 366 } else { 367 return def; 368 } 369 } 370 371 /** 372 * Get a preference's value as an integer, or else -1 373 * 374 * @param x 375 * field name 376 * @return preference value or -1 377 */ prefInt(String x)378 int prefInt(String x) { 379 return prefInt(x, -1); 380 } 381 382 int codesPerPage = -1; 383 384 /** 385 * Special preference: Number of codes to show on a single page 386 * 387 * @return The preferred value (minimum: 5) 388 * @see SurveyMain#CODES_PER_PAGE 389 */ prefCodesPerPage()390 int prefCodesPerPage() { 391 if (codesPerPage == -1) { 392 codesPerPage = prefInt(SurveyMain.PREF_CODES_PER_PAGE, SurveyMain.CODES_PER_PAGE); 393 codesPerPage = Math.max(codesPerPage, 5); 394 } 395 return codesPerPage; 396 } 397 398 /** 399 * get a preference's value as a boolean. defaults to defVal. 400 * 401 * @param x 402 * preference name 403 * @param defVal 404 * default value 405 * @return the preference value 406 */ prefBool(String x, boolean defVal)407 boolean prefBool(String x, boolean defVal) { 408 if (session == null) { 409 return defVal; 410 } 411 boolean ret = fieldBool(x, session.prefGetBool(x, defVal)); 412 session.prefPut(x, ret); 413 return ret; 414 } 415 416 /** 417 * get a pref that is a string, 418 * 419 * @param x 420 * the field name and pref name 421 * @return string preference value or "" if otherwise not found. 422 */ pref(String x)423 String pref(String x) { 424 return pref(x, ""); 425 } 426 427 /** 428 * get a pref that is a string, 429 * 430 * @param x 431 * the field name and pref name 432 * @param def 433 * default value 434 * @return pref value or def 435 */ pref(String x, String def)436 String pref(String x, String def) { 437 String ret = field(x, session.prefGet(x)); 438 if (ret != null) { 439 session.prefPut(x, ret); 440 } 441 if ((ret == null) || (ret.length() <= 0)) { 442 ret = def; 443 } 444 return ret; 445 } 446 447 /** 448 * Get the target keyword and value for an 'a href' HTML tag 449 * 450 * @param target 451 * the target name to use 452 * @return the 'target=...' string - may be blank if the user has requested 453 * no popups 454 */ atarget(String t)455 public String atarget(String t) { 456 if (prefBool(SurveyMain.PREF_NOPOPUPS)) { 457 return ""; 458 } else { 459 return "target='" + t + "' "; 460 } 461 } 462 463 /** 464 * Get the target keyword and value for an 'a href' HTML tag on 465 * TARGET_ZOOMED 466 * 467 * @return the 'target=...' string - may be blank if the user has requested 468 * no popups 469 * @see #TARGET_ZOOMED 470 */ atarget()471 public String atarget() { 472 return atarget(TARGET_ZOOMED); 473 } 474 475 /** 476 * Add a parameter to the output URL 477 * 478 * @param k 479 * key 480 * @param v 481 * value 482 */ addQuery(String k, String v)483 public void addQuery(String k, String v) { 484 outQueryMap.put(k, v); 485 if (outQuery == null) { 486 outQuery = k + "=" + v; 487 } else { 488 outQuery = outQuery + "&" + k + "=" + v; 489 } 490 } 491 492 /** 493 * Copy from request queries to output query 494 */ addAllParametersAsQuery()495 public void addAllParametersAsQuery() { 496 for (Entry<String, String[]> e : request.getParameterMap().entrySet()) { 497 addQuery(e.getKey(), e.getValue()[0]); 498 } 499 } 500 501 /** 502 * Add a boolean parameter to the output URL as 't' or 'f' 503 * 504 * @param k 505 * key 506 * @param v 507 * value 508 */ addQuery(String k, boolean v)509 void addQuery(String k, boolean v) { 510 addQuery(k, v ? "t" : "f"); 511 } 512 513 /** 514 * Set a parameter on the output URL, replacing an existing value if any 515 * 516 * @param k 517 * key 518 * @param v 519 * value 520 */ setQuery(String k, String v)521 public void setQuery(String k, String v) { 522 if (outQueryMap.get(k) == null) { // if it wasn't there.. 523 addQuery(k, v); // then do a simple append 524 } else { 525 // rebuild query string: 526 outQuery = null; 527 TreeMap<String, String> oldMap = outQueryMap; 528 oldMap.put(k, v); // replace 529 outQueryMap = new TreeMap<>(); 530 for (Iterator<String> i = oldMap.keySet().iterator(); i.hasNext();) { 531 String somek = i.next(); 532 addQuery(somek, oldMap.get(somek)); 533 } 534 } 535 } 536 537 /** 538 * Set a query from an integer 539 * 540 * @param k 541 * @param v 542 */ setQuery(String k, int v)543 public void setQuery(String k, int v) { 544 setQuery(k, new Integer(v).toString()); 545 } 546 547 /** 548 * Remove the specified key from the query. Has no effect if the field 549 * doesn't exist. 550 * 551 * @param k 552 * key 553 */ removeQuery(String k)554 public void removeQuery(String k) { 555 if (outQueryMap.get(k) != null) { // if it was there.. 556 // rebuild query string: 557 outQuery = null; 558 TreeMap<String, String> oldMap = outQueryMap; 559 oldMap.remove(k); // replace 560 outQueryMap = new TreeMap<>(); 561 for (Iterator<String> i = oldMap.keySet().iterator(); i.hasNext();) { 562 String somek = i.next(); 563 addQuery(somek, oldMap.get(somek)); 564 } 565 } 566 } 567 568 /** 569 * Return the output URL 570 * 571 * @return the output URL 572 */ url()573 public String url() { 574 if (outQuery == null) { 575 return base(); 576 } else { 577 return base() + "?" + outQuery; 578 } 579 } 580 581 /** 582 * Return the raw query string, or null 583 * @return 584 */ query()585 public String query() { 586 return outQuery; 587 } 588 589 /** 590 * Returns the string that must be appended to the URL to start the next 591 * parameter - either ? or & 592 * 593 * @return the connecting string 594 */ urlConnector()595 public final String urlConnector() { 596 return (url().indexOf('?') != -1) ? "&" : "?"; 597 } 598 599 /** 600 * Get the base URL (servlet path) 601 * 602 * @return the servlet path in context 603 */ base()604 public String base() { 605 if (theServletPath == null) { 606 return context() + request.getServletPath(); 607 } else { 608 return context() + theServletPath; 609 } 610 } 611 vurl(CLDRLocale loc)612 public String vurl(CLDRLocale loc) { 613 return vurl(loc, null, null, null); 614 } 615 616 /** 617 * Get the new '/v' viewing URL. Note that this will include a fragment, do NOT append to the result (pass in something in queryAppend) 618 * @param loc locale to view. 619 * @param page pageID to view. Example: PageId.Africa (shouldn't be null- yet) 620 * @param strid strid to view. Example: "12345678" or null 621 * @param queryAppend this will be appended as the query. Example: "?email=foo@bar" 622 * @return 623 */ vurl(CLDRLocale loc, PageId page, String strid, String queryAppend)624 public String vurl(CLDRLocale loc, PageId page, String strid, String queryAppend) { 625 StringBuilder sb = new StringBuilder(request.getContextPath()); 626 return WebContext.appendContextVurl(sb, loc, page, strid, queryAppend).toString(); 627 } 628 appendContextVurl(StringBuilder sb, CLDRLocale loc, PageId page, String strid, String queryAppend)629 public static StringBuilder appendContextVurl(StringBuilder sb, CLDRLocale loc, PageId page, String strid, String queryAppend) { 630 631 sb.append("/v"); 632 if (queryAppend != null && !queryAppend.isEmpty()) { 633 sb.append(queryAppend); 634 } 635 sb.append('#'); // hash 636 637 // locale 638 sb.append('/'); 639 sb.append(loc.getBaseName()); 640 641 // page 642 sb.append('/'); 643 if (page != null) { 644 sb.append(page.name()); 645 } 646 if (strid != null && !strid.isEmpty()) { 647 sb.append('/'); 648 sb.append(strid); 649 } 650 651 return sb; 652 } 653 redirectToVurl(String vurl)654 public void redirectToVurl(String vurl) { 655 println("<a class='vredirect' href='" + vurl + "'>Redirecting to " + vurl + "</a>"); 656 println("<script>window.location=' " + vurl + "/'+window.location.hash.substring(1);</script>"); 657 } 658 setServletPath(String path)659 public void setServletPath(String path) { 660 theServletPath = path; 661 } 662 663 private String theServletPath = null; 664 665 /** 666 * Get the base URL for some request 667 * 668 * @param request 669 * @return base URL 670 */ base(HttpServletRequest request)671 public static String base(HttpServletRequest request) { 672 return contextBase(request) + request.getServletPath(); 673 } 674 675 /** 676 * The base not including /servlet 677 * @param request 678 * @return 679 */ contextBase(HttpServletRequest request)680 public static String contextBase(HttpServletRequest request) { 681 return schemeHostPort(request) + request.getContextPath(); 682 } 683 684 /** 685 * Get the context path 686 * 687 * @return the context path 688 */ context()689 public String context() { 690 return request.getContextPath(); 691 } 692 693 /** 694 * Get the context path for a certain resource 695 * 696 * @param s 697 * resource URL 698 * @return the context path for the specified resource 699 */ context(String s)700 public String context(String s) { 701 if (request == null) 702 return s; 703 return context(request, s); 704 } 705 706 /** 707 * Get the context path for a certain resource 708 * 709 * @param s 710 * resource URL 711 * @return the context path for the specified resource 712 */ context(HttpServletRequest request, String s)713 public static String context(HttpServletRequest request, String s) { 714 if (request == null) 715 return "/" + s; 716 return request.getContextPath() + "/" + s; 717 } 718 719 /** 720 * Get a link (HTML URL) to a JSP 721 * 722 * @param s 723 * resource to link to 724 * @return the URL suitable for HTML 725 */ jspLink(String s)726 public String jspLink(String s) { 727 return context(s) + "?a=" + base() 728 + ((outQuery != null) ? ("&" + outQuery) : ((session != null) ? ("&s=" + session.id) : "")); 729 } 730 731 /** 732 * Get a link (Text URL) to a JSP 733 * 734 * @param s 735 * resource to link to 736 * @return the URL suitable for Text 737 */ jspUrl(String s)738 public String jspUrl(String s) { 739 return context(s) + "?a=" + base() 740 + ((outQuery != null) ? ("&" + outQuery) : ((session != null) ? ("&s=" + session.id) : "")); 741 } 742 743 /** 744 * Output the full current output URL in hidden field format. 745 */ printUrlAsHiddenFields()746 void printUrlAsHiddenFields() { 747 for (Iterator<String> e = outQueryMap.keySet().iterator(); e.hasNext();) { 748 String k = e.next().toString(); 749 String v = outQueryMap.get(k).toString(); 750 print("<input type='hidden' name='" + k + "' value='" + v + "'/>"); 751 } 752 } 753 754 /** 755 * return the IP of the remote user. If they are behind a proxy, return the 756 * actual original URL. 757 * 758 * @return a URL 759 */ userIP()760 String userIP() { 761 return userIP(request); 762 } 763 764 /** 765 * return the IP of the remote user given a request. If they are behind a 766 * proxy, return the actual original URL. 767 * 768 * @param request 769 * the request to use 770 * @return a URL 771 */ userIP(HttpServletRequest request)772 public static String userIP(HttpServletRequest request) { 773 String ip = request.getHeader("x-forwarded-for"); 774 if (ip == null || ip.length() == 0) { 775 ip = request.getRemoteAddr(); 776 } 777 return ip; 778 } 779 780 /** 781 * return the hostname of the web server 782 * 783 * @return the Server Name 784 */ serverName()785 String serverName() { 786 return request.getServerName(); 787 } 788 789 /** 790 * return the hostname of the web server given a request 791 * 792 * @return the Server name 793 */ serverName(HttpServletRequest request)794 static String serverName(HttpServletRequest request) { 795 return request.getServerName(); 796 } 797 798 /** 799 * Returns the host:port of the server 800 * 801 * @return the "host:port: 802 */ serverHostport()803 String serverHostport() { 804 return serverHostport(request); 805 } 806 807 /** 808 * Returns the host:port of the server 809 * 810 * @param request 811 * a specific request 812 * @return the "host:port: 813 */ serverHostport(HttpServletRequest request)814 static String serverHostport(HttpServletRequest request) { 815 int port = request.getServerPort(); 816 String scheme = request.getScheme(); 817 if (port == 80 && "http".equals(scheme)) { 818 return serverName(request); 819 } else if (port == 443 && "https".equals(scheme)) { 820 return serverName(request); 821 } else { 822 return serverName(request) + ":" + port; 823 } 824 } 825 826 /** 827 * Returns the scheme://host:port 828 * 829 * @return the "scheme://host:port" 830 */ schemeHostPort()831 String schemeHostPort() { 832 return schemeHostPort(request); 833 } 834 835 /** 836 * Returns the scheme://host:port 837 * 838 * @return the "scheme://host:port" 839 * @param request 840 * the request portion 841 */ schemeHostPort(HttpServletRequest request)842 static String schemeHostPort(HttpServletRequest request) { 843 return request.getScheme() + "://" + serverHostport(request); 844 } 845 846 /** 847 * Print out a line 848 * 849 * @param s 850 * line to print 851 * @see PrintWriter#println(String) 852 */ println(String s)853 public final void println(String s) { 854 pw.println(s); 855 } 856 857 /** 858 * @param s 859 * @see PrintWriter#print(String) 860 */ print(String s)861 public final void print(String s) { 862 pw.print(s); 863 } 864 865 /** 866 * Print out a Throwable as HTML. 867 * 868 * @param t 869 * throwable to print 870 */ print(Throwable t)871 void print(Throwable t) { 872 print("<pre style='border: 2px dashed red; margin: 1em; padding: 1'>" + t.toString() + "<br />"); 873 StringWriter asString = new StringWriter(); 874 if (t instanceof SQLException) { 875 println("SQL: " + DBUtils.unchainSqlException((SQLException) t)); 876 } else { 877 t.printStackTrace(new PrintWriter(asString)); 878 } 879 print(asString.toString()); 880 print("</pre>"); 881 } 882 883 /** 884 * Send the user to another URL. Won't work if there was already some 885 * output. 886 * 887 * @param where 888 * @see HttpServletResponse#sendRedirect(String) 889 */ redirect(String where)890 void redirect(String where) { 891 try { 892 response.sendRedirect(where); 893 out.close(); 894 close(); 895 } catch (IOException ioe) { 896 throw new RuntimeException(ioe.toString() + " while redirecting to " + where); 897 } 898 } 899 900 /** 901 * Close the stream. Normally not called directly, except in outermost 902 * processor. 903 * 904 * @throws IOException 905 */ close()906 void close() throws IOException { 907 if (!dontCloseMe) { 908 out.close(); 909 out = null; 910 } else { 911 closeUserFile(); 912 } 913 } 914 915 // doc api 916 917 /** 918 * the current processor 919 * 920 * @see DisplayAndInputProcessor 921 */ 922 public DisplayAndInputProcessor processor = null; 923 924 /** 925 * Set this context to be handling a certain locale 926 * 927 * @param l 928 * locale to set 929 */ setLocale(CLDRLocale l)930 public void setLocale(CLDRLocale l) { 931 if (!SurveyMain.getLocalesSet().contains(l)) { // bogus 932 locale = null; 933 return; 934 } 935 locale = l; 936 // localeString = locale.getBaseName(); 937 processor = new DisplayAndInputProcessor(l, false); 938 Vector<CLDRLocale> localesVector = new Vector<>(); 939 for (CLDRLocale parents : locale.getParentIterator()) { 940 localesVector.add(parents); 941 } 942 docLocale = localesVector.toArray(docLocale); 943 // logger.info("NOT NOT NOT fetching locale: " + l.toString() + 944 // ", count: " + doc.length); 945 } 946 947 /** 948 * Cached direction of this locale. 949 */ 950 private HTMLDirection direction = null; 951 952 /** 953 * Return the HTML direction of this locale, ltr or rtl. Returns ltr by 954 * default. TODO: should return display locale's directionality by default. 955 * 956 * @return directionality 957 */ getDirectionForLocale()958 public HTMLDirection getDirectionForLocale() { 959 if ((locale != null) && (direction == null)) { 960 direction = sm.getHTMLDirectionFor(getLocale()); 961 } 962 if (direction == null) { 963 return HTMLDirection.LEFT_TO_RIGHT; 964 } else { 965 return direction; 966 } 967 } 968 969 /** 970 * Return the current locale as a string. Deprecated, please use getLocale 971 * instead. 972 * 973 * @deprecated use getLocale().toString() - 974 * @see #getLocale() 975 */ 976 @Deprecated localeString()977 public final String localeString() { 978 if (locale == null) { 979 throw new InternalError("localeString is null, locale=" + locale); 980 } 981 return locale.toString(); 982 } 983 984 /** 985 * Print the coverage level for a certain locale. 986 */ showCoverageLevel()987 public void showCoverageLevel() { 988 String itsLevel = getEffectiveCoverageLevel(); 989 String recLevel = getRecommendedCoverageLevel(); 990 String def = getRequiredCoverageLevel(); 991 if (def.equals(COVLEV_RECOMMENDED)) { 992 print("Coverage Level: <tt class='codebox'>" + itsLevel.toString() + "</tt><br>"); 993 } else { 994 print("Coverage Level: <tt class='codebox'>" + def + "</tt> (overriding <tt>" + itsLevel.toString() + "</tt>)<br>"); 995 } 996 print("Recommended level: <tt class='codebox'>" + recLevel.toString() + "</tt><br>"); 997 print("<ul><li>To change your default coverage level, see "); 998 SurveyMain.printMenu(this, "", "options", "My Options", SurveyMain.QUERY_DO); 999 println("</li></ul>"); 1000 } 1001 getEffectiveCoverageLevel(CLDRLocale locale)1002 public String getEffectiveCoverageLevel(CLDRLocale locale) { 1003 return getEffectiveCoverageLevel(locale.getBaseName()); 1004 } 1005 getEffectiveCoverageLevel(String locale)1006 public String getEffectiveCoverageLevel(String locale) { 1007 String level = getRequiredCoverageLevel(); 1008 if ((level == null) || (level.equals(COVLEV_RECOMMENDED)) || (level.equals("default"))) { 1009 // fetch from org 1010 level = session.getOrgCoverageLevel(locale); 1011 } 1012 return level; 1013 } 1014 getRecommendedCoverageLevel()1015 public String getRecommendedCoverageLevel() { 1016 String myOrg = session.getUserOrg(); 1017 if ((myOrg == null) || !isCoverageOrganization(myOrg)) { 1018 return COVLEV_DEFAULT_RECOMMENDED_STRING; 1019 } else { 1020 CLDRLocale loc = getLocale(); 1021 if (loc != null) { 1022 Level lev = StandardCodes.make().getLocaleCoverageLevel(myOrg, loc.toString()); 1023 if (lev == Level.UNDETERMINED) { 1024 lev = COVLEVEL_DEFAULT_RECOMMENDED; 1025 } 1026 return lev.toString(); 1027 } else { 1028 return COVLEVEL_DEFAULT_RECOMMENDED.toString(); 1029 } 1030 } 1031 } 1032 showCoverageSetting()1033 public String showCoverageSetting() { 1034 String rv = sm.showListSetting(this, SurveyMain.PREF_COVLEV, "Coverage", WebContext.PREF_COVLEV_LIST); 1035 return rv; 1036 } 1037 showCoverageSettingForLocale()1038 public String showCoverageSettingForLocale() { 1039 String rv = sm.showListSetting(this, SurveyMain.PREF_COVLEV, "Coverage", WebContext.PREF_COVLEV_LIST, 1040 getRecommendedCoverageLevel()); 1041 return rv; 1042 } 1043 getCoverageSetting()1044 public String getCoverageSetting() { 1045 return sm.getListSetting(this, SurveyMain.PREF_COVLEV, WebContext.PREF_COVLEV_LIST, false); 1046 } 1047 1048 public static final String COVLEV_RECOMMENDED = "default"; 1049 public static final String PREF_COVLEV_LIST[] = { COVLEV_RECOMMENDED, 1050 Level.COMPREHENSIVE.toString(), Level.MODERN.toString(), Level.MODERATE.toString(), Level.BASIC.toString() }; 1051 1052 /** 1053 * The default level, if no organization is available. 1054 */ 1055 public static final Level COVLEVEL_DEFAULT_RECOMMENDED = org.unicode.cldr.util.Level.MODERN; 1056 public static final String COVLEV_DEFAULT_RECOMMENDED_STRING = COVLEVEL_DEFAULT_RECOMMENDED.name().toLowerCase(); 1057 1058 /** 1059 * Is it an organization that participates in coverage? 1060 * 1061 * @param org the name of the organization 1062 * @return true if the organization participates in coverage, else false 1063 */ isCoverageOrganization(String org)1064 public static boolean isCoverageOrganization(String org) { 1065 return (org != null && StandardCodes.make().getLocaleCoverageOrganizationStrings().contains(org.toLowerCase())); 1066 } 1067 1068 /** 1069 * Get a list of all locale types 1070 * 1071 * @return a list of locale types 1072 * @see StandardCodes#getLocaleCoverageOrganizations() 1073 */ getLocaleCoverageOrganizations()1074 static String[] getLocaleCoverageOrganizations() { 1075 Set<Organization> set = StandardCodes.make().getLocaleCoverageOrganizations(); 1076 Organization[] orgArray = set.toArray(new Organization[0]); 1077 String[] stringArray = new String[orgArray.length]; 1078 int i = 0; 1079 for (Organization org : orgArray) { 1080 stringArray[i++] = org.toString(); 1081 } 1082 return stringArray; 1083 // There was ArrayStoreException here: 1084 // return StandardCodes.make().getLocaleCoverageOrganizations().toArray(new String[0]); 1085 } 1086 1087 /** 1088 * Append the WebContext Options map to the specified map 1089 * 1090 * @return the map 1091 */ getOptionsMap()1092 public CheckCLDR.Options getOptionsMap() { 1093 String def = getRequiredCoverageLevel(); 1094 String org = getEffectiveCoverageLevel(); 1095 1096 return new CheckCLDR.Options(getLocale(), SurveyMain.getTestPhase(), def, org); 1097 } 1098 1099 /** 1100 * @return 1101 */ getEffectiveCoverageLevel()1102 public String getEffectiveCoverageLevel() { 1103 String org = getEffectiveCoverageLevel(getLocale().toString()); 1104 return org; 1105 } 1106 1107 /** 1108 * @return 1109 */ getRequiredCoverageLevel()1110 public String getRequiredCoverageLevel() { 1111 String def = sm.getListSetting(this, SurveyMain.PREF_COVLEV, WebContext.PREF_COVLEV_LIST, false); 1112 return def; 1113 } 1114 1115 public enum LoadingShow { 1116 dontShowLoading, showLoading 1117 } 1118 1119 private static final boolean CACHE_DATA_SECTION = false; // TESTING, not ready for use 1120 1121 private static final Map<String, DataSection> dataSectionCache = CACHE_DATA_SECTION ? new ConcurrentHashMap<>() : null; 1122 1123 /** 1124 * Get a DataSection 1125 * 1126 * @param prefix a string such as "//ldml"; or null 1127 * @param matcher the XPathMatcher (which is ... ?); or null 1128 * @param pageId the PageId, with a name such as "Generic" and a SectionId with a name such as "DateTime"; or null 1129 * @return the DataSection 1130 * 1131 * Called only by SurveyAjax.getRow, twice: 1132 * ctx.getDataSection(null [prefix], null [matcher], pageId); 1133 * ctx.getDataSection(baseXp [prefix], matcher, null [pageId]); 1134 * 1135 * TODO: as part of DataSection performance improvement, consider moving this code to a different 1136 * module, maybe DataSection itself, especially if we can make DataSection not be user-specific. 1137 * WebContext is user-specific, and even request-specific. 1138 * 1139 * Renamed 2019-05-15 from getSection (5 args) to getDataSection 1140 * 1141 * Reference: https://unicode-org.atlassian.net/browse/CLDR-12020 1142 */ getDataSection(String prefix, XPathMatcher matcher, PageId pageId)1143 public DataSection getDataSection(String prefix, XPathMatcher matcher, PageId pageId) { 1144 1145 DataSection section = null; 1146 synchronized (this) { 1147 String cacheKey = null; 1148 if (CACHE_DATA_SECTION) { 1149 cacheKey = locale.toString(); // not enough! 1150 section = dataSectionCache.get(cacheKey); 1151 } 1152 if (section == null) { 1153 CLDRProgressTask progress = sm.openProgress("Loading"); 1154 try { 1155 progress.update("<span title='" + sm.xpt.getPrettyPath(prefix) + "'>" + locale + "</span>"); 1156 flush(); 1157 synchronized (session) { 1158 section = DataSection.make(pageId, this /* ctx */, this.session, locale, prefix, matcher); 1159 } 1160 } finally { 1161 progress.close(); // TODO: this can trigger "State Error: Closing an already-closed CLDRProgressIndicator" 1162 } 1163 if (section == null) { 1164 throw new InternalError("No section."); 1165 } 1166 if (CACHE_DATA_SECTION) { 1167 dataSectionCache.put(cacheKey, section); 1168 } 1169 } 1170 } 1171 return section; 1172 } 1173 1174 // Internal Utils 1175 1176 static final HelpMessages surveyToolHelpMessages = new HelpMessages("test_help_messages.html"); 1177 public static final String CAN_MODIFY = "canModify"; 1178 public static final String DATA_SECTION = "DataSection"; 1179 public static final String ZOOMED_IN = "zoomedIn"; 1180 public static final String DATA_ROW = "DataRow"; 1181 public static final String BASE_EXAMPLE = "baseExample"; 1182 public static final String BASE_VALUE = "baseValue"; 1183 1184 /** 1185 * Print a link to help with a specified title 1186 * 1187 * @param what 1188 * the help to link to 1189 * @param title 1190 * the title of the help 1191 */ printHelpLink(String what, String title)1192 public void printHelpLink(String what, String title) { 1193 printHelpLink(what, title, true); 1194 } 1195 1196 /** 1197 * @param what 1198 * @param title 1199 * @param doEdit 1200 * @deprecated editing is deprecated 1201 */ 1202 @Deprecated printHelpLink(String what, String title, boolean doEdit)1203 public void printHelpLink(String what, String title, boolean doEdit) { 1204 printHelpLink(what, title, doEdit, true); 1205 } 1206 1207 /** 1208 * @deprecated editing is deprecated 1209 * @param what 1210 * @param title 1211 * @param doEdit 1212 * @param parens 1213 */ 1214 @Deprecated printHelpLink(String what, String title, boolean doEdit, boolean parens)1215 private void printHelpLink(String what, String title, boolean doEdit, boolean parens) { 1216 if (parens) { 1217 print("("); 1218 } 1219 print("<a href=\"" + (SurveyMain.CLDR_HELP_LINK) + what + "\">" + title + "</a>"); 1220 if (parens) { 1221 println(")"); 1222 } 1223 } 1224 1225 /** 1226 * Get HTML for the 'modify' thing, with the hand icon 1227 * 1228 * @param message 1229 * @see #iconHtml(String, String) 1230 * @return HTML for the message 1231 */ modifyThing(String message)1232 public String modifyThing(String message) { 1233 return iconHtml("hand", message); 1234 } 1235 1236 /** 1237 * get HTML for an icon with a certain message 1238 * 1239 * @param icon 1240 * @param message 1241 * @return the HTML for the icon and message 1242 */ iconHtml(String icon, String message)1243 public String iconHtml(String icon, String message) { 1244 return iconHtml(request, icon, message); 1245 } 1246 iconHtml(HttpServletRequest request, String icon, String message)1247 public static String iconHtml(HttpServletRequest request, String icon, String message) { 1248 if (message == null) { 1249 message = "[" + icon + "]"; 1250 } 1251 return "<img alt='[" + icon + "]' style='width: 16px; height: 16px; border: 0;' src='" + context(request, icon + ".png") 1252 + "' title='" + message + "' />"; 1253 } 1254 1255 /** 1256 * Clone (copy construct) the context 1257 * 1258 * @see #WebContext(WebContext) 1259 */ 1260 @Override clone()1261 public Object clone() { 1262 Object o; 1263 try { 1264 o = super.clone(); 1265 } catch (CloneNotSupportedException e) { 1266 throw new InternalError(); 1267 } 1268 WebContext n = (WebContext) o; 1269 n.init(this); 1270 1271 return o; 1272 } 1273 init(WebContext other)1274 private void init(WebContext other) { 1275 doc = other.doc; 1276 docLocale = other.docLocale; 1277 displayLocale = other.displayLocale; 1278 out = other.out; 1279 pw = other.pw; 1280 outQuery = other.outQuery; 1281 locale = other.locale; 1282 session = other.session; 1283 outQueryMap = (TreeMap<String, String>) other.outQueryMap.clone(); 1284 dontCloseMe = true; 1285 request = other.request; 1286 response = other.response; 1287 sm = other.sm; 1288 processor = other.processor; 1289 temporaryStuff = other.temporaryStuff; 1290 theServletPath = other.theServletPath; 1291 } 1292 1293 /** 1294 * Include a template fragment from /WEB-INF/tmpl 1295 * 1296 * @param filename 1297 */ includeFragment(String filename)1298 public void includeFragment(String filename) { 1299 try { 1300 WebContext.includeFragment(request, response, filename); 1301 } catch (Throwable t) { 1302 SurveyLog.logException(t, "Including template " + filename); 1303 this.println("<div class='ferrorbox'><B>Error</b> while including template <tt class='code'>" + filename 1304 + "</tt>:<br>"); 1305 this.print(t); 1306 this.println("</div>"); 1307 System.err.println("While expanding " + TMPL_PATH + filename + ": " + t.toString()); 1308 t.printStackTrace(); 1309 } 1310 } 1311 1312 /** 1313 * Include a template fragment from /WEB-INF/tmpl 1314 * 1315 * @param request 1316 * @param response 1317 * @param filename 1318 * @throws ServletException 1319 * @throws IOException 1320 * , NullPointerException 1321 */ includeFragment(HttpServletRequest request, HttpServletResponse response, String filename)1322 public static void includeFragment(HttpServletRequest request, HttpServletResponse response, String filename) 1323 throws ServletException, IOException { 1324 RequestDispatcher dp = request.getRequestDispatcher(TMPL_PATH + filename); 1325 dp.include(request, response); 1326 } 1327 1328 /** 1329 * Put something into the temporary (context, non session data) store 1330 * 1331 * @param string 1332 * @param object 1333 */ put(String string, Object object)1334 public void put(String string, Object object) { 1335 if (object == null) { 1336 temporaryStuff.remove(string); 1337 } else { 1338 temporaryStuff.put(string, object); 1339 } 1340 } 1341 1342 /** 1343 * Get something from the temporary (context, non session data) store 1344 * 1345 * @param string 1346 * @return the object 1347 */ get(String string)1348 public Object get(String string) { 1349 return temporaryStuff.get(string); 1350 } 1351 getString(String k)1352 public String getString(String k) { 1353 return (String) get(k); 1354 } 1355 1356 /** 1357 * @return the CLDRLocale with which this WebContext currently pertains. 1358 * @see CLDRLocale 1359 */ getLocale()1360 public CLDRLocale getLocale() { 1361 return locale; 1362 } 1363 1364 // Display Context Data 1365 protected Boolean canModify = null; 1366 1367 /** 1368 * A direction, suitable for html 'dir=...' 1369 * 1370 * @author srl 1371 * @see getDirectionForLocale 1372 */ 1373 public enum HTMLDirection { 1374 LEFT_TO_RIGHT("ltr"), RIGHT_TO_LEFT("rtl"); 1375 1376 private String str; 1377 HTMLDirection(String str)1378 HTMLDirection(String str) { 1379 this.str = str; 1380 } 1381 1382 @Override toString()1383 public String toString() { 1384 return str; 1385 } 1386 1387 /** 1388 * Convert a CLDR direction to an enum 1389 * 1390 * @param dir 1391 * CLDR direction string 1392 * @return HTML direction enum 1393 */ fromCldr(String dir)1394 public static HTMLDirection fromCldr(String dir) { 1395 if (dir.equals("left-to-right")) { 1396 return HTMLDirection.LEFT_TO_RIGHT; 1397 } else if (dir.equals("right-to-left")) { 1398 return HTMLDirection.RIGHT_TO_LEFT; 1399 } else if (dir.equals("top-to-bottom")) { 1400 return HTMLDirection.LEFT_TO_RIGHT; // ! 1401 } else { 1402 return HTMLDirection.LEFT_TO_RIGHT; 1403 } 1404 } 1405 } 1406 1407 /** 1408 * Return true if the user can modify this locale 1409 * 1410 * @return true if the user can modify this locale 1411 */ canModify()1412 public Boolean canModify() { 1413 if (STFactory.isReadOnlyLocale(locale) || SurveyMain.phase() == Phase.READONLY) 1414 return (canModify = false); 1415 if (canModify == null) { 1416 if (session != null && session.user != null) { 1417 canModify = UserRegistry.userCanModifyLocale(session.user, locale); 1418 } else { 1419 canModify = false; 1420 } 1421 } 1422 return canModify; 1423 } 1424 includeAjaxScript(AjaxType type)1425 public void includeAjaxScript(AjaxType type) { 1426 try { 1427 SurveyAjax.includeAjaxScript(request, response, type); 1428 } catch (Throwable t) { 1429 this.println("<div class='ferrorbox'><B>Error</b> while expanding ajax template ajax_" + type.name().toLowerCase() 1430 + ".jsp:<br>"); 1431 this.print(t); 1432 this.println("</div>"); 1433 System.err.println("While expanding ajax: " + t.toString()); 1434 t.printStackTrace(); 1435 } 1436 } 1437 getUserFile()1438 public SurveyMain.UserLocaleStuff getUserFile() { 1439 SurveyMain.UserLocaleStuff uf = peekUserFile(); 1440 if (uf == null) { 1441 uf = sm.getUserFile(session, getLocale()); 1442 put("UserFile", uf); 1443 } 1444 return uf; 1445 } 1446 peekUserFile()1447 private UserLocaleStuff peekUserFile() { 1448 return (UserLocaleStuff) get("UserFile"); 1449 } 1450 closeUserFile()1451 public void closeUserFile() { 1452 UserLocaleStuff uf = (UserLocaleStuff) temporaryStuff.remove("UserFile"); 1453 if (uf != null) { 1454 uf.close(); 1455 } 1456 } 1457 getCLDRFile()1458 public CLDRFile getCLDRFile() { 1459 return getUserFile().cldrfile; 1460 } 1461 1462 /** 1463 * Get the user settings. 1464 * 1465 * @return 1466 */ settings()1467 UserSettings settings() { 1468 return session.settings(); 1469 } 1470 no_js_warning()1471 public void no_js_warning() { 1472 boolean no_js = prefBool(SurveyMain.PREF_NOJAVASCRIPT); 1473 if (!no_js) { 1474 WebContext nuCtx = (WebContext) clone(); 1475 nuCtx.setQuery(SurveyMain.PREF_NOJAVASCRIPT, "t"); 1476 println("<noscript><h1>"); 1477 println(iconHtml("warn", "JavaScript disabled") + "JavaScript is disabled. Please enable JavaScript.."); 1478 println("</h1></noscript>"); 1479 } 1480 } 1481 1482 /** 1483 * Return the ID of this user, or -1 (UserRegistry.NO_USER) 1484 * 1485 * @return user's id, or -1 (UserRegistry.NO_USER) if not found/not set 1486 */ userId()1487 public int userId() { 1488 if (session != null && session.user != null) { 1489 return session.user.id; 1490 } else { 1491 return UserRegistry.NO_USER; 1492 } 1493 } 1494 user()1495 private User user() { 1496 if (session != null && session.user != null) { 1497 return session.user; 1498 } else { 1499 return null; 1500 } 1501 } 1502 1503 /** 1504 * Get a certain cookie 1505 * 1506 * @param id 1507 * @return 1508 */ getCookie(String id)1509 public Cookie getCookie(String id) { 1510 return getCookie(request, id); 1511 } 1512 getCookie(HttpServletRequest request, String id)1513 public static Cookie getCookie(HttpServletRequest request, String id) { 1514 if (request == null) return null; 1515 Cookie cooks[] = request.getCookies(); 1516 if (cooks == null) 1517 return null; 1518 for (Cookie c : cooks) { 1519 if (c.getName().equals(id)) { 1520 return c; 1521 } 1522 } 1523 return null; 1524 } 1525 1526 /** 1527 * Get a cookie value or null 1528 * 1529 * @param id 1530 * @return 1531 */ getCookieValue(String id)1532 public String getCookieValue(String id) { 1533 Cookie c = getCookie(id); 1534 if (c != null) { 1535 return c.getValue(); 1536 } 1537 return null; 1538 } 1539 1540 /** 1541 * Set a cookie 1542 * 1543 * @param id 1544 * @param value 1545 * @param expiry 1546 */ addCookie(String id, String value, int expiry)1547 Cookie addCookie(String id, String value, int expiry) { 1548 Cookie c = new Cookie(id, value); 1549 c.setMaxAge(expiry); 1550 response.addCookie(c); 1551 return c; 1552 } 1553 1554 @Override append(CharSequence csq)1555 public Appendable append(CharSequence csq) throws IOException { 1556 pw.append(csq); 1557 return this; 1558 } 1559 1560 @Override append(char c)1561 public Appendable append(char c) throws IOException { 1562 pw.append(c); 1563 return this; 1564 } 1565 1566 @Override append(CharSequence csq, int start, int end)1567 public Appendable append(CharSequence csq, int start, int end) throws IOException { 1568 pw.append(csq, start, end); 1569 return this; 1570 } 1571 1572 @Override toString()1573 public String toString() { 1574 return "{ " + this.getClass().getName() + ": url=" + url() + ", ip=" + this.userIP() + ", user=" + this.user() + "}"; 1575 } 1576 1577 /** 1578 * @return 1579 */ getAlign()1580 public String getAlign() { 1581 String ourAlign = "left"; 1582 if (getDirectionForLocale().equals(HTMLDirection.RIGHT_TO_LEFT)) { 1583 ourAlign = "right"; 1584 } 1585 return ourAlign; 1586 } 1587 1588 /** 1589 * @return 1590 */ getCanModify()1591 public boolean getCanModify() { 1592 boolean canModify = (UserRegistry.userCanModifyLocale(session.user, getLocale())); 1593 return canModify; 1594 } 1595 1596 /** 1597 * @param locale 1598 * @return 1599 */ urlForLocale(CLDRLocale locale)1600 String urlForLocale(CLDRLocale locale) { 1601 return vurl(locale, null, null, null); 1602 } 1603 getLocaleDisplayName(String loc)1604 public String getLocaleDisplayName(String loc) { 1605 return getLocaleDisplayName(CLDRLocale.getInstance(loc)); 1606 } 1607 getLocaleDisplayName(CLDRLocale instance)1608 public String getLocaleDisplayName(CLDRLocale instance) { 1609 return instance.getDisplayName(); 1610 } 1611 1612 /** 1613 * @return 1614 */ hasAdminPassword()1615 public boolean hasAdminPassword() { 1616 return field("dump").equals(SurveyMain.vap); 1617 } 1618 hasTestPassword()1619 public boolean hasTestPassword() { 1620 return field("dump").equals(SurveyMain.testpw) || hasAdminPassword(); 1621 } 1622 1623 private static UnicodeSet csvSpecial = new UnicodeSet("[, \"]").freeze(); 1624 csvWrite(Writer out, String str)1625 public static final void csvWrite(Writer out, String str) throws IOException { 1626 str = str.trim(); 1627 if (csvSpecial.containsSome(str)) { 1628 out.write('"'); 1629 out.write(str.replaceAll("\"", "\"\"")); 1630 out.write('"'); 1631 } else { 1632 out.write(str); 1633 } 1634 } 1635 1636 private boolean checkedPage = false; 1637 private PageId pageId = null; 1638 getPageId()1639 public PageId getPageId() { 1640 if (!checkedPage) { 1641 String pageField = field(SurveyMain.QUERY_SECTION); 1642 pageId = getPageId(pageField); 1643 checkedPage = true; 1644 } 1645 return pageId; 1646 } 1647 1648 /** 1649 * set the session. Only call this once. 1650 */ setSession()1651 public String setSession() { 1652 if (request == null && session != null) { 1653 return "using canned session"; // already set - for testing 1654 } 1655 1656 if (this.session != null) return "Internal error - session already set."; 1657 1658 CookieSession.checkForExpiredSessions(); // If a session has expired, remove it 1659 1660 String message = null; // return message, explaining what happened with the session. 1661 1662 String myNum = field(SurveyMain.QUERY_SESSION); // s 1663 String password = field(SurveyMain.QUERY_PASSWORD); 1664 if (password.isEmpty()) { 1665 password = field(SurveyMain.QUERY_PASSWORD_ALT); 1666 } 1667 boolean letmein = SurveyMain.vap.equals(field("letmein")); // using CLDR_VAP (admin) password 1668 String email = field(SurveyMain.QUERY_EMAIL); 1669 if ("admin@".equals(email) && SurveyMain.vap.equals(password)) { 1670 letmein = true; 1671 } 1672 1673 // if there was an email/password in the cookie, use that. 1674 { 1675 String myEmail = getCookieValue(SurveyMain.QUERY_EMAIL); 1676 String myPassword = getCookieValue(SurveyMain.QUERY_PASSWORD); 1677 if (myEmail != null && (email == null || email.isEmpty())) { 1678 email = myEmail; 1679 if (myPassword != null && (password == null || password.isEmpty())) { 1680 password = myPassword; 1681 } 1682 } else { 1683 if (myEmail != null && !myEmail.equals(email)) { 1684 removeLoginCookies(request, response); 1685 } 1686 } 1687 } 1688 1689 User user = null; 1690 // if an email/password given, try to fetch a user 1691 try { 1692 user = CookieSession.sm.reg.get(password, email, userIP(), letmein); 1693 } catch (LogoutException e) { 1694 logout(); // failed login, so logout this session. 1695 } 1696 if (user != null) { 1697 user.touch(); // mark this user as active. 1698 } 1699 1700 HttpSession httpSession = request.getSession(true); // create httpsession 1701 1702 //boolean idFromSession = false; // did the id come from the httpsession? (and why do we care?) 1703 1704 if (myNum.equals(SurveyMain.SURVEYTOOL_COOKIE_NONE)) { // "0"- for testing 1705 httpSession.removeAttribute(SurveyMain.SURVEYTOOL_COOKIE_SESSION); 1706 } 1707 1708 // we just logged in- see if there's already a user session for this user.. 1709 if (user != null) { 1710 session = CookieSession.retrieveUser(user.email); // is this user already logged in? 1711 if (session != null) { 1712 if (null == CookieSession.retrieve(session.id)) { // double check- is the session still valid? 1713 session = null; // don't allow dead sessions to show up 1714 // via the user list. 1715 } 1716 } 1717 } 1718 1719 // Retreive a number from the httpSession if present 1720 if ((httpSession != null) && (session == null)) { 1721 String aNum = (String) httpSession.getAttribute(SurveyMain.SURVEYTOOL_COOKIE_SESSION); 1722 if (aNum != null) { 1723 session = CookieSession.retrieve(aNum); 1724 if (CookieSession.DEBUG_INOUT) { 1725 System.err.println("From httpsession " + httpSession.getId() + " = ST session " + aNum + " = " + session); 1726 } 1727 } 1728 } 1729 1730 if (session != null && session.user != null && user != null && user.id != session.user.id) { 1731 session = null; // user was already logged in as 'session.user', replacing this with 'user' 1732 } 1733 1734 if ((user == null) && 1735 (hasField(SurveyMain.QUERY_PASSWORD) || hasField(SurveyMain.QUERY_EMAIL))) { 1736 if (CookieSession.DEBUG_INOUT) { 1737 System.out.println("Logging out - mySession=" + session + ",user=" + user + ", and had em/pw"); 1738 } 1739 logout(); // zap cookies if some id/pw failed to work 1740 } 1741 1742 if (session == null && user == null) { 1743 session = CookieSession.checkForAbuseFrom(userIP(), SurveyMain.BAD_IPS, request.getHeader("User-Agent")); 1744 if (session != null) { 1745 println("<h1>Note: Your IP, " + userIP() + " has been throttled for making " + SurveyMain.BAD_IPS.get(userIP()) 1746 + " connections. Try turning on cookies, or obeying the 'META ROBOTS' tag.</h1>"); 1747 flush(); 1748 session = null; 1749 return "Bad IP."; 1750 } 1751 } 1752 1753 if (CookieSession.DEBUG_INOUT) System.out.println("Session Now=" + session + ", user=" + user); 1754 1755 // guest? 1756 if (letmein || (user != null && UserRegistry.userIsTC(user))) { 1757 // allow in administrator or TC. 1758 } else if ((user != null) && (session == null)) { // user trying to log in- 1759 if (CookieSession.tooManyUsers()) { 1760 System.err.println("Refused login for " + email + " from " + userIP() + " - too many users ( " + CookieSession.getUserCount() + ")"); 1761 return "We are swamped with about " + CookieSession.getUserCount() 1762 + " people using the SurveyTool right now! Please try back in a little while."; 1763 } 1764 } else if (session == null || (session.user == null)) { // guest user 1765 if (CookieSession.tooManyGuests()) { 1766 if (session != null) { 1767 System.err.println("Logged-out guest " + session.id + " from " + userIP() + " - too many users ( " + CookieSession.getUserCount() + ")"); 1768 session.remove(); // remove guests at this point 1769 session = null; 1770 } 1771 logout(); // clear session cookie 1772 return "We have too many people browsing the CLDR Data on the Survey Tool. Please try again later when the load has gone down."; 1773 } 1774 } 1775 1776 // New up a session, if we don't already have one. 1777 if (session == null) { 1778 session = CookieSession.newSession(user == null, userIP(), httpSession.getId()); 1779 if (!myNum.equals(SurveyMain.SURVEYTOOL_COOKIE_NONE)) { 1780 // ctx.println("New session: " + mySession.id + "<br>"); 1781 } 1782 } 1783 1784 // should we add "&s=.#####" to the URL? 1785 if (httpSession.isNew()) { // If it's a new session.. 1786 addQuery(SurveyMain.QUERY_SESSION + "__", session.id); 1787 } else { 1788 // ctx.println("['s' suppressed]"); 1789 } 1790 1791 // store the session id in the HttpSession 1792 httpSession.setAttribute(SurveyMain.SURVEYTOOL_COOKIE_SESSION, session.id); 1793 httpSession.setMaxInactiveInterval(-1); // never expire 1794 1795 if (user != null) { 1796 session.setUser(user); // this will replace any existing session by this user. 1797 session.user.ip = userIP(); 1798 } else { 1799 if ((email != null) && (email.length() > 0) && (session.user == null)) { 1800 String encodedEmail; 1801 try { 1802 encodedEmail = URLEncoder.encode(email, "UTF-8"); 1803 } catch (UnsupportedEncodingException e) { 1804 // The server doesn't support UTF-8? (Should never happen) 1805 throw new RuntimeException(e); 1806 } 1807 message = iconHtml("stop", "failed login") + "login failed. <a href='" 1808 + request.getContextPath() + "/reset.jsp" 1809 + "?email=" + encodedEmail 1810 + "&s=" + session.id 1811 + "' id='notselected'>recover password?</a><br>"; 1812 } 1813 } 1814 // processs the 'remember me' 1815 if (session != null && session.user != null) { 1816 if (hasField(SurveyMain.QUERY_SAVE_COOKIE)) { 1817 if (SurveyMain.isUnofficial()) { 1818 System.out.println("Remembering user: " + session.user); 1819 } 1820 loginRemember(session.user); 1821 } 1822 } 1823 return message; 1824 } 1825 1826 /** 1827 * Logout this ctx 1828 */ logout()1829 public void logout() { 1830 logout(request, response); 1831 } 1832 1833 /** 1834 * Logout this req/response (zap cookie) 1835 * Zaps http session 1836 * @param request 1837 * @param response 1838 */ logout(HttpServletRequest request, HttpServletResponse response)1839 public static void logout(HttpServletRequest request, HttpServletResponse response) { 1840 HttpSession session = request.getSession(false); // dont create 1841 if (session != null) { 1842 String sessionId = (String) session.getAttribute(SurveyMain.SURVEYTOOL_COOKIE_SESSION); 1843 if (CookieSession.DEBUG_INOUT) { 1844 System.err.println("logout() of session " + session.getId() + " and cookieSession " + sessionId); 1845 } 1846 if (sessionId != null) { 1847 CookieSession sess = CookieSession.retrieveWithoutTouch(sessionId); 1848 if (sess != null) { 1849 sess.remove(); // forcibly remove session 1850 } 1851 } 1852 session.removeAttribute(SurveyMain.SURVEYTOOL_COOKIE_SESSION); 1853 } 1854 removeLoginCookies(request, response); 1855 } 1856 1857 /** 1858 * @param request 1859 * @param response 1860 */ removeLoginCookies(HttpServletRequest request, HttpServletResponse response)1861 public static void removeLoginCookies(HttpServletRequest request, HttpServletResponse response) { 1862 Cookie c0 = WebContext.getCookie(request, SurveyMain.QUERY_EMAIL); 1863 if (c0 != null) { // only zap extant cookies 1864 c0.setValue(""); 1865 c0.setMaxAge(0); 1866 response.addCookie(c0); 1867 } 1868 Cookie c1 = WebContext.getCookie(request, SurveyMain.QUERY_PASSWORD); 1869 if (c1 != null) { 1870 c1.setValue(""); 1871 c1.setMaxAge(0); 1872 response.addCookie(c1); 1873 } 1874 } 1875 getPageId(String pageField)1876 public static PageId getPageId(String pageField) { 1877 if (pageField != null && !pageField.isEmpty()) { 1878 try { 1879 return PathHeader.PageId.forString(pageField); 1880 } catch (Exception e) { 1881 // ignore. 1882 } 1883 } 1884 return null; 1885 } 1886 1887 /** 1888 * Remember this login (adds cookie to ctx.response ) 1889 */ loginRemember(User user)1890 public void loginRemember(User user) { 1891 addCookie(SurveyMain.QUERY_EMAIL, user.email, SurveyMain.TWELVE_WEEKS); 1892 addCookie(SurveyMain.QUERY_PASSWORD, user.password, SurveyMain.TWELVE_WEEKS); 1893 } 1894 } 1895