1 // UserRegistry.java 2 // 3 // Created by Steven R. Loomis on 14/10/2005. 4 // Copyright 2005-2013 IBM. All rights reserved. 5 // 6 7 package org.unicode.cldr.web; 8 9 import java.io.BufferedReader; 10 import java.io.File; 11 import java.io.FileNotFoundException; 12 import java.io.FileOutputStream; 13 import java.io.FileReader; 14 import java.io.IOException; 15 import java.io.OutputStreamWriter; 16 import java.io.PrintWriter; 17 import java.io.UnsupportedEncodingException; 18 import java.sql.Connection; 19 import java.sql.PreparedStatement; 20 import java.sql.ResultSet; 21 import java.sql.SQLException; 22 import java.sql.Statement; 23 import java.util.Date; 24 import java.util.HashMap; 25 import java.util.HashSet; 26 import java.util.LinkedList; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.TreeMap; 31 import java.util.TreeSet; 32 33 import org.apache.commons.codec.digest.DigestUtils; 34 import org.json.JSONException; 35 import org.json.JSONObject; 36 import org.json.JSONString; 37 import org.unicode.cldr.test.CheckCLDR.Phase; 38 import org.unicode.cldr.util.CLDRConfig; 39 import org.unicode.cldr.util.CLDRConfig.Environment; 40 import org.unicode.cldr.util.CLDRInfo.UserInfo; 41 import org.unicode.cldr.util.CLDRLocale; 42 import org.unicode.cldr.util.Organization; 43 import org.unicode.cldr.util.VoteResolver; 44 import org.unicode.cldr.util.VoteResolver.Level; 45 import org.unicode.cldr.util.VoteResolver.VoterInfo; 46 import org.unicode.cldr.util.XMLFileReader; 47 import org.unicode.cldr.util.XPathParts; 48 49 import com.ibm.icu.dev.util.ElapsedTimer; 50 import com.ibm.icu.lang.UCharacter; 51 import com.ibm.icu.util.ULocale; 52 53 /** 54 * This class represents the list of all registered users. It contains an inner 55 * class, UserRegistry.User, which represents an individual user. 56 * 57 * @see UserRegistry.User 58 * @see OldUserRegistry 59 **/ 60 public class UserRegistry { 61 62 /** 63 * Special constant for specifying access to all locales. 64 */ 65 public static final String ALL_LOCALES = "*"; 66 public static final String ALL_LOCALES_LIST[] = { ALL_LOCALES }; 67 /** 68 * Special constant for specifying access to no locales. Used with intlocs (not with locale access) 69 */ 70 public static final String NO_LOCALES = "none"; 71 72 /** 73 * The number of anonymous users, ANONYMOUS_USER_COUNT, limits the number of distinct values that 74 * can be added for a given locale and path as anonymous imported old losing votes. If eventually more 75 * are needed, ANONYMOUS_USER_COUNT can be increased and more anonymous users will automatically 76 * be created. 77 */ 78 private static final int ANONYMOUS_USER_COUNT = 20; 79 80 /** 81 * Thrown to indicate the caller should log out. 82 * @author srl 83 * 84 */ 85 public class LogoutException extends Exception { 86 87 /** 88 * 89 */ 90 private static final long serialVersionUID = 8960959307439428532L; 91 92 } 93 getCovGroupsForOrg(String st_org)94 static Set<String> getCovGroupsForOrg(String st_org) { 95 Connection conn = null; 96 ResultSet rs = null; 97 PreparedStatement s = null; 98 Set<String> res = new HashSet<>(); 99 100 try { 101 conn = DBUtils.getInstance().getDBConnection(); 102 s = DBUtils 103 .prepareStatementWithArgs( 104 conn, 105 "select distinct cldr_interest.forum from cldr_interest where exists (select * from cldr_users where cldr_users.id=cldr_interest.uid and cldr_users.org=?)", 106 st_org); 107 rs = s.executeQuery(); 108 while (rs.next()) { 109 res.add(rs.getString(1)); 110 } 111 return res; 112 } catch (SQLException se) { 113 SurveyLog.logException(se, "Querying cov groups for org " + st_org, null); 114 throw new InternalError("error: " + se.toString()); 115 } finally { 116 DBUtils.close(rs, s, conn); 117 } 118 } 119 anyVotesForOrg(String st_org)120 static Set<CLDRLocale> anyVotesForOrg(String st_org) { 121 // 122 Connection conn = null; 123 ResultSet rs = null; 124 PreparedStatement s = null; 125 Set<CLDRLocale> res = new HashSet<>(); 126 127 try { 128 conn = DBUtils.getInstance().getDBConnection(); 129 s = DBUtils 130 .prepareStatementWithArgs( 131 conn, 132 "select distinct " + DBUtils.Table.VOTE_VALUE + ".locale from " + DBUtils.Table.VOTE_VALUE 133 + " where exists (select * from cldr_users where " + DBUtils.Table.VOTE_VALUE + ".submitter=cldr_users.id and cldr_users.org=?)", 134 st_org); 135 rs = s.executeQuery(); 136 while (rs.next()) { 137 res.add(CLDRLocale.getInstance(rs.getString(1))); 138 } 139 return res; 140 } catch (SQLException se) { 141 SurveyLog.logException(se, "Querying voter locs for org " + st_org, null); 142 throw new InternalError("error: " + se.toString()); 143 } finally { 144 DBUtils.close(rs, s, conn); 145 } 146 } 147 148 public interface UserChangedListener { handleUserChanged(User u)149 public void handleUserChanged(User u); 150 } 151 152 private List<UserChangedListener> listeners = new LinkedList<>(); 153 addListener(UserChangedListener l)154 public synchronized void addListener(UserChangedListener l) { 155 listeners.add(l); 156 } 157 notify(User u)158 private synchronized void notify(User u) { 159 for (UserChangedListener l : listeners) { 160 l.handleUserChanged(u); 161 } 162 } 163 164 private static java.util.logging.Logger logger; 165 // user levels 166 public static final int ADMIN = VoteResolver.Level.admin.getSTLevel(); 167 /**< Administrator **/ 168 public static final int TC = VoteResolver.Level.tc.getSTLevel(); 169 /**< Technical Committee **/ 170 public static final int MANAGER = VoteResolver.Level.manager.getSTLevel(); 171 /**< manager **/ 172 public static final int EXPERT = VoteResolver.Level.expert.getSTLevel(); 173 /**< Expert Vetter **/ 174 public static final int VETTER = VoteResolver.Level.vetter.getSTLevel(); 175 /**< regular Vetter **/ 176 public static final int STREET = VoteResolver.Level.street.getSTLevel(); 177 /**< Guest Vetter **/ 178 public static final int LOCKED = VoteResolver.Level.locked.getSTLevel(); 179 /**< Locked user - can't login **/ 180 public static final int ANONYMOUS = VoteResolver.Level.anonymous.getSTLevel(); 181 /**< Anonymous user - special for imported old losing votes **/ 182 183 public static final int LIMIT_LEVEL = 10000; 184 /** max level **/ 185 public static final int NO_LEVEL = -1; 186 /** min level **/ 187 188 public static final String FOR_ADDING = "(for adding)"; 189 /** special "IP" value referring to a user being added **/ 190 private static final String INTERNAL = "INTERNAL"; 191 192 /** 193 * List of all user levels - for UI presentation 194 **/ 195 public static final int ALL_LEVELS[] = { ADMIN, TC, MANAGER, EXPERT, VETTER, STREET, LOCKED }; 196 197 /** 198 * get a level as a string - presentation form 199 **/ levelToStr(WebContext ctx, int level)200 public static String levelToStr(WebContext ctx, int level) { 201 return level + ": (" + levelAsStr(level) + ")"; 202 } 203 204 /** 205 * get just the raw level as a string 206 */ levelAsStr(int level)207 public static String levelAsStr(int level) { 208 VoteResolver.Level l = VoteResolver.Level.fromSTLevel(level); 209 if (l == null) { 210 return "??"; 211 } else { 212 return l.name().toUpperCase(); 213 } 214 } 215 216 /** 217 * The name of the user sql database 218 */ 219 public static final String CLDR_USERS = "cldr_users"; 220 public static final String CLDR_INTEREST = "cldr_interest"; 221 222 public static final String SQL_insertStmt = "INSERT INTO " + CLDR_USERS 223 + "(userlevel,name,org,email,password,locales,lastlogin) " + "VALUES(?,?,?,?,?,?,NULL)"; 224 public static final String SQL_queryStmt_FRO = "SELECT id,name,userlevel,org,locales,intlocs,lastlogin from " + CLDR_USERS 225 + " where email=? AND password=?"; 226 // ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); 227 public static final String SQL_queryIdStmt_FRO = "SELECT name,org,email,userlevel,intlocs,locales,lastlogin,password from " 228 + CLDR_USERS + " where id=?"; 229 // ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); 230 public static final String SQL_queryEmailStmt_FRO = "SELECT id,name,userlevel,org,locales,intlocs,lastlogin,password from " 231 + CLDR_USERS + " where email=?"; 232 // ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); 233 public static final String SQL_touchStmt = "UPDATE " + CLDR_USERS + " set lastlogin=CURRENT_TIMESTAMP where id=?"; 234 public static final String SQL_removeIntLoc = "DELETE FROM " + CLDR_INTEREST + " WHERE uid=?"; 235 public static final String SQL_updateIntLoc = "INSERT INTO " + CLDR_INTEREST + " (uid,forum) VALUES(?,?)"; 236 237 private UserSettingsData userSettings; 238 239 /** 240 * This nested class is the representation of an individual user. It may not 241 * have all fields filled out, if it is simply from the cache. 242 */ 243 public class User implements Comparable<User>, UserInfo, JSONString { 244 public int id; // id number 245 public int userlevel = LOCKED; // user level 246 public String password; // password 247 public String email; // 248 public String org; // organization 249 public String name; // full name 250 public java.sql.Timestamp last_connect; 251 public String locales; 252 public String intlocs = null; 253 public String ip; 254 255 private String emailmd5 = null; 256 getEmailHash()257 public String getEmailHash() { 258 if (emailmd5 == null) { 259 String newHash = DigestUtils.md5Hex(email.trim().toLowerCase()); 260 emailmd5 = newHash; 261 return newHash; 262 } 263 return emailmd5; 264 } 265 266 private UserSettings settings; 267 268 /** 269 * @deprecated may not use 270 */ 271 @Deprecated User()272 private User() { 273 this.id = -1; 274 settings = userSettings.getSettings(id); // may not use settings. 275 } 276 User(int id)277 public User(int id) { 278 this.id = id; 279 settings = userSettings.getSettings(id); 280 } 281 282 /** 283 * Get a settings object for use with this user. 284 * 285 * @return 286 */ settings()287 public UserSettings settings() { 288 return settings; 289 } 290 touch()291 public void touch() { 292 UserRegistry.this.touch(id); 293 } 294 295 @Override equals(Object other)296 public boolean equals(Object other) { 297 if (!(other instanceof User)) { 298 return false; 299 } 300 User u = (User) other; 301 return (u.id == id); 302 } 303 printPasswordLink(WebContext ctx)304 public void printPasswordLink(WebContext ctx) { 305 UserRegistry.printPasswordLink(ctx, email, password); 306 } 307 308 @Override toString()309 public String toString() { 310 return email + "(" + org + ")-" + levelAsStr(userlevel) + "#" + userlevel + " - " + name + ", locs=" + locales; 311 } 312 toHtml(User forUser)313 public String toHtml(User forUser) { 314 if (forUser == null || !userIsTC(forUser)) { 315 return "(" + org + "#" + id + ")"; 316 } else { 317 return "<a href='mailto:" + email + "'>" + name + "</a>-" + levelAsStr(userlevel).toLowerCase(); 318 } 319 } 320 toHtml()321 public String toHtml() { 322 return "<a href='mailto:" + email + "'>" + name + "</a>-" + levelAsStr(userlevel).toLowerCase(); 323 } 324 toString(User forUser)325 public String toString(User forUser) { 326 if (forUser == null || !userIsTC(forUser)) { 327 return "(" + org + "#" + id + ")"; 328 } else { 329 return email + "(" + org + ")-" + levelAsStr(userlevel) + "#" + userlevel + " - " + name; 330 } 331 } 332 333 @Override hashCode()334 public int hashCode() { 335 return id; 336 } 337 338 /** 339 * is the user interested in this locale? 340 */ interestedIn(CLDRLocale locale)341 public boolean interestedIn(CLDRLocale locale) { 342 return UserRegistry.localeMatchesLocaleList(intlocs, locale); 343 } 344 345 /** 346 * List of interest groups the user is interested in. 347 * 348 * @return list of locales, or null for ALL locales, or a 0-length list 349 * for NO locales. 350 */ getInterestList()351 public String[] getInterestList() { 352 if (userIsExpert(this)) { 353 if (intlocs == null || intlocs.length() == 0) { 354 return null; 355 } else { 356 if (intlocs.equalsIgnoreCase(NO_LOCALES)) { 357 return new String[0]; 358 } 359 if (isAllLocales(intlocs)) return null; // all = null 360 return tokenizeLocale(intlocs); 361 } 362 } else if (userIsStreet(this)) { 363 if (isAllLocales(locales)) return null; // all = null 364 return tokenizeLocale(locales); 365 } else { 366 return new String[0]; 367 } 368 } 369 370 /** 371 * @deprecated CLDR15 was a while ago. 372 * @param locale 373 * @return 374 */ 375 @Deprecated userIsSpecialForCLDR15(CLDRLocale locale)376 public final boolean userIsSpecialForCLDR15(CLDRLocale locale) { 377 return false; 378 } 379 380 // if(locale.equals("be")||locale.startsWith("be_")) { 381 // if( ( id == 315 /* V. P. */ ) || (id == 8 /* S. M. */ ) ) { 382 // return true; 383 // } else { 384 // return false; 385 // } 386 // } else if ( id == 7 ) { // Erkki 387 // return true; 388 // } else { 389 // return false; 390 // } 391 392 /** 393 * Convert this User to a VoteREsolver.VoterInfo. Not cached. 394 */ createVoterInfo()395 private VoterInfo createVoterInfo() { 396 // VoterInfo(Organization.google, Level.vetter, "J. 397 // Smith") }, 398 Organization o = this.getOrganization(); 399 VoteResolver.Level l = this.getLevel(); 400 Set<String> localesSet = new HashSet<>(); 401 if (!isAllLocales(locales)) { 402 for (String s : tokenizeLocale(locales)) { 403 localesSet.add(s); 404 } 405 } 406 VoterInfo v = new VoterInfo(o, l, this.name, localesSet); 407 return v; 408 } 409 410 /** 411 * Return the value of this voter info, out of the cache 412 * 413 * @deprecated use getVoterInfo 414 * @see #getVoterInfo 415 */ 416 @Deprecated voterInfo()417 public VoterInfo voterInfo() { 418 return getVoterInfo(); 419 } 420 421 @Override getVoterInfo()422 public VoterInfo getVoterInfo() { 423 return getVoterToInfo(id); 424 } 425 getLevel()426 public synchronized VoteResolver.Level getLevel() { 427 if (vr_level == null) { 428 vr_level = VoteResolver.Level.fromSTLevel(this.userlevel); 429 } 430 return vr_level; 431 } 432 433 private VoteResolver.Level vr_level = null; 434 getOrganization()435 public synchronized Organization getOrganization() { 436 if (vr_org == null) { 437 vr_org = UserRegistry.computeVROrganization(this.org); 438 } 439 return vr_org; 440 } 441 442 private Organization vr_org = null; 443 444 private String voterOrg = null; 445 446 /** 447 * Convenience function for returning the "VoteResult friendly" 448 * organization. 449 */ voterOrg()450 public String voterOrg() { 451 if (voterOrg == null) { 452 voterOrg = getVoterInfo().getOrganization().name(); 453 } 454 return voterOrg; 455 } 456 457 /** 458 * Is this user an administrator 'over' this user? 459 * 460 * @param other 461 * @see VoteResolver.Level.isAdminFor() 462 * @deprecated 463 */ 464 @Deprecated isAdminFor(User other)465 public boolean isAdminFor(User other) { 466 return getLevel().isManagerFor(getOrganization(), other.getLevel(), other.getOrganization()); 467 } 468 isSameOrg(User other)469 public boolean isSameOrg(User other) { 470 return getOrganization() == other.getOrganization(); 471 } 472 473 /** 474 * Is this user an administrator 'over' this user? Always true if admin, 475 * orif TC in same org. 476 * 477 * @param other 478 */ isAdminForOrg(String org)479 public boolean isAdminForOrg(String org) { 480 boolean adminOrRelevantTc = UserRegistry.userIsAdmin(this) || 481 482 ((UserRegistry.userIsTC(this) || this.userlevel == MANAGER) && (org != null) && this.org.equals(org)); 483 return adminOrRelevantTc; 484 } 485 486 @Override compareTo(User other)487 public int compareTo(User other) { 488 if (other == this || other.equals(this)) 489 return 0; 490 if (this.id < other.id) { 491 return -1; 492 } else { 493 return 1; 494 } 495 } 496 vrOrg()497 Organization vrOrg() { 498 return Organization.fromString(voterOrg()); 499 } 500 501 /** 502 * Doesn't send the password, does send other info 503 */ 504 @Override toJSONString()505 public String toJSONString() throws JSONException { 506 return new JSONObject().put("email", email) 507 .put("emailHash", getEmailHash()) 508 .put("name", name) 509 .put("userlevel", userlevel) 510 .put("votecount", getLevel().getVotes()) 511 .put("userlevelName", UserRegistry.levelAsStr(userlevel)) 512 .put("org", vrOrg().name()) 513 .put("orgName", vrOrg().displayName) 514 .put("id", id) 515 .toString(); 516 } 517 canImportOldVotes()518 public boolean canImportOldVotes() { 519 return UserRegistry.userIsVetter(this) && (CLDRConfig.getInstance().getPhase() == Phase.SUBMISSION); 520 } 521 } 522 printPasswordLink(WebContext ctx, String email, String password)523 public static void printPasswordLink(WebContext ctx, String email, String password) { 524 ctx.println("<a href='" + ctx.base() + "?email=" + email + "&uid=" + password + "'>Login for " + email + "</a>"); 525 } 526 527 private static Map<String, Organization> orgToVrOrg = new HashMap<>(); 528 computeVROrganization(String org)529 public static synchronized Organization computeVROrganization(String org) { 530 Organization o = Organization.fromString(org); 531 if (o == null) { 532 o = orgToVrOrg.get(org); 533 } else { 534 orgToVrOrg.put(org, o); // from Organization.fromString 535 } 536 if (o == null) { 537 try { 538 /* 539 * TODO: "utilika" always logs WARNING: ** Unknown organization (treating as Guest): Utilika Foundation" 540 * Map to "The Long Now Foundation" instead? Cf. https://unicode.org/cldr/trac/ticket/6320 541 * Organization.java has: longnow("The Long Now Foundation", "Long Now", "PanLex") 542 */ 543 String arg = org.replaceAll("Utilika Foundation", "utilika") 544 .replaceAll("Government of Pakistan - National Language Authority", "pakistan") 545 .replaceAll("ICT Agency of Sri Lanka", "srilanka").toLowerCase().replaceAll("[.-]", "_"); 546 o = Organization.valueOf(arg); 547 } catch (IllegalArgumentException iae) { 548 o = Organization.guest; 549 SurveyLog.warnOnce("** Unknown organization (treating as Guest): " + org); 550 } 551 orgToVrOrg.put(org, o); 552 } 553 return o; 554 } 555 556 /** 557 * Called by SM to create the reg 558 * 559 * @param xlogger 560 * the logger to use 561 * @param ourConn 562 * the conn to use 563 */ createRegistry(java.util.logging.Logger xlogger, SurveyMain theSm)564 public static UserRegistry createRegistry(java.util.logging.Logger xlogger, SurveyMain theSm) throws SQLException { 565 sm = theSm; 566 UserRegistry reg = new UserRegistry(xlogger); 567 reg.setupDB(); 568 // logger.info("UserRegistry DB: created"); 569 return reg; 570 } 571 572 /** 573 * Called by SM to shutdown 574 */ shutdownDB()575 public void shutdownDB() throws SQLException { 576 // DBUtils.closeDBConnection(conn); 577 } 578 579 /** 580 * internal - called to setup db 581 */ setupDB()582 private void setupDB() throws SQLException { 583 // must be set up first. 584 userSettings = UserSettingsData.getInstance(sm); 585 586 String sql = null; 587 Connection conn = DBUtils.getInstance().getDBConnection(); 588 try { 589 synchronized (conn) { 590 // logger.info("UserRegistry DB: initializing..."); 591 592 boolean hadUserTable = DBUtils.hasTable(conn, CLDR_USERS); 593 if (!hadUserTable) { 594 sql = createUserTable(conn); 595 conn.commit(); 596 } else if (!DBUtils.db_Derby) { 597 /* update table to DATETIME instead of TIMESTAMP */ 598 Statement s = conn.createStatement(); 599 sql = "alter table cldr_users change lastlogin lastlogin DATETIME"; 600 s.execute(sql); 601 s.close(); 602 conn.commit(); 603 } 604 605 //create review and post table 606 sql = "(see ReviewHide.java)"; 607 ReviewHide.createTable(conn); 608 boolean hadInterestTable = DBUtils.hasTable(conn, CLDR_INTEREST); 609 if (!hadInterestTable) { 610 Statement s = conn.createStatement(); 611 612 sql = ("create table " + CLDR_INTEREST + " (uid INT NOT NULL , " + "forum varchar(256) not null " + ")"); 613 s.execute(sql); 614 sql = "CREATE INDEX " + CLDR_INTEREST + "_id_loc ON " + CLDR_INTEREST + " (uid) "; 615 s.execute(sql); 616 sql = "CREATE INDEX " + CLDR_INTEREST + "_id_for ON " + CLDR_INTEREST + " (forum) "; 617 s.execute(sql); 618 SurveyLog.debug("DB: created " + CLDR_INTEREST); 619 sql = null; 620 s.close(); 621 conn.commit(); 622 } 623 624 myinit(); // initialize the prepared statements 625 626 if (!hadInterestTable) { 627 setupIntLocs(); // set up user -> interest table mapping 628 } 629 630 } 631 } catch (SQLException se) { 632 se.printStackTrace(); 633 System.err.println("SQL err: " + DBUtils.unchainSqlException(se)); 634 System.err.println("Last SQL run: " + sql); 635 throw se; 636 } finally { 637 DBUtils.close(conn); 638 } 639 } 640 641 /** 642 * @param conn 643 * @return 644 * @throws SQLException 645 */ createUserTable(Connection conn)646 private String createUserTable(Connection conn) throws SQLException { 647 String sql; 648 Statement s = conn.createStatement(); 649 650 sql = ("create table " + CLDR_USERS + "(id INT NOT NULL " + DBUtils.DB_SQL_IDENTITY + ", " + "userlevel int not null, " 651 + "name " + DBUtils.DB_SQL_UNICODE + " not null, " + "email varchar(128) not null UNIQUE, " 652 + "org varchar(256) not null, " + "password varchar(100) not null, " + "audit varchar(1024) , " 653 + "locales varchar(1024) , " + 654 // "prefs varchar(1024) , " + /* deprecated Dec 2010. Not used 655 // anywhere */ 656 "intlocs varchar(1024) , " + // added apr 2006: ALTER table 657 // CLDR_USERS ADD COLUMN intlocs 658 // VARCHAR(1024) 659 "lastlogin " + DBUtils.DB_SQL_TIMESTAMP0 + // added may 2006: 660 // alter table 661 // CLDR_USERS ADD 662 // COLUMN lastlogin 663 // TIMESTAMP 664 (!DBUtils.db_Mysql ? ",primary key(id)" : "") + ")"); 665 s.execute(sql); 666 sql = ("INSERT INTO " + CLDR_USERS + "(userlevel,name,org,email,password) " + "VALUES(" + ADMIN + "," + "'admin'," 667 + "'SurveyTool'," + "'admin@'," + "'" + SurveyMain.vap + "')"); 668 s.execute(sql); 669 sql = null; 670 SurveyLog.debug("DB: added user Admin"); 671 672 s.close(); 673 return sql; 674 } 675 676 /** 677 * ID# of the user 678 */ 679 static final int ADMIN_ID = 1; 680 681 /** 682 * special ID meaning 'all' 683 */ 684 static final int ALL_ID = -1; 685 myinit()686 private void myinit() throws SQLException { 687 } 688 689 /** 690 * info = name/email/org immutable info, keep it in a separate list for 691 * quick lookup. 692 */ 693 public final static int CHUNKSIZE = 128; 694 int arraySize = 0; 695 UserRegistry.User infoArray[] = new UserRegistry.User[arraySize]; 696 697 /** 698 * Mark user as modified 699 * 700 * @param id 701 */ userModified(int id)702 void userModified(int id) { 703 synchronized (infoArray) { 704 try { 705 infoArray[id] = null; 706 } catch (IndexOutOfBoundsException ioob) { 707 // nothing to do 708 } 709 } 710 userModified(); // do this if any users are modified 711 } 712 713 /** 714 * Mark the UserRegistry as changed, purging the VoterInfo map 715 * 716 * @see #getVoterToInfo() 717 */ userModified()718 private void userModified() { 719 voterInfo = null; 720 } 721 722 /** 723 * Get the singleton user for this ID. 724 * 725 * @param id 726 * @return singleton, or null if not found/invalid 727 */ getInfo(int id)728 public UserRegistry.User getInfo(int id) { 729 if (id < 0) { 730 return null; 731 } 732 // System.err.println("Fetching info for id " + id); 733 synchronized (infoArray) { 734 User ret = null; 735 try { 736 // System.err.println("attempting array lookup for id " + id); 737 // ret = (User)infoArray.get(id); 738 ret = infoArray[id]; 739 } catch (IndexOutOfBoundsException ioob) { 740 // System.err.println("Index out of bounds for id " + id + " - " 741 // + ioob); 742 ret = null; // not found 743 } 744 745 if (ret == null) { // synchronized(conn) { 746 // System.err.println("go fish for id " + id); 747 // queryIdStmt = 748 // conn.prepareStatement("SELECT name,org,email from " 749 // + CLDR_USERS +" where id=?"); 750 ResultSet rs = null; 751 PreparedStatement pstmt = null; 752 Connection conn = DBUtils.getInstance().getDBConnection(); 753 try { 754 pstmt = DBUtils.prepareForwardReadOnly(conn, UserRegistry.SQL_queryIdStmt_FRO); 755 pstmt.setInt(1, id); 756 // First, try to query it back from the DB. 757 rs = pstmt.executeQuery(); 758 if (!rs.next()) { 759 // System.err.println("Unknown user#:" + id); 760 return null; 761 } 762 User u = new UserRegistry.User(id); 763 // from params: 764 u.name = DBUtils.getStringUTF8(rs, 1);// rs.getString(1); 765 u.org = rs.getString(2); 766 u.getOrganization(); // verify 767 768 u.email = rs.getString(3); 769 u.userlevel = rs.getInt(4); 770 u.intlocs = rs.getString(5); 771 u.locales = normalizeLocaleList(rs.getString(6)); 772 u.last_connect = rs.getTimestamp(7); 773 u.password = rs.getString(8); 774 // queryIdStmt = 775 // conn.prepareStatement("SELECT name,org,email,userlevel,intlocs,lastlogin,password from " 776 // + CLDR_USERS +" where id=?", 777 778 // System.err.println("SQL Loaded info for U#"+u.id + 779 // " - "+u.name +"/"+u.org+"/"+u.email); 780 ret = u; // let it finish.. 781 782 if (id >= arraySize) { 783 int newchunk = (((id + 1) / CHUNKSIZE) + 1) * CHUNKSIZE; 784 // System.err.println("UR: userInfo resize from " + 785 // infoArray.length + " to " + newchunk); 786 infoArray = new UserRegistry.User[newchunk]; 787 arraySize = newchunk; 788 } 789 infoArray[id] = u; 790 // good so far.. 791 if (rs.next()) { 792 // dup returned! 793 throw new InternalError("Dup user id # " + id); 794 } 795 } catch (SQLException se) { 796 logger.log(java.util.logging.Level.SEVERE, 797 "UserRegistry: SQL error trying to get #" + id + " - " + DBUtils.unchainSqlException(se), se); 798 throw new InternalError("UserRegistry: SQL error trying to get #" + id + " - " 799 + DBUtils.unchainSqlException(se)); 800 // return ret; 801 } catch (Throwable t) { 802 logger.log(java.util.logging.Level.SEVERE, "UserRegistry: some error trying to get #" + id, t); 803 throw new InternalError("UserRegistry: some error trying to get #" + id + " - " + t.toString()); 804 // return ret; 805 } finally { 806 // close out the RS 807 DBUtils.close(rs, pstmt, conn); 808 } // end try 809 } 810 // /*srl*/ if(ret==null) { System.err.println("returning NULL for " 811 // + id); } else { User u = ret; 812 // System.err.println("Returned info for U#"+u.id + " - "+u.name 813 // +"/"+u.org+"/"+u.email); } 814 return ret; 815 } // end synch array 816 } 817 normalizeEmail(String str)818 private final String normalizeEmail(String str) { 819 return str.trim().toLowerCase(); 820 } 821 get(String pass, String email, String ip)822 public final UserRegistry.User get(String pass, String email, String ip) throws LogoutException { 823 return get(pass, email, ip, false); 824 } 825 touch(int id)826 public void touch(int id) { 827 // System.err.println("Touching: " + id); 828 Connection conn = null; 829 PreparedStatement pstmt = null; 830 // synchronized(conn) { 831 try { 832 conn = DBUtils.getInstance().getDBConnection(); 833 pstmt = conn.prepareStatement(SQL_touchStmt); 834 pstmt.setInt(1, id); 835 pstmt.executeUpdate(); 836 conn.commit(); 837 } catch (SQLException se) { 838 logger.log(java.util.logging.Level.SEVERE, 839 "UserRegistry: SQL error trying to touch " + id + " - " + DBUtils.unchainSqlException(se), se); 840 throw new InternalError("UserRegistry: SQL error trying to touch " + id + " - " + DBUtils.unchainSqlException(se)); 841 } finally { 842 DBUtils.close(pstmt, conn); 843 } 844 845 // } 846 } 847 848 /** 849 * @param letmein 850 * The VAP was given - allow the user in regardless 851 * @param pass 852 * the password to match. If NULL, means just do a lookup 853 */ get(String pass, String email, String ip, boolean letmein)854 public UserRegistry.User get(String pass, String email, String ip, boolean letmein) throws LogoutException { 855 if ((email == null) || (email.length() <= 0)) { 856 return null; // nothing to do 857 } 858 if (((pass != null && pass.length() <= 0)) && !letmein) { 859 return null; // nothing to do 860 } 861 862 if (email.startsWith("!") && pass != null && pass.equals(SurveyMain.vap)) { 863 email = email.substring(1); 864 letmein = true; 865 } 866 867 email = normalizeEmail(email); 868 869 ResultSet rs = null; 870 // synchronized(conn) { 871 Connection conn = null; 872 PreparedStatement pstmt = null; 873 try { 874 conn = DBUtils.getInstance().getDBConnection(); 875 if ((pass != null) && !letmein) { 876 // logger.info("Looking up " + email + " : " + pass); 877 pstmt = DBUtils.prepareForwardReadOnly(conn, SQL_queryStmt_FRO); 878 pstmt.setString(1, email); 879 pstmt.setString(2, pass); 880 } else { 881 // logger.info("Looking up " + email); 882 pstmt = DBUtils.prepareForwardReadOnly(conn, SQL_queryEmailStmt_FRO); 883 pstmt.setString(1, email); 884 } 885 // First, try to query it back from the DB. 886 rs = pstmt.executeQuery(); 887 if (!rs.next()) { // user was not found. 888 throw new UserRegistry.LogoutException(); 889 } 890 User u = new UserRegistry.User(rs.getInt(1)); 891 892 // from params: 893 u.password = pass; 894 if (letmein) { 895 u.password = rs.getString(8); 896 } 897 u.email = normalizeEmail(email); 898 // from db: (id,name,userlevel,org,locales) 899 u.name = DBUtils.getStringUTF8(rs, 2);// rs.getString(2); 900 u.userlevel = rs.getInt(3); 901 u.org = rs.getString(4); 902 u.locales = rs.getString(5); 903 u.intlocs = rs.getString(6); 904 u.last_connect = rs.getTimestamp(7); 905 906 // good so far.. 907 908 if (rs.next()) { 909 // dup returned! 910 logger.severe("Duplicate user for " + email + " - ids " + u.id + " and " + rs.getInt(1)); 911 return null; 912 } 913 return u; 914 } catch (SQLException se) { 915 logger.log(java.util.logging.Level.SEVERE, 916 "UserRegistry: SQL error trying to get " + email + " - " + DBUtils.unchainSqlException(se), se); 917 throw new InternalError("UserRegistry: SQL error trying to get " + email + " - " + DBUtils.unchainSqlException(se)); 918 // return null; 919 } catch (LogoutException le) { 920 if (pass != null) { 921 // only log this if they were actually trying to login. 922 logger.log(java.util.logging.Level.SEVERE, "AUTHENTICATION FAILURE; email=" + email + "; ip=" + ip); 923 } 924 throw le; // bubble 925 } catch (Throwable t) { 926 logger.log(java.util.logging.Level.SEVERE, "UserRegistry: some error trying to get " + email, t); 927 throw new InternalError("UserRegistry: some error trying to get " + email + " - " + t.toString()); 928 // return null; 929 } finally { 930 // close out the RS 931 DBUtils.close(rs, pstmt, conn); 932 } // end try 933 // } // end synch(conn) 934 } // end get 935 get(String email)936 public UserRegistry.User get(String email) { 937 try { 938 return get(null, email, INTERNAL); 939 } catch (LogoutException le) { 940 return null; 941 } 942 } 943 944 /** 945 * @deprecated 946 * @return 947 */ 948 @Deprecated getEmptyUser()949 public UserRegistry.User getEmptyUser() { 950 User u = new User(); 951 u.name = "UNKNOWN"; 952 u.email = "UN@KNOWN.example.com"; 953 u.org = "NONE"; 954 u.password = null; 955 u.locales = ""; 956 957 return u; 958 } 959 960 static SurveyMain sm = null; // static for static checking of defaultContent 961 UserRegistry(java.util.logging.Logger xlogger)962 private UserRegistry(java.util.logging.Logger xlogger) { 963 logger = xlogger; 964 } 965 966 // ------- special things for "list" mode: 967 list(String organization, Connection conn)968 public java.sql.ResultSet list(String organization, Connection conn) throws SQLException { 969 ResultSet rs = null; 970 Statement s = null; 971 final String ORDER = " ORDER BY org,userlevel,name "; 972 // synchronized(conn) { 973 // try { 974 s = conn.createStatement(); 975 if (organization == null) { 976 rs = s.executeQuery("SELECT id,userlevel,name,email,org,locales,intlocs,lastlogin FROM " + CLDR_USERS + ORDER); 977 } else { 978 rs = s.executeQuery("SELECT id,userlevel,name,email,org,locales,intlocs,lastlogin FROM " + CLDR_USERS 979 + " WHERE org='" + organization + "'" + ORDER); 980 } 981 // } finally { 982 // s.close(); 983 // } 984 // } 985 986 return rs; 987 } 988 listPass(Connection conn)989 public java.sql.ResultSet listPass(Connection conn) throws SQLException { 990 ResultSet rs = null; 991 Statement s = null; 992 final String ORDER = " ORDER BY id "; 993 // synchronized(conn) { 994 // try { 995 s = conn.createStatement(); 996 rs = s.executeQuery("SELECT id,userlevel,name,email,org,locales,intlocs, password FROM " + CLDR_USERS + ORDER); 997 // } finally { 998 // s.close(); 999 // } 1000 // } 1001 1002 return rs; 1003 } 1004 setupIntLocs()1005 void setupIntLocs() throws SQLException { 1006 Connection conn = DBUtils.getInstance().getDBConnection(); 1007 PreparedStatement removeIntLoc = null; 1008 PreparedStatement updateIntLoc = null; 1009 try { 1010 removeIntLoc = conn.prepareStatement(SQL_removeIntLoc); 1011 updateIntLoc = conn.prepareStatement(SQL_updateIntLoc); 1012 ResultSet rs = list(null, conn); 1013 ElapsedTimer et = new ElapsedTimer(); 1014 int count = 0; 1015 while (rs.next()) { 1016 int user = rs.getInt(1); 1017 // String who = rs.getString(4); 1018 1019 updateIntLocs(user, false, conn, removeIntLoc, updateIntLoc); 1020 count++; 1021 } 1022 conn.commit(); 1023 SurveyLog.debug("update:" + count + " user's locales updated " + et); 1024 } finally { 1025 DBUtils.close(removeIntLoc, updateIntLoc, conn); 1026 } 1027 } 1028 1029 /** 1030 * assumes caller has a lock on conn 1031 */ updateIntLocs(int user, Connection conn)1032 String updateIntLocs(int user, Connection conn) throws SQLException { 1033 PreparedStatement removeIntLoc = null; 1034 PreparedStatement updateIntLoc = null; 1035 try { 1036 removeIntLoc = conn.prepareStatement(SQL_removeIntLoc); 1037 updateIntLoc = conn.prepareStatement(SQL_updateIntLoc); 1038 return updateIntLocs(user, true, conn, removeIntLoc, updateIntLoc); 1039 } finally { 1040 DBUtils.close(removeIntLoc, updateIntLoc); 1041 } 1042 } 1043 1044 /** 1045 * validate an interest locale list. 1046 * @param list 1047 * @return 1048 */ validateIntlocList(String list)1049 static String validateIntlocList(String list) { 1050 list = list.trim(); 1051 StringBuilder sb = new StringBuilder(); 1052 for (CLDRLocale l : tokenizeValidCLDRLocale(list)) { 1053 if (sb.length() > 0) { 1054 sb.append(' '); 1055 } 1056 sb.append(l.getBaseName()); 1057 } 1058 return sb.toString(); 1059 } 1060 normalizeLocaleList(String list)1061 public static String normalizeLocaleList(String list) { 1062 if (isAllLocales(list)) { 1063 return ALL_LOCALES; 1064 } 1065 if (list == null) { 1066 return ""; 1067 } 1068 list = list.trim(); 1069 if (list.length() > 0) { 1070 if (list.equals(NO_LOCALES)) { 1071 return ""; 1072 } 1073 Set<String> s = new TreeSet<>(); 1074 for (String l : UserRegistry.tokenizeLocale(list)) { 1075 String forum = new ULocale(l).getBaseName(); 1076 s.add(forum); 1077 } 1078 list = null; 1079 for (String forum : s) { 1080 if (list == null) { 1081 list = forum; 1082 } else { 1083 list = list + " " + forum; 1084 } 1085 } 1086 } 1087 return list; 1088 } 1089 1090 /** 1091 * assumes caller has a lock on conn 1092 */ updateIntLocs(int id, boolean doCommit, Connection conn, PreparedStatement removeIntLoc, PreparedStatement updateIntLoc)1093 String updateIntLocs(int id, boolean doCommit, Connection conn, PreparedStatement removeIntLoc, PreparedStatement updateIntLoc) 1094 throws SQLException { 1095 1096 User user = getInfo(id); 1097 if (user == null) { 1098 return ""; 1099 } 1100 1101 removeIntLoc.setInt(1, id); 1102 removeIntLoc.executeUpdate(); 1103 1104 String[] il = user.getInterestList(); 1105 if (il != null) { 1106 updateIntLoc.setInt(1, id); 1107 Set<String> s = new HashSet<>(); 1108 for (String l : il) { 1109 String forum = new ULocale(l).getLanguage(); 1110 s.add(forum); 1111 } 1112 for (String forum : s) { 1113 updateIntLoc.setString(2, forum); 1114 updateIntLoc.executeUpdate(); 1115 } 1116 } 1117 1118 if (doCommit) { 1119 conn.commit(); 1120 } 1121 return ""; 1122 } 1123 setUserLevel(WebContext ctx, int theirId, String theirEmail, int newLevel)1124 String setUserLevel(WebContext ctx, int theirId, String theirEmail, int newLevel) { 1125 if (!ctx.session.user.getLevel().canCreateOrSetLevelTo(VoteResolver.Level.fromSTLevel(newLevel))) { 1126 return ("[Permission Denied]"); 1127 } 1128 1129 String orgConstraint = null; 1130 String msg = ""; 1131 if (ctx.session.user.userlevel == ADMIN) { 1132 orgConstraint = ""; // no constraint 1133 } else { 1134 orgConstraint = " AND org='" + ctx.session.user.org + "' "; 1135 } 1136 Connection conn = null; 1137 try { 1138 conn = DBUtils.getInstance().getDBConnection(); 1139 Statement s = conn.createStatement(); 1140 String theSql = "UPDATE " + CLDR_USERS + " SET userlevel=" + newLevel + " WHERE id=" + theirId + " AND email='" 1141 + theirEmail + "' " + orgConstraint; 1142 // msg = msg + " (<br /><pre> " + theSql + " </pre><br />) "; 1143 logger.info("Attempt user update by " + ctx.session.user.email + ": " + theSql); 1144 int n = s.executeUpdate(theSql); 1145 conn.commit(); 1146 userModified(theirId); 1147 if (n == 0) { 1148 msg = msg + " [Error: no users were updated!] "; 1149 logger.severe("Error: 0 records updated."); 1150 } else if (n != 1) { 1151 msg = msg + " [Error in updating users!] "; 1152 logger.severe("Error: " + n + " records updated!"); 1153 } else { 1154 msg = msg + " [user level set]"; 1155 msg = msg + updateIntLocs(theirId, conn); 1156 } 1157 } catch (SQLException se) { 1158 msg = msg + " exception: " + DBUtils.unchainSqlException(se); 1159 } catch (Throwable t) { 1160 msg = msg + " exception: " + t.toString(); 1161 } finally { 1162 DBUtils.closeDBConnection(conn); 1163 } 1164 1165 return msg; 1166 } 1167 setLocales(WebContext ctx, int theirId, String theirEmail, String newLocales)1168 String setLocales(WebContext ctx, int theirId, String theirEmail, String newLocales) { 1169 return setLocales(ctx, theirId, theirEmail, newLocales, false); 1170 } 1171 setLocales(WebContext ctx, int theirId, String theirEmail, String newLocales, boolean intLocs)1172 String setLocales(WebContext ctx, int theirId, String theirEmail, String newLocales, boolean intLocs) { 1173 if (!intLocs && !ctx.session.user.isAdminFor(getInfo(theirId))) { // will make sure other user is at or below userlevel 1174 return ("[Permission Denied]"); 1175 } 1176 1177 if (!intLocs) { 1178 newLocales = normalizeLocaleList(newLocales); 1179 if (newLocales.isEmpty()) newLocales = "und"; 1180 } else { 1181 newLocales = validateIntlocList(newLocales); 1182 } 1183 String orgConstraint = null; 1184 String msg = ""; 1185 if (ctx.session.user.userlevel == ADMIN) { 1186 orgConstraint = ""; // no constraint 1187 } else { 1188 orgConstraint = " AND org='" + ctx.session.user.org + "' "; 1189 } 1190 Connection conn = null; 1191 PreparedStatement ps = null; 1192 try { 1193 final String normalizedLocales = newLocales; 1194 conn = DBUtils.getInstance().getDBConnection(); 1195 String theSql = "UPDATE " + CLDR_USERS + " SET " + (intLocs ? "intlocs" : "locales") + "=? WHERE id=" + theirId 1196 + " AND email='" + theirEmail + "' " + orgConstraint; 1197 ps = conn.prepareStatement(theSql); 1198 // msg = msg + " (<br /><pre> " + theSql + " </pre><br />) "; 1199 logger.info("Attempt user locales update by " + ctx.session.user.email + ": " + theSql + " - " + newLocales); 1200 ps.setString(1, normalizedLocales); 1201 int n = ps.executeUpdate(); 1202 conn.commit(); 1203 userModified(theirId); 1204 if (n == 0) { 1205 msg = msg + " [Error: no users were updated!] "; 1206 logger.severe("Error: 0 records updated."); 1207 } else if (n != 1) { 1208 msg = msg + " [Error in updating users!] "; 1209 logger.severe("Error: " + n + " records updated!"); 1210 } else { 1211 msg = msg + " [locales set]"; 1212 1213 msg = msg + updateIntLocs(theirId, conn); 1214 /* 1215 * if(intLocs) { return updateIntLocs(theirId); } 1216 */ 1217 } 1218 } catch (SQLException se) { 1219 msg = msg + " exception: " + DBUtils.unchainSqlException(se); 1220 } catch (Throwable t) { 1221 msg = msg + " exception: " + t.toString(); 1222 } finally { 1223 try { 1224 if (ps != null) 1225 ps.close(); 1226 if (conn != null) 1227 conn.close(); 1228 } catch (SQLException se) { 1229 logger.log(java.util.logging.Level.SEVERE, 1230 "UserRegistry: SQL error trying to close. " + DBUtils.unchainSqlException(se), se); 1231 } 1232 } 1233 // } 1234 1235 return msg; 1236 } 1237 delete(WebContext ctx, int theirId, String theirEmail)1238 String delete(WebContext ctx, int theirId, String theirEmail) { 1239 if (!ctx.session.user.isAdminFor(getInfo(theirId))) { 1240 return ("[Permission Denied]"); 1241 } 1242 1243 String orgConstraint = null; // keep org constraint in place 1244 String msg = ""; 1245 if (ctx.session.user.userlevel == ADMIN) { 1246 orgConstraint = ""; // no constraint 1247 } else { 1248 orgConstraint = " AND org='" + ctx.session.user.org + "' "; 1249 } 1250 Connection conn = null; 1251 Statement s = null; 1252 try { 1253 conn = DBUtils.getInstance().getDBConnection(); 1254 s = conn.createStatement(); 1255 String theSql = "DELETE FROM " + CLDR_USERS + " WHERE id=" + theirId + " AND email='" + theirEmail + "' " 1256 + orgConstraint; 1257 // msg = msg + " (<br /><pre> " + theSql + " </pre><br />) "; 1258 logger.info("Attempt user DELETE by " + ctx.session.user.email + ": " + theSql); 1259 int n = s.executeUpdate(theSql); 1260 conn.commit(); 1261 userModified(theirId); 1262 if (n == 0) { 1263 msg = msg + " [Error: no users were removed!] "; 1264 logger.severe("Error: 0 users removed."); 1265 } else if (n != 1) { 1266 msg = msg + " [Error in removing users!] "; 1267 logger.severe("Error: " + n + " records removed!"); 1268 } else { 1269 msg = msg + " [removed OK]"; 1270 } 1271 } catch (SQLException se) { 1272 msg = msg + " exception: " + DBUtils.unchainSqlException(se); 1273 } catch (Throwable t) { 1274 msg = msg + " exception: " + t.toString(); 1275 } finally { 1276 DBUtils.close(s, conn); 1277 // s.close(); 1278 } 1279 // } 1280 1281 return msg; 1282 } 1283 1284 public enum InfoType { 1285 INFO_EMAIL("E-mail", "email"), INFO_NAME("Name", "name"), INFO_PASSWORD("Password", "password"), INFO_ORG("Organization", 1286 "org"); 1287 private static final String CHANGE = "change_"; 1288 private String sqlField; 1289 private String title; 1290 InfoType(String title, String sqlField)1291 InfoType(String title, String sqlField) { 1292 this.title = title; 1293 this.sqlField = sqlField; 1294 } 1295 1296 @Override toString()1297 public String toString() { 1298 return title; 1299 } 1300 field()1301 public String field() { 1302 return sqlField; 1303 } 1304 fromAction(String action)1305 public static InfoType fromAction(String action) { 1306 if (action != null && action.startsWith(CHANGE)) { 1307 String which = action.substring(CHANGE.length()); 1308 return InfoType.valueOf(which); 1309 } else { 1310 return null; 1311 } 1312 } 1313 toAction()1314 public String toAction() { 1315 return CHANGE + name(); 1316 } 1317 } 1318 updateInfo(WebContext ctx, int theirId, String theirEmail, InfoType type, String value)1319 String updateInfo(WebContext ctx, int theirId, String theirEmail, InfoType type, String value) { 1320 if (type == InfoType.INFO_ORG && ctx.session.user.userlevel > ADMIN) { 1321 return ("[Permission Denied]"); 1322 } 1323 1324 if (type == InfoType.INFO_EMAIL) { 1325 value = normalizeEmail(value); 1326 } else { 1327 value = value.trim(); 1328 } 1329 1330 if (!ctx.session.user.isAdminFor(getInfo(theirId))) { 1331 return ("[Permission Denied]"); 1332 } 1333 1334 String msg = ""; 1335 Connection conn = null; 1336 PreparedStatement updateInfoStmt = null; 1337 try { 1338 conn = DBUtils.getInstance().getDBConnection(); 1339 1340 updateInfoStmt = conn.prepareStatement("UPDATE " + CLDR_USERS + " set " + type.field() + "=? WHERE id=? AND email=?"); 1341 if (type == UserRegistry.InfoType.INFO_NAME) { // unicode treatment 1342 DBUtils.setStringUTF8(updateInfoStmt, 1, value); 1343 } else { 1344 updateInfoStmt.setString(1, value); 1345 } 1346 updateInfoStmt.setInt(2, theirId); 1347 updateInfoStmt.setString(3, theirEmail); 1348 1349 logger.info("Attempt user UPDATE by " + ctx.session.user.email + ": " + type.toString() + " = " 1350 + ((type != InfoType.INFO_PASSWORD) ? value : "********")); 1351 int n = updateInfoStmt.executeUpdate(); 1352 conn.commit(); 1353 userModified(theirId); 1354 if (n == 0) { 1355 msg = msg + " [Error: no users were updated!] "; 1356 logger.severe("Error: 0 users updated."); 1357 } else if (n != 1) { 1358 msg = msg + " [Error in updated users!] "; 1359 logger.severe("Error: " + n + " updated removed!"); 1360 } else { 1361 msg = msg + " [updated OK]"; 1362 } 1363 } catch (SQLException se) { 1364 msg = msg + " exception: " + DBUtils.unchainSqlException(se); 1365 } catch (Throwable t) { 1366 msg = msg + " exception: " + t.toString(); 1367 } finally { 1368 DBUtils.close(updateInfoStmt, conn); 1369 } 1370 // } 1371 1372 return msg; 1373 } 1374 resetPassword(String forEmail, String ip)1375 public String resetPassword(String forEmail, String ip) { 1376 String msg = ""; 1377 String newPassword = CookieSession.newId(false); 1378 if (newPassword.length() > 10) { 1379 newPassword = newPassword.substring(0, 10); 1380 } 1381 Connection conn = null; 1382 PreparedStatement updateInfoStmt = null; 1383 try { 1384 conn = DBUtils.getInstance().getDBConnection(); 1385 1386 updateInfoStmt = DBUtils.prepareStatementWithArgs(conn, "UPDATE " + CLDR_USERS + " set password=? , audit=? WHERE email=? AND userlevel <" 1387 + LOCKED + " AND userlevel >= " + TC, newPassword, 1388 "Reset: " + new Date().toString() + " by " + ip, 1389 forEmail); 1390 1391 logger.info("** Attempt password reset " + forEmail + " from " + ip); 1392 int n = updateInfoStmt.executeUpdate(); 1393 1394 conn.commit(); 1395 userModified(forEmail); 1396 if (n == 0) { 1397 msg = msg + "Error: no valid accounts found"; 1398 logger.severe("Error in password reset:: 0 users updated."); 1399 } else if (n != 1) { 1400 msg = msg + " [Error in updated users!] "; 1401 logger.severe("Error in password reset: " + n + " updated removed!"); 1402 } else { 1403 msg = msg + "OK"; 1404 sm.notifyUser(null, forEmail, newPassword); 1405 } 1406 } catch (SQLException se) { 1407 SurveyLog.logException(se, "Resetting password for user " + forEmail + " from " + ip); 1408 msg = msg + " exception"; 1409 } catch (Throwable t) { 1410 SurveyLog.logException(t, "Resetting password for user " + forEmail + " from " + ip); 1411 msg = msg + " exception: " + t.toString(); 1412 } finally { 1413 DBUtils.close(updateInfoStmt, conn); 1414 } 1415 return msg; 1416 } 1417 lockAccount(String forEmail, String reason, String ip)1418 public String lockAccount(String forEmail, String reason, String ip) { 1419 String msg = ""; 1420 User u = this.get(forEmail); 1421 logger.info("** Attempt LOCK " + forEmail + " from " + ip + " reason " + reason); 1422 String newPassword = CookieSession.newId(false); 1423 if (newPassword.length() > 10) { 1424 newPassword = newPassword.substring(0, 10); 1425 } 1426 if (reason.length() > 500) { 1427 reason = reason.substring(0, 500) + "..."; 1428 } 1429 Connection conn = null; 1430 PreparedStatement updateInfoStmt = null; 1431 try { 1432 conn = DBUtils.getInstance().getDBConnection(); 1433 1434 updateInfoStmt = DBUtils.prepareStatementWithArgs(conn, "UPDATE " + CLDR_USERS + " set password=?,userlevel=" + LOCKED 1435 + ", audit=? WHERE email=? AND userlevel <" + LOCKED + " AND userlevel >= " + TC, 1436 newPassword, 1437 "Lock: " + new Date().toString() + " by " + ip + ":" + reason, 1438 forEmail); 1439 1440 int n = updateInfoStmt.executeUpdate(); 1441 1442 conn.commit(); 1443 userModified(forEmail); 1444 if (n == 0) { 1445 msg = msg + "Error: no valid accounts found"; 1446 logger.severe("Error in LOCK:: 0 users updated."); 1447 } else if (n != 1) { 1448 msg = msg + " [Error in updated users!] "; 1449 logger.severe("Error in LOCK: " + n + " updated removed!"); 1450 } else { 1451 msg = msg + "OK"; 1452 MailSender.getInstance().queue(null, 1, "User Locked: " + forEmail, "User account locked: " + ip + " reason=" + reason + " - " + u); 1453 } 1454 } catch (SQLException se) { 1455 SurveyLog.logException(se, "Locking account for user " + forEmail + " from " + ip); 1456 msg = msg + " exception"; 1457 } catch (Throwable t) { 1458 SurveyLog.logException(t, "Locking account for user " + forEmail + " from " + ip); 1459 msg = msg + " exception: " + t.toString(); 1460 } finally { 1461 DBUtils.close(updateInfoStmt, conn); 1462 } 1463 return msg; 1464 } 1465 userModified(String forEmail)1466 private void userModified(String forEmail) { 1467 User u = get(forEmail); 1468 if (u != null) userModified(u); 1469 } 1470 userModified(User u)1471 private void userModified(User u) { 1472 // TODO Auto-generated method stub 1473 userModified(u.id); 1474 } 1475 getPassword(WebContext ctx, int theirId)1476 public String getPassword(WebContext ctx, int theirId) { 1477 ResultSet rs = null; 1478 Statement s = null; 1479 String result = null; 1480 Connection conn = null; 1481 // try { 1482 if (ctx != null) { 1483 logger.info("UR: Attempt getPassword by " + ctx.session.user.email + ": of #" + theirId); 1484 } 1485 try { 1486 conn = DBUtils.getInstance().getDBConnection(); 1487 s = conn.createStatement(); 1488 rs = s.executeQuery("SELECT password FROM " + CLDR_USERS + " WHERE id=" + theirId); 1489 if (!rs.next()) { 1490 if (ctx != null) 1491 ctx.println("Couldn't find user."); 1492 return null; 1493 } 1494 result = rs.getString(1); 1495 if (rs.next()) { 1496 if (ctx != null) { 1497 ctx.println("Matched duplicate user (?)"); 1498 } 1499 return null; 1500 } 1501 } catch (SQLException se) { 1502 logger.severe("UR: exception: " + DBUtils.unchainSqlException(se)); 1503 if (ctx != null) 1504 ctx.println(" An error occured: " + DBUtils.unchainSqlException(se)); 1505 } catch (Throwable t) { 1506 logger.severe("UR: exception: " + t.toString()); 1507 if (ctx != null) 1508 ctx.println(" An error occured: " + t.toString()); 1509 } finally { 1510 DBUtils.close(s, conn); 1511 } 1512 // } 1513 1514 return result; 1515 } 1516 makePassword(String email)1517 public static String makePassword(String email) { 1518 return CookieSession.newId(false).substring(0, 9); 1519 // return CookieSession.cheapEncode((System.currentTimeMillis()*100) + 1520 // SurveyMain.pages) + "x" + 1521 // CookieSession.cheapEncode(email.hashCode() * 1522 // SurveyMain.vap.hashCode()); 1523 } 1524 newUser(WebContext ctx, User u)1525 public User newUser(WebContext ctx, User u) { 1526 final boolean hushUserMessages = CLDRConfig.getInstance().getEnvironment() == Environment.UNITTEST; 1527 u.email = normalizeEmail(u.email); 1528 // prepare quotes 1529 u.email = u.email.replace('\'', '_').toLowerCase(); 1530 u.org = u.org.replace('\'', '_'); 1531 u.name = u.name.replace('\'', '_'); 1532 u.locales = u.locales.replace('\'', '_'); 1533 1534 Connection conn = null; 1535 PreparedStatement insertStmt = null; 1536 try { 1537 conn = DBUtils.getInstance().getDBConnection(); 1538 insertStmt = conn.prepareStatement(SQL_insertStmt); 1539 insertStmt.setInt(1, u.userlevel); 1540 DBUtils.setStringUTF8(insertStmt, 2, u.name); // insertStmt.setString(2, 1541 // u.name); 1542 insertStmt.setString(3, u.org); 1543 insertStmt.setString(4, u.email); 1544 insertStmt.setString(5, u.password); 1545 insertStmt.setString(6, normalizeLocaleList(u.locales)); 1546 if (!insertStmt.execute()) { 1547 if (!hushUserMessages) logger.info("Added."); 1548 conn.commit(); 1549 if (ctx != null) 1550 ctx.println("<p>Added user.<p>"); 1551 User newu = get(u.password, u.email, FOR_ADDING); // throw away 1552 // old user 1553 updateIntLocs(newu.id, conn); 1554 resetOrgList(); // update with new org spelling. 1555 notify(newu); 1556 return newu; 1557 } else { 1558 if (ctx != null) 1559 ctx.println("Couldn't add user."); 1560 conn.commit(); 1561 return null; 1562 } 1563 } catch (SQLException se) { 1564 SurveyLog.logException(se, "Adding User"); 1565 logger.severe("UR: Adding " + u.toString() + ": exception: " + DBUtils.unchainSqlException(se)); 1566 } catch (Throwable t) { 1567 SurveyLog.logException(t, "Adding User"); 1568 logger.severe("UR: Adding " + u.toString() + ": exception: " + t.toString()); 1569 } finally { 1570 userModified(); // new user 1571 DBUtils.close(insertStmt, conn); 1572 } 1573 1574 return null; 1575 } 1576 1577 // All of the userlevel policy is concentrated here, or in above functions 1578 // (search for 'userlevel') 1579 1580 // * user types userIsAdmin(User u)1581 public static final boolean userIsAdmin(User u) { 1582 return (u != null) && (u.userlevel <= UserRegistry.ADMIN); 1583 } 1584 userIsTC(User u)1585 public static final boolean userIsTC(User u) { 1586 return (u != null) && (u.userlevel <= UserRegistry.TC); 1587 } 1588 userIsExactlyManager(User u)1589 public static final boolean userIsExactlyManager(User u) { 1590 return (u != null) && (u.userlevel == UserRegistry.MANAGER); 1591 } 1592 userIsExpert(User u)1593 public static final boolean userIsExpert(User u) { 1594 return (u != null) && (u.userlevel <= UserRegistry.EXPERT); 1595 } 1596 userIsVetter(User u)1597 public static final boolean userIsVetter(User u) { 1598 return (u != null) && (u.userlevel <= UserRegistry.VETTER); 1599 } 1600 userIsStreet(User u)1601 public static final boolean userIsStreet(User u) { 1602 return (u != null) && (u.userlevel <= UserRegistry.STREET); 1603 } 1604 userIsLocked(User u)1605 public static final boolean userIsLocked(User u) { 1606 return (u != null) && (u.userlevel == UserRegistry.LOCKED); 1607 } 1608 userIsExactlyAnonymous(User u)1609 public static final boolean userIsExactlyAnonymous(User u) { 1610 return (u != null) && (u.userlevel == UserRegistry.ANONYMOUS); 1611 } 1612 1613 // * user rights 1614 /** can create a user in a different organization? */ userCreateOtherOrgs(User u)1615 public static final boolean userCreateOtherOrgs(User u) { 1616 return userIsAdmin(u); 1617 } 1618 1619 /** What level can the new user be, given requested? */ 1620 1621 @Deprecated 1622 /** 1623 * 1624 * @param u 1625 * @param requestedLevel 1626 * @return 1627 * @deprecated 1628 * @see Level#canCreateOrSetLevelTo 1629 */ userCanCreateUserOfLevel(User u, int requestedLevel)1630 public static final int userCanCreateUserOfLevel(User u, int requestedLevel) { 1631 if (requestedLevel < 0) { 1632 requestedLevel = 0; 1633 } 1634 if (requestedLevel < u.userlevel) { // pin to creator 1635 requestedLevel = u.userlevel; 1636 } 1637 if (requestedLevel == EXPERT && u.userlevel != ADMIN) { 1638 return VETTER; // only admin can create EXPERT 1639 } 1640 if (u.userlevel == MANAGER) { 1641 if (requestedLevel == MANAGER) { 1642 return MANAGER; 1643 } else { 1644 if (requestedLevel < VETTER) { 1645 return VETTER; 1646 } else { 1647 return requestedLevel; 1648 } 1649 } 1650 } 1651 return requestedLevel; 1652 } 1653 1654 /** Can the user modify anyone's level? */ userCanModifyUsers(User u)1655 static final boolean userCanModifyUsers(User u) { 1656 return userIsTC(u) || userIsExactlyManager(u); 1657 } 1658 userCanEmailUsers(User u)1659 static final boolean userCanEmailUsers(User u) { 1660 return userIsTC(u) || userIsExactlyManager(u); 1661 } 1662 1663 /** 1664 * Returns true if the manager user can change the user's userlevel 1665 * @param managerUser the user doing the changing 1666 * @param targetId the user being changed 1667 * @param targetNewUserLevel the new userlevel of the user 1668 * @return true if the action can proceed, otherwise false 1669 */ userCanModifyUser(User managerUser, int targetId, int targetNewUserLevel)1670 static final boolean userCanModifyUser(User managerUser, int targetId, int targetNewUserLevel) { 1671 if (targetId == ADMIN_ID) { 1672 return false; // can't modify admin user 1673 } 1674 if (managerUser == null) { 1675 return false; // no user 1676 } 1677 if (userIsAdmin(managerUser)) { 1678 return true; // admin can modify everyone 1679 } 1680 final User otherUser = CookieSession.sm.reg.getInfo(targetId); // TODO static 1681 if (otherUser == null) { 1682 return false; // ? 1683 } 1684 if (!managerUser.org.equals(otherUser.org)) { 1685 return false; 1686 } 1687 if (!userCanModifyUsers(managerUser)) { 1688 return false; 1689 } 1690 if (targetId == managerUser.id) { 1691 return false; // cannot modify self 1692 } 1693 if (targetNewUserLevel < managerUser.userlevel) { 1694 return false; // Cannot assign a userlevel higher than the manager 1695 } 1696 return true; 1697 } 1698 userCanDeleteUser(User managerUser, int targetId, int targetLevel)1699 static final boolean userCanDeleteUser(User managerUser, int targetId, int targetLevel) { 1700 return (userCanModifyUser(managerUser, targetId, targetLevel) && targetLevel > managerUser.userlevel); // must 1701 // be 1702 // at 1703 // a 1704 // lower 1705 // level 1706 } 1707 userCanDoList(User managerUser)1708 static final boolean userCanDoList(User managerUser) { 1709 return (userIsVetter(managerUser)); 1710 } 1711 userCanCreateUsers(User u)1712 public static final boolean userCanCreateUsers(User u) { 1713 return (userIsTC(u) || userIsExactlyManager(u)); 1714 } 1715 userCanSubmit(User u)1716 static final boolean userCanSubmit(User u) { 1717 if (SurveyMain.isPhaseReadonly()) 1718 return false; 1719 return ((u != null) && userIsStreet(u)); 1720 } 1721 1722 /** 1723 * Can the user use the vetting summary page? 1724 * @param u 1725 * @return 1726 */ userCanUseVettingSummary(User u)1727 public static final boolean userCanUseVettingSummary(User u) { 1728 return (u != null) && ( /* userIsExactlyManager(u) || */userIsTC(u)); 1729 } 1730 1731 /** 1732 * Can the user monitor forum participation? 1733 * 1734 * @param u the user 1735 * @return true or false 1736 */ userCanMonitorForum(User u)1737 public static final boolean userCanMonitorForum(User u) { 1738 return userIsTC(u) || userIsExactlyManager(u); 1739 } 1740 localeMatchesLocaleList(String localeArray[], CLDRLocale locale)1741 static boolean localeMatchesLocaleList(String localeArray[], CLDRLocale locale) { 1742 return localeMatchesLocaleList(stringArrayToLocaleArray(localeArray), locale); 1743 } 1744 localeMatchesLocaleList(CLDRLocale localeArray[], CLDRLocale locale)1745 static boolean localeMatchesLocaleList(CLDRLocale localeArray[], CLDRLocale locale) { 1746 for (CLDRLocale entry : localeArray) { 1747 if (entry.equals(locale)) { 1748 return true; 1749 } 1750 } 1751 return false; 1752 } 1753 localeMatchesLocaleList(String localeList, CLDRLocale locale)1754 static boolean localeMatchesLocaleList(String localeList, CLDRLocale locale) { 1755 if (isAllLocales(localeList)) { 1756 return true; 1757 } 1758 String localeArray[] = tokenizeLocale(localeList); 1759 return localeMatchesLocaleList(localeArray, locale); 1760 } 1761 1762 public enum ModifyDenial { 1763 DENY_NULL_USER("No user specified"), DENY_LOCALE_READONLY("Locale is read-only"), DENY_PHASE_READONLY( 1764 "SurveyTool is in read-only mode"), DENY_ALIASLOCALE("Locale is an alias"), DENY_DEFAULTCONTENT( 1765 "Locale is the Default Content for another locale"), DENY_PHASE_CLOSED("SurveyTool is in 'closed' phase"), DENY_NO_RIGHTS( 1766 "User does not have any voting rights"), DENY_LOCALE_LIST("User does not have rights to vote for this locale"); 1767 ModifyDenial(String reason)1768 ModifyDenial(String reason) { 1769 this.reason = reason; 1770 } 1771 1772 final String reason; 1773 getReason()1774 public String getReason() { 1775 return reason; 1776 } 1777 } 1778 userCanModifyLocale(User u, CLDRLocale locale)1779 public static final boolean userCanModifyLocale(User u, CLDRLocale locale) { 1780 return (userCanModifyLocaleWhy(u, locale) == null); 1781 } 1782 userCanAccessForum(User u, CLDRLocale locale)1783 public static final boolean userCanAccessForum(User u, CLDRLocale locale) { 1784 return (userCanAccessForumWhy(u, locale) == null); 1785 } 1786 userCanAccessForumWhy(User u, CLDRLocale locale)1787 private static Object userCanAccessForumWhy(User u, CLDRLocale locale) { 1788 if (u == null) 1789 return ModifyDenial.DENY_NULL_USER; // no user, no dice 1790 if (!userIsStreet(u)) 1791 return ModifyDenial.DENY_NO_RIGHTS; // at least street level 1792 if (userIsAdmin(u)) 1793 return null; // Admin can modify all 1794 if (userIsTC(u)) 1795 return null; // TC can modify all 1796 if (locale.getLanguage().equals("und")) { // all user accounts can write 1797 // to und. 1798 return null; 1799 } 1800 if ((u.locales == null) && userIsExpert(u)) 1801 return null; // empty = ALL 1802 if (false || userIsExactlyManager(u)) 1803 return null; // manager can edit all 1804 if (isAllLocales(u.locales)) { 1805 return null; // all 1806 } 1807 String localeArray[] = tokenizeLocale(u.locales); 1808 final CLDRLocale languageLocale = locale.getLanguageLocale(); 1809 for (final CLDRLocale l : stringArrayToLocaleArray(localeArray)) { 1810 if (l.getLanguageLocale() == languageLocale) { 1811 return null; 1812 } 1813 } 1814 return ModifyDenial.DENY_LOCALE_LIST; 1815 } 1816 countUserVoteForLocale(User theSubmitter, CLDRLocale locale)1817 public static boolean countUserVoteForLocale(User theSubmitter, CLDRLocale locale) { 1818 return (countUserVoteForLocaleWhy(theSubmitter, locale) == null); 1819 } 1820 countUserVoteForLocaleWhy(User u, CLDRLocale locale)1821 public static final ModifyDenial countUserVoteForLocaleWhy(User u, CLDRLocale locale) { 1822 // must not have a null user 1823 if (u == null) 1824 return ModifyDenial.DENY_NULL_USER; 1825 1826 // can't vote in a readonly locale 1827 if (STFactory.isReadOnlyLocale(locale)) 1828 return ModifyDenial.DENY_LOCALE_READONLY; 1829 1830 // user must have street level perms 1831 if (!userIsStreet(u)) 1832 return ModifyDenial.DENY_NO_RIGHTS; // at least street level 1833 1834 // locales that are aliases can't be modified. 1835 if (sm.isLocaleAliased(locale) != null) { 1836 return ModifyDenial.DENY_ALIASLOCALE; 1837 } 1838 1839 // locales that are default content parents can't be modified. 1840 CLDRLocale dcParent = sm.getSupplementalDataInfo().getBaseFromDefaultContent(locale); 1841 if (dcParent != null) { 1842 return ModifyDenial.DENY_DEFAULTCONTENT; // it's a defaultcontent 1843 // locale or a pure alias. 1844 } 1845 // admin, TC, and manager can always modify. 1846 if (userIsAdmin(u) || 1847 userIsTC(u) || 1848 userIsExactlyManager(u)) 1849 return null; 1850 1851 // the 'und' locale and sublocales can always be modified 1852 if (locale.getLanguage().equals("und")) { 1853 return null; 1854 } 1855 1856 // unrestricted experts can modify all 1857 if ((u.locales == null || isAllLocales(u.locales)) && userIsExpert(u)) 1858 return null; // empty = ALL 1859 1860 // User has a wildcard (*) - can modify all. 1861 if (isAllLocales(u.locales)) { 1862 return null; 1863 } 1864 String localeArray[] = tokenizeLocale(u.locales); 1865 if (localeMatchesLocaleList(localeArray, locale)) { 1866 return null; 1867 } else { 1868 return ModifyDenial.DENY_LOCALE_LIST; 1869 } 1870 } 1871 1872 // TODO: speedup. precalculate list of locales on user load. userCanModifyLocaleWhy(User u, CLDRLocale locale)1873 public static final ModifyDenial userCanModifyLocaleWhy(User u, CLDRLocale locale) { 1874 final ModifyDenial denyCountVote = countUserVoteForLocaleWhy(u, locale); 1875 1876 // If we don't count the votes, modify is prohibited. 1877 if (denyCountVote != null) { 1878 return denyCountVote; 1879 } 1880 1881 // We add more restrictions 1882 1883 // Admin and TC users can always modify, even in closed state. 1884 if (userIsAdmin(u) || userIsTC(u)) 1885 return null; 1886 1887 // Otherwise, if closed, deny 1888 if (SurveyMain.isPhaseClosed()) 1889 return ModifyDenial.DENY_PHASE_CLOSED; 1890 if (SurveyMain.isPhaseReadonly()) 1891 return ModifyDenial.DENY_PHASE_READONLY; 1892 1893 return null; 1894 } 1895 stringArrayToLocaleArray(String[] localeArray)1896 private static CLDRLocale[] stringArrayToLocaleArray(String[] localeArray) { 1897 CLDRLocale arr[] = new CLDRLocale[localeArray.length]; 1898 for (int j = 0; j < localeArray.length; j++) { 1899 arr[j] = CLDRLocale.getInstance(localeArray[j]); 1900 } 1901 return arr; 1902 } 1903 userCanSubmitLocale(User u, CLDRLocale locale)1904 static final boolean userCanSubmitLocale(User u, CLDRLocale locale) { 1905 return userCanSubmitLocaleWhenDisputed(u, locale, false); 1906 } 1907 userCanSubmitLocaleWhenDisputed(User u, CLDRLocale locale, boolean disputed)1908 static final boolean userCanSubmitLocaleWhenDisputed(User u, CLDRLocale locale, boolean disputed) { 1909 if (SurveyMain.isPhaseReadonly()) 1910 return false; 1911 if (u == null) 1912 return false; // no user, no dice 1913 if (userIsTC(u)) 1914 return true; // TC can modify all 1915 if ((SurveyMain.phase() == SurveyMain.Phase.VETTING_CLOSED)) { 1916 if (u.userIsSpecialForCLDR15(locale)) { 1917 return true; 1918 } else { 1919 return false; 1920 } 1921 } 1922 if (SurveyMain.isPhaseClosed()) 1923 return false; 1924 if (!u.userIsSpecialForCLDR15(locale) && SurveyMain.isPhaseVetting() && !disputed && !userIsExpert(u)) 1925 return false; // only expert can submit new data. 1926 return userCanModifyLocale(u, locale); 1927 } 1928 userCanSubmitAnyLocale(User u)1929 static final boolean userCanSubmitAnyLocale(User u) { 1930 if (SurveyMain.isPhaseReadonly()) 1931 return false; 1932 if (u == null) 1933 return false; // no user, no dice 1934 if (userIsTC(u)) 1935 return true; // TC can modify all 1936 if ((SurveyMain.phase() == SurveyMain.Phase.VETTING_CLOSED)) { 1937 // if(u.userIsSpecialForCLDR15("be")) { 1938 // return true; 1939 // } 1940 } 1941 if (SurveyMain.isPhaseClosed()) 1942 return false; 1943 if (SurveyMain.isPhaseVetting() && !userIsExpert(u)) 1944 return false; // only expert can submit new data. 1945 return userCanSubmit(u); 1946 } 1947 userCanVetLocale(User u, CLDRLocale locale)1948 static final boolean userCanVetLocale(User u, CLDRLocale locale) { 1949 if (SurveyMain.isPhaseReadonly()) 1950 return false; 1951 if ((SurveyMain.phase() == SurveyMain.Phase.VETTING_CLOSED) && u.userIsSpecialForCLDR15(locale)) { 1952 return true; 1953 } 1954 if (userIsTC(u)) 1955 return true; // TC can modify all 1956 if (SurveyMain.isPhaseClosed()) 1957 return false; 1958 return userCanModifyLocale(u, locale); 1959 } 1960 1961 static final String LOCALE_PATTERN = "[, \t\u00a0\\s]+"; // whitespace 1962 /** 1963 * Invalid user ID, representing NO USER. 1964 */ 1965 public static final int NO_USER = -1; 1966 isAllLocales(String localeList)1967 public static final boolean isAllLocales(String localeList) { 1968 return (localeList != null) && (localeList.contains(ALL_LOCALES) || localeList.trim().equals("all")); 1969 } 1970 1971 /* 1972 * Split into an array. Will return {} if no locales match, or {ALL_LOCALES}==ALL_LOCALES_LIST if it matches all locales 1973 */ tokenizeLocale(String localeList)1974 static String[] tokenizeLocale(String localeList) { 1975 if (isAllLocales(localeList)) { 1976 throw new IllegalArgumentException("Don't call this function with '" + ALL_LOCALES + "' - " + localeList); 1977 } 1978 if ((localeList == null) || ((localeList = localeList.trim()).length() == 0)) { 1979 // System.err.println("TKL: null input"); 1980 return new String[0]; 1981 } 1982 return localeList.trim().split(LOCALE_PATTERN); 1983 } 1984 1985 /** 1986 * Tokenize a string, but return an array of CLDRLocales 1987 * 1988 * @param localeList 1989 * @return 1990 */ tokenizeCLDRLocale(String localeList)1991 static CLDRLocale[] tokenizeCLDRLocale(String localeList) { 1992 if (isAllLocales(localeList)) { 1993 throw new IllegalArgumentException("Don't call this function with '" + ALL_LOCALES + "' - " + localeList); 1994 } 1995 if ((localeList == null) || ((localeList = localeList.trim()).length() == 0)) { 1996 // System.err.println("TKL: null input"); 1997 return new CLDRLocale[0]; 1998 } 1999 2000 String s[] = tokenizeLocale(localeList); 2001 CLDRLocale l[] = new CLDRLocale[s.length]; 2002 for (int j = 0; j < s.length; j++) { 2003 l[j] = CLDRLocale.getInstance(s[j]); 2004 } 2005 return l; 2006 } 2007 2008 /** 2009 * Tokenize a list, and validate it against actual locales 2010 * 2011 * @param localeList 2012 * @return 2013 */ tokenizeValidCLDRLocale(String localeList)2014 static Set<CLDRLocale> tokenizeValidCLDRLocale(String localeList) { 2015 if (isAllLocales(localeList)) { 2016 throw new IllegalArgumentException("Don't call this function with '" + ALL_LOCALES + "' - " + localeList); 2017 } 2018 Set<CLDRLocale> s = new TreeSet<>(); 2019 if (localeList == null || isAllLocales(localeList)) 2020 return s; // empty 2021 2022 Set<CLDRLocale> allLocs = SurveyMain.getLocalesSet(); 2023 CLDRLocale locs[] = tokenizeCLDRLocale(localeList); 2024 for (CLDRLocale l : locs) { 2025 if (!allLocs.contains(l)) { 2026 continue; 2027 } 2028 s.add(l); 2029 } 2030 if (s.isEmpty() && localeList.trim().length() > 0) { 2031 s.add(CLDRLocale.getInstance("und")); // don't set it to 'all' 2032 } 2033 return s; 2034 } 2035 2036 /** 2037 * take a locale string and convert it to HTML. 2038 */ prettyPrintLocale(String localeList)2039 static String prettyPrintLocale(String localeList) { 2040 if (isAllLocales(localeList)) { 2041 return ("* (<i title='" + localeList + "'>all locales</i>)"); 2042 } 2043 // System.err.println("TKL: ppl - " + localeList); 2044 Set<CLDRLocale> localeArray = tokenizeValidCLDRLocale(localeList); 2045 String ret = ""; 2046 if ((localeList == null) || (localeList.isEmpty())) { 2047 // System.err.println("TKL: null output"); 2048 ret = ("<i title='" + localeList + "'>all locales</i>"); 2049 } else if (localeArray.isEmpty()) { 2050 if (localeList.equals("all")) { 2051 ret = ("<i title='" + localeList + "'>all locales</i>"); 2052 } else { 2053 ret = ("<i style='font-size: smaller' title='" + localeList + "'>no locales</i>"); 2054 } 2055 } else { 2056 for (CLDRLocale l : localeArray) { 2057 ret = ret + " <tt class='codebox' title='" + l.getDisplayName() + "'>" + l.getBaseName() + "</tt> "; 2058 } 2059 } 2060 // return ret + " [" + localeList + "]"; 2061 return ret; 2062 } 2063 2064 Set<User> specialUsers = null; 2065 2066 /* 2067 * TODO: getSpecialUsers is unused (with or without boolean arg)? 2068 * Delete if the code is obsolete. 2069 */ 2070 getSpecialUsers()2071 public Set<User> getSpecialUsers() { 2072 return getSpecialUsers(false); 2073 } 2074 getSpecialUsers(boolean reread)2075 public synchronized Set<User> getSpecialUsers(boolean reread) { 2076 if (specialUsers == null) { 2077 reread = true; 2078 } 2079 if (reread == true) { 2080 doReadSpecialUsers(); 2081 } 2082 return specialUsers; 2083 } 2084 doReadSpecialUsers()2085 private synchronized boolean doReadSpecialUsers() { 2086 String externalErrorName = SurveyMain.getSurveyHome() + "/" + "specialusers.txt"; 2087 2088 try { 2089 File extFile = new File(externalErrorName); 2090 2091 if (!extFile.isFile() && !extFile.canRead()) { 2092 System.err.println("Can't read special user file: " + externalErrorName); 2093 return false; 2094 } 2095 2096 // ok, now read it 2097 BufferedReader in = new BufferedReader(new FileReader(extFile)); 2098 String line; 2099 int lines = 0; 2100 Set<User> newSet = new HashSet<>(); 2101 System.err.println("* Reading special user file: " + externalErrorName); 2102 while ((line = in.readLine()) != null) { 2103 lines++; 2104 line = line.trim(); 2105 if ((line.length() <= 0) || (line.charAt(0) == '#')) { 2106 continue; 2107 } 2108 try { 2109 int theirId = new Integer(line).intValue(); 2110 User u = getInfo(theirId); 2111 if (u == null) { 2112 System.err.println("Could not find user: " + line); 2113 continue; 2114 } 2115 newSet.add(u); 2116 System.err.println("*+ User: " + u.toString()); 2117 2118 } catch (Throwable t) { 2119 System.err.println("** " + externalErrorName + ":" + lines + " - " + t.toString()); 2120 t.printStackTrace(); 2121 in.close(); 2122 return false; 2123 } 2124 } 2125 in.close(); 2126 System.err.println(externalErrorName + " - " + lines + " and " + newSet.size() + " users loaded."); 2127 2128 specialUsers = newSet; 2129 return true; 2130 } catch (IOException ioe) { 2131 System.err.println("Reading externalErrorFile: " + "specialusers.txt - " + ioe.toString()); 2132 ioe.printStackTrace(); 2133 return false; 2134 } 2135 } 2136 getVoterToInfo(int userid)2137 public VoterInfo getVoterToInfo(int userid) { 2138 return getVoterToInfo().get(userid); 2139 } 2140 2141 // Interface for VoteResolver interface 2142 /** 2143 * Fetch the user map in VoterInfo format. 2144 * 2145 * @see #userModified() 2146 */ getVoterToInfo()2147 public synchronized Map<Integer, VoterInfo> getVoterToInfo() { 2148 if (voterInfo == null) { 2149 Map<Integer, VoterInfo> map = new TreeMap<>(); 2150 2151 ResultSet rs = null; 2152 Connection conn = null; 2153 try { 2154 conn = DBUtils.getInstance().getDBConnection(); 2155 rs = list(null, conn); 2156 // id,userlevel,name,email,org,locales,intlocs,lastlogin 2157 while (rs.next()) { 2158 // We don't go through the cache, because not all users may 2159 // be loaded. 2160 2161 User u = new UserRegistry.User(rs.getInt(1)); 2162 // from params: 2163 u.userlevel = rs.getInt(2); 2164 u.name = DBUtils.getStringUTF8(rs, 3); 2165 u.email = rs.getString(4); 2166 u.org = rs.getString(5); 2167 u.locales = rs.getString(6); 2168 if (isAllLocales(u.locales)) { 2169 u.locales = ALL_LOCALES; 2170 } 2171 u.intlocs = rs.getString(7); 2172 u.last_connect = rs.getTimestamp(8); 2173 2174 // now, map it to a UserInfo 2175 VoterInfo v = u.createVoterInfo(); 2176 2177 map.put(u.id, v); 2178 } 2179 voterInfo = map; 2180 } catch (SQLException se) { 2181 logger.log(java.util.logging.Level.SEVERE, 2182 "UserRegistry: SQL error trying to update VoterInfo - " + DBUtils.unchainSqlException(se), se); 2183 } catch (Throwable t) { 2184 logger.log(java.util.logging.Level.SEVERE, 2185 "UserRegistry: some error trying to update VoterInfo - " + t.toString(), t); 2186 } finally { 2187 // close out the RS 2188 DBUtils.close(rs, conn); 2189 } // end try 2190 } 2191 return voterInfo; 2192 } 2193 2194 /** 2195 * VoterInfo map 2196 */ 2197 private Map<Integer, VoterInfo> voterInfo = null; 2198 2199 /** 2200 * Not yet implemented. 2201 * 2202 * TODO: get rid of this code, or document its purpose, referencing a ticket. 2203 * 2204 * @return 2205 */ 2206 private static String[] orgList = new String[0]; 2207 getOrgList()2208 public static String[] getOrgList() { 2209 return orgList; 2210 } 2211 2212 /** 2213 * Update the organization list. 2214 */ setOrgList()2215 public synchronized void setOrgList() { 2216 if (orgList.length > 0) { 2217 return; // already set. 2218 } 2219 resetOrgList(); 2220 } 2221 2222 /** 2223 * TODO: obsolete logic. 2224 */ resetOrgList()2225 private void resetOrgList() { 2226 // get all orgs in use... 2227 Set<String> orgs = new TreeSet<>(); 2228 Connection conn = null; 2229 Statement s = null; 2230 try { 2231 conn = DBUtils.getInstance().getDBConnection(); 2232 s = conn.createStatement(); 2233 ResultSet rs = s.executeQuery("SELECT distinct org FROM " + CLDR_USERS + " order by org"); 2234 // System.err.println("Adding orgs..."); 2235 while (rs.next()) { 2236 String org = rs.getString(1); 2237 // System.err.println("Adding org: "+ org); 2238 orgs.add(org); 2239 } 2240 } catch (SQLException se) { 2241 /* logger.severe */System.err.println(/* 2242 * java.util.logging.Level.SEVERE 2243 * , 2244 */"UserRegistry: SQL error trying to get orgs resultset for: VI " + " - " 2245 + DBUtils.unchainSqlException(se)/* ,se */); 2246 } finally { 2247 // close out the RS 2248 try { 2249 if (s != null) { 2250 s.close(); 2251 } 2252 if (conn != null) { 2253 DBUtils.closeDBConnection(conn); 2254 } 2255 } catch (SQLException se) { 2256 /* logger.severe */System.err.println(/* 2257 * java.util.logging.Level. 2258 * SEVERE, 2259 */"UserRegistry: SQL error trying to close out: " 2260 + DBUtils.unchainSqlException(se)/* ,se */); 2261 } 2262 } // end try 2263 2264 // get all possible VR orgs.. 2265 Set<Organization> allvr = new HashSet<>(); 2266 for (Organization org : Organization.values()) { 2267 allvr.add(org); 2268 } 2269 // Subtract out ones already in use 2270 for (String org : orgs) { 2271 allvr.remove(UserRegistry.computeVROrganization(org)); 2272 } 2273 // Add back any ones not yet in use 2274 for (Organization org : allvr) { 2275 String orgName = org.name(); 2276 orgName = UCharacter.toTitleCase(orgName, null); 2277 orgs.add(orgName); 2278 } 2279 2280 orgList = orgs.toArray(orgList); 2281 } 2282 2283 /** 2284 * Read back an XML file 2285 * 2286 * @param sm 2287 * @param inFile 2288 * @return 2289 */ readUserFile(SurveyMain sm, final File inFile)2290 public int readUserFile(SurveyMain sm, final File inFile) { 2291 int nusers = 0; 2292 if (CLDRConfig.getInstance().getEnvironment() != Environment.SMOKETEST) { 2293 throw new InternalError("Error: can only do this in SMOKETEST"); // insanity 2294 // check 2295 } 2296 2297 Connection conn = null; 2298 PreparedStatement ps = null; 2299 PreparedStatement ps2 = null; 2300 PreparedStatement ps3 = null; 2301 try { // do this in 1 transaction. just in case. 2302 conn = DBUtils.getInstance().getDBConnection(); 2303 2304 ps = DBUtils.prepareStatementWithArgs(conn, "drop table " + CLDR_USERS); 2305 2306 int del = ps.executeUpdate(); 2307 System.err.println("DELETED " + del + "users.. reading from " + inFile.getAbsolutePath()); 2308 2309 createUserTable(conn); 2310 2311 XMLFileReader myReader = new XMLFileReader(); 2312 final Map<String, String> attrs = new TreeMap<>(); 2313 2314 // <user id="10" email="u_10@apple.example.com" level="vetter" 2315 // name="Apple#10" org="apple" locales="nl nl_BE nl_NL"/> 2316 // >> 2317 // //users/user[@id="10"][@email="__"][@level="vetter"][@name="Apple"][@org="apple"][@locales="nl.. "] 2318 final PreparedStatement myInsert = ps2 = DBUtils.prepareStatementForwardReadOnly(conn, "myInser", "UPDATE " 2319 + CLDR_USERS + " SET userlevel=?,name=?,org=?,email=?,password=?,locales=?, lastlogin=NULL where id=?"); 2320 final PreparedStatement myAdder = ps3 = DBUtils.prepareStatementForwardReadOnly(conn, "myAdder", SQL_insertStmt); 2321 final Connection myConn = conn; // for committing 2322 myReader.setHandler(new XMLFileReader.SimpleHandler() { 2323 int maxUserId = 1; 2324 2325 @Override 2326 public void handlePathValue(String path, String value) { 2327 XPathParts xpp = XPathParts.getFrozenInstance(path); 2328 attrs.clear(); 2329 for (String k : xpp.getAttributeKeys(-1)) { 2330 attrs.put(k, xpp.getAttributeValue(-1, k)); 2331 } 2332 String elem = xpp.getElement(-1); 2333 System.err.println("* <" + elem + " " + attrs.toString() + ">" + value + "</" + elem + ">"); 2334 2335 try { 2336 2337 String xpath = attrs.get("xpath"); 2338 if (xpath != null) { 2339 xpath = xpath.trim().replace("'", "\""); 2340 } 2341 if (elem.equals("user")) { 2342 int id = Integer.parseInt(attrs.get("id")); 2343 if (id <= 1) { 2344 return; // skip user 1 2345 } 2346 while (id > maxUserId) { // loop, until we get to 2347 // that ID. 2348 ++maxUserId; 2349 // userlevel,name,org,email,password,locales,lastlogin 2350 myAdder.setInt(1, LOCKED); 2351 myAdder.setString(2, "Deleted User " + maxUserId); 2352 myAdder.setString(3, "n/a"); 2353 myAdder.setString(4, "deleted-" + maxUserId + "@nobody.example.com"); 2354 myAdder.setString(5, makePassword("")); 2355 myAdder.setString(6, ""); 2356 int add = myAdder.executeUpdate(); 2357 if (add != 1) { 2358 throw new InternalError("Trying to add dummy user " + maxUserId); 2359 } 2360 } 2361 2362 String name = attrs.get("name"); 2363 String org = attrs.get("org"); 2364 String email = normalizeEmail(attrs.get("email")); 2365 Level level = Level.valueOf(attrs.get("level")); 2366 String locales = attrs.get("locales"); 2367 2368 if (name == null) { 2369 name = org + "#" + id; 2370 } 2371 if (email == null) { 2372 email = org.toLowerCase() + id + "@" + org.toLowerCase() + ".example.com"; 2373 } 2374 myInsert.setInt(1, level.getSTLevel()); 2375 DBUtils.setStringUTF8(myInsert, 2, name); 2376 myInsert.setString(3, org); 2377 myInsert.setString(4, email); 2378 myInsert.setString(5, makePassword(email)); 2379 myInsert.setString(6, locales); 2380 myInsert.setInt(7, id); 2381 int set = myInsert.executeUpdate(); 2382 if (set != 1) { 2383 myConn.commit(); 2384 throw new InternalError("Could not add user " + id + " - max user supposedly " + maxUserId 2385 + " :: " + path); 2386 } 2387 } else { 2388 throw new IllegalArgumentException("Unknown test element type " + elem); 2389 } 2390 } catch (SQLException e) { 2391 SurveyLog.logException(e, "importing from " + inFile.getAbsolutePath() + " - " + "* <" + elem + " " 2392 + attrs.toString() + ">" + value + "</" + elem + ">"); 2393 throw new IllegalArgumentException(e); 2394 } 2395 } 2396 }); 2397 myReader.read(inFile.getAbsolutePath(), -1, false); 2398 nusers++; 2399 conn.commit(); 2400 } catch (SQLException e) { 2401 SurveyLog.logException(e, "importing users from " + inFile.getAbsolutePath()); 2402 } finally { 2403 DBUtils.close(ps3, ps2, ps, conn); 2404 } 2405 return nusers; 2406 } 2407 2408 /* 2409 * <user id="460" email="?@??.??"> > <level n="5"/> > <org>IBM</org> > 2410 * <locales type="edit"> > <locale id="sq"/> > </locales> > </user> 2411 * 2412 * It's probably better to just give VETTER, seems more portable than '5'. 2413 * 2414 * > If it is real info, make it an element. If not (and I think not, for > 2415 * "ibm"), omit it. 2416 * 2417 * In the comments are the VoteResolver enum value. I'll probably just use 2418 * that value. 2419 * 2420 * > 5. More issues with that. The structure is inconsistent, with some > 2421 * info in attributes and some in elements. Should be one or the other. > > 2422 * all attributes: > > <user id="460" email="?@??.??" level="5" org="IBM" 2423 * edit="sq de"/> > > all elements > > <user id="460"> > 2424 * <email>?@??.??</email> > <level/>5</level> > <org>IBM</org> > 2425 * <edit>sq</edit> > <edit>de</edit> > </user> > 2426 */ 2427 2428 /** 2429 * @param sm 2430 * TODO 2431 * @param ourDate 2432 * @param obscured 2433 * @param outFile 2434 * @throws UnsupportedEncodingException 2435 * @throws FileNotFoundException 2436 */ writeUserFile(SurveyMain sm, String ourDate, boolean obscured, File outFile)2437 int writeUserFile(SurveyMain sm, String ourDate, boolean obscured, File outFile) throws UnsupportedEncodingException, 2438 FileNotFoundException { 2439 PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF8")); 2440 // } catch (UnsupportedEncodingException e) { 2441 // throw new InternalError("UTF8 unsupported?").setCause(e); 2442 out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); 2443 // ctx.println("<!DOCTYPE ldml SYSTEM \"http://.../.../stusers.dtd\">"); 2444 out.println("<users generated=\"" + ourDate + "\" obscured=\"" + obscured + "\">"); 2445 String org = null; 2446 Connection conn = null; 2447 try { 2448 conn = DBUtils.getInstance().getDBConnection(); 2449 synchronized (this) { 2450 java.sql.ResultSet rs = list(org, conn); 2451 if (rs == null) { 2452 out.println("\t<!-- No results -->"); 2453 return 0; 2454 } 2455 Organization lastOrg = null; 2456 while (rs.next()) { 2457 int theirId = rs.getInt(1); 2458 int theirLevel = rs.getInt(2); 2459 String theirName = obscured ? ("#" + theirId) : DBUtils.getStringUTF8(rs, 3).trim();// rs.getString(3); 2460 String theirEmail = obscured ? /* "?@??.??" */"" : rs.getString(4).trim(); 2461 String theirOrg = rs.getString(5); 2462 String theirLocales = rs.getString(6); 2463 2464 Organization theOrg = computeVROrganization(theirOrg); 2465 if (theOrg == null) { 2466 SurveyLog.warnOnce("UserRegistry: Writing Illegal/unknown org: " + theirOrg); 2467 } 2468 if (!theOrg.equals(lastOrg)) { 2469 out.println("<!-- " + SurveyMain.xmlescape(theirOrg) + " -->"); 2470 lastOrg = theOrg; 2471 } 2472 out.print("\t<user id=\"" + theirId + "\" "); 2473 if (theirEmail.length() > 0) 2474 out.print("email=\"" + theirEmail + "\" "); 2475 out.print("level=\"" + UserRegistry.levelAsStr(theirLevel).toLowerCase() + "\""); 2476 if (theirEmail.length() > 0) 2477 out.print(" name=\"" + SurveyMain.xmlescape(theirName) + "\""); 2478 out.print(" " + "org=\"" + theOrg + "\" locales=\""); 2479 if (UserRegistry.isAllLocales(theirLocales)) { 2480 out.print('*'); 2481 } else { 2482 String theirLocalesList[] = UserRegistry.tokenizeLocale(theirLocales); 2483 for (int i = 0; i < theirLocalesList.length; i++) { 2484 if (i > 0) 2485 out.print(" "); 2486 out.print(theirLocalesList[i]); 2487 } 2488 } 2489 out.println("\"/>"); 2490 } 2491 } /* end synchronized(reg) */ 2492 } catch (SQLException se) { 2493 SurveyLog.logger.log(java.util.logging.Level.WARNING, 2494 "Query for org " + org + " failed: " + DBUtils.unchainSqlException(se), se); 2495 out.println("<!-- Failure: " + DBUtils.unchainSqlException(se) + " -->"); 2496 } finally { 2497 DBUtils.close(conn); 2498 } 2499 out.println("</users>"); 2500 out.close(); 2501 return 1; 2502 } 2503 2504 /** 2505 * Cache of the set of anonymous users 2506 */ 2507 private Set<User> anonymousUsers = null; 2508 2509 /** 2510 * Get the set of anonymous users, employing a cache. 2511 * If there aren't as many as there should be (ANONYMOUS_USER_COUNT), create some. 2512 * An "anonymous user" is one whose userlevel is ANONYMOUS. 2513 * 2514 * @return the Set. 2515 */ getAnonymousUsers()2516 public Set<User> getAnonymousUsers() { 2517 if (anonymousUsers == null) { 2518 anonymousUsers = getAnonymousUsersFromDb(); 2519 int existingCount = anonymousUsers.size(); 2520 if (existingCount < ANONYMOUS_USER_COUNT) { 2521 createAnonymousUsers(existingCount, ANONYMOUS_USER_COUNT); 2522 /* 2523 * After createAnonymousUsers, call userModified to clear voterInfo, so it will 2524 * be reloaded and include the new anonymous users. Otherwise, we would get an 2525 * "Unknown voter" exception in OrganizationToValueAndVote.add when trying to 2526 * add a vote for one of the new anonymous users. 2527 */ 2528 userModified(); 2529 anonymousUsers = getAnonymousUsersFromDb(); 2530 } 2531 } 2532 return anonymousUsers; 2533 } 2534 2535 /** 2536 * Get the set of anonymous users by running a database query. 2537 * 2538 * @return the Set. 2539 */ getAnonymousUsersFromDb()2540 private Set<User> getAnonymousUsersFromDb() { 2541 Set<User> set = new HashSet<>(); 2542 ResultSet rs = null; 2543 Connection conn = null; 2544 try { 2545 conn = DBUtils.getInstance().getDBConnection(); 2546 rs = list(null, conn); 2547 // id,userlevel,name,email,org,locales,intlocs,lastlogin 2548 while (rs.next()) { 2549 int userlevel = rs.getInt(2); 2550 if (userlevel == ANONYMOUS) { 2551 User u = new User(rs.getInt(1)); 2552 u.userlevel = userlevel; 2553 u.name = DBUtils.getStringUTF8(rs, 3); 2554 u.email = rs.getString(4); 2555 u.org = rs.getString(5); 2556 u.locales = rs.getString(6); 2557 if (isAllLocales(u.locales)) { 2558 u.locales = ALL_LOCALES; 2559 } 2560 u.intlocs = rs.getString(7); 2561 u.last_connect = rs.getTimestamp(8); 2562 set.add(u); 2563 } 2564 } 2565 } catch (SQLException se) { 2566 logger.log(java.util.logging.Level.SEVERE, 2567 "UserRegistry: SQL error getting anonymous users - " + DBUtils.unchainSqlException(se), se); 2568 } catch (Throwable t) { 2569 logger.log(java.util.logging.Level.SEVERE, 2570 "UserRegistry: some error getting anonymous users - " + t.toString(), t); 2571 } finally { 2572 DBUtils.close(rs, conn); 2573 } 2574 return set; 2575 } 2576 2577 /** 2578 * Given that there aren't enough anonymous users in the database yet, create some. 2579 * 2580 * @param existingCount the number of anonymous users that already exist 2581 * @param desiredCount the desired total number of anonymous users 2582 */ createAnonymousUsers(int existingCount, int desiredCount)2583 private void createAnonymousUsers(int existingCount, int desiredCount) { 2584 Connection conn = null; 2585 Statement s = null; 2586 try { 2587 conn = DBUtils.getInstance().getDBConnection(); 2588 s = conn.createStatement(); 2589 for (int i = existingCount + 1; i <= desiredCount; i++) { 2590 /* 2591 * Don't specify the user id; a new unique id will be assigned automatically. 2592 * Names are like "anon#3"; emails are like "anon3@example.org". 2593 */ 2594 String sql = "INSERT INTO " + CLDR_USERS 2595 + "(userlevel, name, email, org, password, locales) VALUES(" 2596 + ANONYMOUS + "," // userlevel 2597 + "'anon#" + i + "'," // name 2598 + "'anon" + i + "@example.org'," // email 2599 + "'cldr'," // org 2600 + "''," // password 2601 + "'" + ALL_LOCALES + "')"; // locales 2602 s.execute(sql); 2603 } 2604 conn.commit(); 2605 } catch (SQLException se) { 2606 logger.log(java.util.logging.Level.SEVERE, 2607 "UserRegistry: SQL error creating anonymous users - " + DBUtils.unchainSqlException(se), se); 2608 } catch (Throwable t) { 2609 logger.log(java.util.logging.Level.SEVERE, 2610 "UserRegistry: some error creating anonymous users - " + t.toString(), t); 2611 } finally { 2612 DBUtils.close(s, conn); 2613 } 2614 } 2615 } 2616