Mercurial Hosting > nabble
comparison src/nabble/model/UserImpl.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children | cc5b7d515580 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 /* | |
2 | |
3 Copyright (C) 2003 Franklin Schmidt <frank@gustos.com> | |
4 | |
5 */ | |
6 | |
7 package nabble.model; | |
8 | |
9 import fschmidt.db.DbDatabase; | |
10 import fschmidt.db.DbNull; | |
11 import fschmidt.db.DbObjectFactory; | |
12 import fschmidt.db.DbRecord; | |
13 import fschmidt.db.DbTable; | |
14 import fschmidt.db.DbUtils; | |
15 import fschmidt.db.Listener; | |
16 import fschmidt.db.ListenerList; | |
17 import fschmidt.db.LongKey; | |
18 import fschmidt.db.postgres.DbDatabaseImpl; | |
19 import fschmidt.util.java.Computable; | |
20 import fschmidt.util.java.Memoizer; | |
21 import fschmidt.util.java.ObjectUtils; | |
22 import fschmidt.util.java.SimpleCache; | |
23 import fschmidt.util.java.TimedCacheMap; | |
24 import fschmidt.util.mail.MailAddress; | |
25 import org.jasypt.digest.PooledStringDigester; | |
26 import org.jasypt.salt.FixedByteArraySaltGenerator; | |
27 import org.slf4j.Logger; | |
28 import org.slf4j.LoggerFactory; | |
29 | |
30 import java.awt.image.BufferedImage; | |
31 import java.sql.Connection; | |
32 import java.sql.PreparedStatement; | |
33 import java.sql.ResultSet; | |
34 import java.sql.SQLException; | |
35 import java.sql.Statement; | |
36 import java.util.ArrayList; | |
37 import java.util.Collection; | |
38 import java.util.Collections; | |
39 import java.util.Date; | |
40 import java.util.HashMap; | |
41 import java.util.HashSet; | |
42 import java.util.Iterator; | |
43 import java.util.List; | |
44 import java.util.Map; | |
45 import java.util.Set; | |
46 import java.util.WeakHashMap; | |
47 import java.util.concurrent.CopyOnWriteArrayList; | |
48 | |
49 | |
50 final class UserImpl extends PersonImpl implements User { | |
51 private static final Logger logger = LoggerFactory.getLogger(UserImpl.class); | |
52 | |
53 final SiteKey siteKey; | |
54 private final DbRecord<LongKey,UserImpl> record; | |
55 private String email; | |
56 private String passwordDigest; | |
57 private String name; | |
58 private Date registered; | |
59 private boolean noArchive; | |
60 private Message signature = null; | |
61 private int bounces; | |
62 | |
63 private UserImpl(SiteKey siteKey,LongKey key,ResultSet rs) | |
64 throws SQLException | |
65 { | |
66 this.siteKey = siteKey; | |
67 record = table(siteKey).newRecord(this,key); | |
68 email = rs.getString("email"); | |
69 passwordDigest = rs.getString("password_digest"); | |
70 name = rs.getString("name"); | |
71 registered = DbUtils.getDate(rs,"registered"); | |
72 noArchive = rs.getBoolean("no_archive"); | |
73 String signatureRaw = rs.getString("signature"); | |
74 String signatureFormatS = rs.getString("signature_format"); | |
75 if( signatureRaw!=null && signatureFormatS!=null ) { | |
76 Message.Format signatureFormat = Message.Format.getMessageFormat( signatureFormatS.charAt(0) ); | |
77 signature = new Message(signatureRaw,signatureFormat); | |
78 } | |
79 bounces = rs.getInt("bounces"); | |
80 for( ExtensionFactory<User,?> factory : extensionFactories ) { | |
81 Object obj = factory.construct(this,rs); | |
82 if( obj != null ) | |
83 getExtensionMap().put(factory,obj); | |
84 } | |
85 } | |
86 | |
87 private UserImpl(SiteImpl site) { | |
88 this.siteKey = site.siteKey; | |
89 record = table(siteKey).newRecord(this); | |
90 } | |
91 | |
92 | |
93 public DbRecord<LongKey,UserImpl> getDbRecord() { | |
94 return record; | |
95 } | |
96 | |
97 private DbTable<LongKey,UserImpl> table() { | |
98 return record.getDbTable(); | |
99 } | |
100 | |
101 private DbDatabase db() { | |
102 return table().getDbDatabase(); | |
103 } | |
104 | |
105 public long getId() { | |
106 return record.getPrimaryKey().value(); | |
107 } | |
108 | |
109 SiteImpl getSiteImpl() { | |
110 return siteKey.site(); | |
111 } | |
112 | |
113 public Site getSite() { | |
114 return getSiteImpl(); | |
115 } | |
116 | |
117 public boolean isDeactivated() { | |
118 return !isRegistered() && isNoArchive(); | |
119 } | |
120 | |
121 boolean isNoArchive() { | |
122 return noArchive; | |
123 } | |
124 | |
125 public void setNoArchive(boolean noArchive) { | |
126 if( this.noArchive == noArchive ) | |
127 return; | |
128 | |
129 if( !db().isInTransaction() ) { | |
130 db().beginTransaction(); | |
131 try { | |
132 UserImpl user = DbUtils.getGoodCopy(this); | |
133 user.setNoArchive(noArchive); | |
134 user.getDbRecord().update(); | |
135 db().commitTransaction(); | |
136 return; | |
137 } finally { | |
138 db().endTransaction(); | |
139 } | |
140 } | |
141 this.noArchive = noArchive; | |
142 record.fields().put("no_archive",DbNull.fix(noArchive)); | |
143 } | |
144 | |
145 public String getEmail() { | |
146 return email; | |
147 } | |
148 | |
149 static void validateEmail(String email) throws ModelException.EmailFormat { | |
150 if (!new MailAddress(email).isValid()) { | |
151 throw new ModelException.EmailFormat(email); | |
152 } | |
153 } | |
154 | |
155 public void setEmail(String email) throws ModelException { | |
156 if( !db().isInTransaction() ) { | |
157 db().beginTransaction(); | |
158 try { | |
159 UserImpl user = DbUtils.getGoodCopy(this); | |
160 user.setEmail(email); | |
161 user.getDbRecord().update(); | |
162 db().commitTransaction(); | |
163 return; | |
164 } finally { | |
165 db().endTransaction(); | |
166 } | |
167 } | |
168 validateEmail(email); | |
169 setEmail2(email); | |
170 } | |
171 | |
172 private void setEmail2(String email) throws ModelException { | |
173 if( email.equals(this.email) ) | |
174 return; | |
175 SiteImpl site = getSiteImpl(); | |
176 if( site.getUserImplFromEmail(email) != null ) | |
177 throw ModelException.newInstance("email_already_in_user","Email already in use"); | |
178 this.email = email; | |
179 record.fields().put("email",email); | |
180 } | |
181 | |
182 public String getPasswordDigest() { | |
183 return passwordDigest; | |
184 } | |
185 | |
186 public void setPassword(String password) throws ModelException { | |
187 if( "".equals(password) ) | |
188 throw ModelException.newInstance("empty_password","Password cannot be empty"); | |
189 setPasswordDigest(digestPassword(password)); | |
190 } | |
191 | |
192 public void setPasswordDigest(String passwordDigest) { | |
193 if( ObjectUtils.equals(passwordDigest,this.passwordDigest) ) | |
194 return; | |
195 this.passwordDigest = passwordDigest; | |
196 record.fields().put("password_digest",DbNull.fix(passwordDigest)); | |
197 synchronized (passcookieLock) { | |
198 this.passcookie = null; | |
199 } | |
200 } | |
201 | |
202 private volatile String passcookie = null; | |
203 private Object passcookieLock = new Object(); | |
204 | |
205 public String getPasscookie() { | |
206 String p = passcookie; | |
207 if (p==null) { | |
208 synchronized (passcookieLock) { | |
209 p = passcookie; | |
210 if (p==null) { | |
211 p = calcPasscookie(); | |
212 passcookie = p; | |
213 } | |
214 } | |
215 } | |
216 return p; | |
217 } | |
218 | |
219 public String getName() { | |
220 return name; | |
221 } | |
222 | |
223 public void setName(String name) throws ModelException { | |
224 setName(name,true); | |
225 } | |
226 | |
227 private void setName(String name,boolean replaceUnregistered) throws ModelException { | |
228 name = name.trim(); | |
229 if( name.equals("") ) | |
230 throw ModelException.newInstance("empty_user_name","User name cannot be empty."); | |
231 if( name.equals(this.name) ) | |
232 return; | |
233 if( !name.equalsIgnoreCase(this.name) ) { | |
234 UserImpl user = getSiteImpl().getUserImplFromName(name); | |
235 if( user != null ) { | |
236 if( !replaceUnregistered || user.isRegistered() ) | |
237 throw ModelException.newInstance("user_name_already_in_use","User name '"+name+"' already in use"); | |
238 user.setNameLike2(name); | |
239 user.update(); | |
240 } | |
241 try { | |
242 Connection con = db().getConnection(); | |
243 PreparedStatement stmt = con.prepareStatement( | |
244 "select 'x' from registration where email!=? and name=?" | |
245 ); | |
246 stmt.setString(1,this.email); | |
247 stmt.setString(2,name); | |
248 try { | |
249 if( stmt.executeQuery().next() ) | |
250 throw ModelException.newInstance("user_name_already_in_use","User name '"+name+"' already in use"); | |
251 } finally { | |
252 stmt.close(); | |
253 con.close(); | |
254 } | |
255 } catch(SQLException e) { | |
256 throw new RuntimeException(e); | |
257 } | |
258 } | |
259 this.name = name; | |
260 record.fields().put("name",name); | |
261 } | |
262 | |
263 void setNameLike(String name,boolean replaceUnregistered) { | |
264 try { | |
265 setName(name,replaceUnregistered); | |
266 } catch(ModelException e) { | |
267 setNameLike2(name); | |
268 } | |
269 } | |
270 | |
271 private void setNameLike2(String name) { | |
272 for( int i=2; true; i++ ) { | |
273 try { | |
274 setName(name+"-"+i,false); | |
275 break; | |
276 } catch(ModelException e2) {} | |
277 } | |
278 } | |
279 | |
280 /* To be called from the shell */ | |
281 public void changeNameTo(String newName) { | |
282 db().beginTransaction(); | |
283 try { | |
284 UserImpl u = (UserImpl) getGoodCopy(); | |
285 u.setName(newName); | |
286 u.update(); | |
287 db().commitTransaction(); | |
288 DbUtils.uncache(u); | |
289 } catch (ModelException e) { | |
290 throw new RuntimeException(e); | |
291 } finally { | |
292 db().endTransaction(); | |
293 } | |
294 } | |
295 | |
296 public Date getRegistered() { | |
297 return registered; | |
298 } | |
299 | |
300 void setRegistered(Date registered) { | |
301 if( ObjectUtils.equals(registered,this.registered) ) | |
302 return; | |
303 this.registered = registered; | |
304 record.fields().put("registered",DbNull.fix(registered)); | |
305 } | |
306 | |
307 public boolean equals(Object obj) { | |
308 return obj instanceof User && ((User)obj).getId()==getId(); | |
309 } | |
310 | |
311 public int hashCode() { | |
312 return (int)getId(); | |
313 } | |
314 | |
315 public String toString() { | |
316 return record.isInDb() ? "user-"+getId() : "user-new"; | |
317 } | |
318 | |
319 public void register() throws ModelException { | |
320 register(new Date()); | |
321 } | |
322 | |
323 public void register(Date registerDate) throws ModelException { | |
324 if( !db().isInTransaction() ) { | |
325 db().beginTransaction(); | |
326 try { | |
327 UserImpl user; | |
328 if( record.isInDb() ) { | |
329 user = DbUtils.getGoodCopy(this); | |
330 user.setEmail(email); | |
331 user.setName(name); | |
332 user.setPasswordDigest(passwordDigest); | |
333 } else { | |
334 user = this; | |
335 } | |
336 user.register(); | |
337 db().commitTransaction(); | |
338 } finally { | |
339 db().endTransaction(); | |
340 } | |
341 return; | |
342 } | |
343 if( passwordDigest==null ) | |
344 throw new RuntimeException(); | |
345 setRegistered( registerDate ); | |
346 if( record.isInDb() ) { | |
347 if( isNoArchive() ) | |
348 setNoArchive(false); | |
349 record.update(); | |
350 } else { | |
351 insert(); | |
352 } | |
353 } | |
354 | |
355 public boolean isRegistered() { | |
356 return record.isInDb() && registered!=null; | |
357 } | |
358 | |
359 void insert() { | |
360 if( email==null || name==null ) | |
361 throw new RuntimeException(); | |
362 record.insert(); | |
363 } | |
364 | |
365 public void update() { | |
366 if( !db().isInTransaction() ) | |
367 throw new RuntimeException("this should be done in a transaction"); | |
368 Set<String> keys = record.fields().keySet(); | |
369 if( keys.contains("name") || keys.contains("signature") ) { | |
370 getSiteImpl().update(); // fire change listeners | |
371 } | |
372 getDbRecord().update(); | |
373 } | |
374 | |
375 public User getGoodCopy() { | |
376 return DbUtils.getGoodCopy(this); | |
377 } | |
378 | |
379 | |
380 | |
381 | |
382 public int getExternalHash(String url) { | |
383 return (url.toLowerCase() + getId()).hashCode(); | |
384 } | |
385 | |
386 | |
387 static final ListenerList<UserImpl> preUpdateListeners = new ListenerList<UserImpl>(); | |
388 static final ListenerList<UserImpl> postInsertListeners = new ListenerList<UserImpl>(); | |
389 | |
390 private static Computable<SiteKey,DbTable<LongKey,UserImpl>> tables = new SimpleCache<SiteKey,DbTable<LongKey,UserImpl>>(new WeakHashMap<SiteKey,DbTable<LongKey,UserImpl>>(), new Computable<SiteKey,DbTable<LongKey,UserImpl>>() { | |
391 public DbTable<LongKey,UserImpl> get(SiteKey siteKey) { | |
392 DbDatabase db = siteKey.getDb(); | |
393 final long siteId = siteKey.getId(); | |
394 DbTable<LongKey,UserImpl> table = db.newTable("user_",db.newIdentityLongKeySetter("user_id") | |
395 , new DbObjectFactory<LongKey,UserImpl>() { | |
396 public UserImpl makeDbObject(LongKey key,ResultSet rs,String tableName) | |
397 throws SQLException | |
398 { | |
399 SiteKey siteKey = SiteKey.getInstance(siteId); | |
400 return new UserImpl(siteKey,key,rs); | |
401 } | |
402 } | |
403 ); | |
404 table.getPreUpdateListeners().add(preUpdateListeners); | |
405 table.getPostInsertListeners().add(postInsertListeners); | |
406 return table; | |
407 } | |
408 }); | |
409 | |
410 private static DbTable<LongKey,UserImpl> table(SiteKey siteKey) { | |
411 return tables.get(siteKey); | |
412 } | |
413 | |
414 static UserImpl getUser(SiteKey siteKey,long id) { | |
415 UserImpl user = table(siteKey).findByPrimaryKey(new LongKey(id)); | |
416 if( user==null ) | |
417 logger.warn("user "+id+" not found"); | |
418 return user; | |
419 } | |
420 | |
421 static Collection<UserImpl> getUsers(SiteKey siteKey,Collection<Long> ids) { | |
422 List<LongKey> list = new ArrayList<LongKey>(); | |
423 for( long id : ids ) { | |
424 list.add( new LongKey(id) ); | |
425 } | |
426 return table(siteKey).findByPrimaryKey(list).values(); | |
427 } | |
428 | |
429 static UserImpl getUser(SiteKey siteKey,ResultSet rs) | |
430 throws SQLException | |
431 { | |
432 return table(siteKey).getDbObject(rs); | |
433 } | |
434 | |
435 static void getUsers(SiteKey siteKey,PreparedStatement stmt,List<? super UserImpl> list) | |
436 throws SQLException | |
437 { | |
438 ResultSet rs = stmt.executeQuery(); | |
439 while( rs.next() ) { | |
440 UserImpl user = getUser(siteKey,rs); | |
441 list.add(user); | |
442 } | |
443 rs.close(); | |
444 stmt.close(); | |
445 } | |
446 | |
447 static List<UserImpl> getUsers(SiteKey siteKey,PreparedStatement stmt) | |
448 throws SQLException | |
449 { | |
450 List<UserImpl> list = new ArrayList<UserImpl>(); | |
451 getUsers(siteKey,stmt,list); | |
452 return list; | |
453 } | |
454 | |
455 private static UserImpl getUser(SiteImpl site,String val,String sql) { | |
456 try { | |
457 SiteKey siteKey = site.siteKey; | |
458 Connection con = siteKey.getDb().getConnection(); | |
459 PreparedStatement stmt = con.prepareStatement(sql); | |
460 stmt.setString(1,val); | |
461 ResultSet rs = stmt.executeQuery(); | |
462 UserImpl user = rs.next() ? getUser(siteKey,rs) : null; | |
463 rs.close(); | |
464 stmt.close(); | |
465 con.close(); | |
466 return user; | |
467 } catch(SQLException e) { | |
468 throw new RuntimeException(e); | |
469 } | |
470 } | |
471 | |
472 static UserImpl getUserFromEmail(SiteImpl site,String email) { | |
473 return getUser( site, email.toLowerCase(), | |
474 "select * from user_" | |
475 +" where lower(email)=?" | |
476 ); | |
477 } | |
478 | |
479 static UserImpl getUserFromName(SiteImpl site,String name) { | |
480 return getUser( site, name.toLowerCase(), | |
481 "select * from user_" | |
482 +" where lower(name)=?" | |
483 ); | |
484 } | |
485 | |
486 static UserImpl createGhost(SiteImpl site,String email) { | |
487 UserImpl user = new UserImpl(site); | |
488 try { | |
489 user.setEmail2(email); | |
490 } catch(ModelException e) { | |
491 throw new RuntimeException(e); | |
492 } | |
493 return user; | |
494 } | |
495 | |
496 // Subscriptions ----------------------------------------------------------- | |
497 | |
498 public boolean isSubscribed(Node node) { | |
499 return SubscriptionImpl.isSubscribed(this, (NodeImpl) node); | |
500 } | |
501 | |
502 public Subscription getSubscription(Node node) { | |
503 return SubscriptionImpl.getSubscription( this, (NodeImpl)node ); | |
504 } | |
505 | |
506 public Subscription subscribe(Node node,Subscription.To to,Subscription.Type type) { | |
507 clearBounces(); | |
508 Subscription subscription = getSubscription(node); | |
509 if( subscription != null ) { | |
510 subscription.setTo(to); | |
511 subscription.setType(type); | |
512 return subscription; | |
513 } else { | |
514 return SubscriptionImpl.insert( this, (NodeImpl)node, to, type ); | |
515 } | |
516 } | |
517 | |
518 /*10 posts in 5 minutes */ | |
519 private static final RecentPostLimit postLimit1 = new RecentPostLimit(5 * 60 * 1000L, 10); | |
520 | |
521 /* 30 posts in 15 minutes */ | |
522 private static final RecentPostLimit postLimit2 = new RecentPostLimit(15 * 60 * 1000L, 30); | |
523 | |
524 void updateNewPostLimit() { | |
525 String key = siteKey.getId() + "-" + record.getPrimaryKey().value(); | |
526 postLimit1.insert(key); | |
527 postLimit2.insert(key); | |
528 } | |
529 | |
530 public boolean hasTooManyPosts() { | |
531 String key = siteKey.getId() + "-" + record.getPrimaryKey().value(); | |
532 return postLimit1.hasTooManyPosts(key) || postLimit2.hasTooManyPosts(key); | |
533 } | |
534 | |
535 private static class RecentPostLimit { | |
536 private final long timeLimit; | |
537 private final int postLimit; | |
538 private final Map<String,long[]> floodMap; | |
539 | |
540 private RecentPostLimit(long timeLimit, int postLimit) { | |
541 this.timeLimit = timeLimit; | |
542 this.postLimit = postLimit; | |
543 this.floodMap = new TimedCacheMap<String,long[]>(timeLimit); | |
544 } | |
545 | |
546 public void insert(String key) { | |
547 long[] recentPostTimes; | |
548 synchronized(floodMap) { | |
549 recentPostTimes = floodMap.get(key); | |
550 if( recentPostTimes==null ) { | |
551 recentPostTimes = new long[postLimit]; | |
552 floodMap.put(key,recentPostTimes); | |
553 } | |
554 } | |
555 long now = System.currentTimeMillis(); | |
556 long recently = now - timeLimit; | |
557 synchronized(recentPostTimes) { | |
558 for( int i=0; i<recentPostTimes.length; i++ ) { | |
559 if( recentPostTimes[i] < recently ) { | |
560 recentPostTimes[i] = now; | |
561 return; | |
562 } | |
563 } | |
564 } | |
565 } | |
566 | |
567 public boolean hasTooManyPosts(String key) { | |
568 long[] recentPostTimes; | |
569 synchronized(floodMap) { | |
570 recentPostTimes = floodMap.get(key); | |
571 if (recentPostTimes==null) | |
572 return false; | |
573 } | |
574 long now = System.currentTimeMillis(); | |
575 long recently = now - timeLimit; | |
576 synchronized(recentPostTimes) { | |
577 for (long time : recentPostTimes) { | |
578 if (time < recently) { | |
579 return false; | |
580 } | |
581 } | |
582 } | |
583 return true; | |
584 } | |
585 } | |
586 | |
587 | |
588 static UserImpl getOrCreateUnregisteredUser(SiteImpl site,String email,String name) | |
589 throws ModelException | |
590 { | |
591 DbDatabase db = site.getDb(); | |
592 if( !db.isInTransaction() ) { | |
593 db.beginTransaction(); | |
594 try { | |
595 UserImpl user = getOrCreateUnregisteredUser(site,email,name); | |
596 db.commitTransaction(); | |
597 return user; | |
598 } finally { | |
599 db.endTransaction(); | |
600 } | |
601 } | |
602 UserImpl user = site.getUserImplFromEmail(email); | |
603 if( user==null ) { | |
604 user = new UserImpl(site); | |
605 user.setEmail(email); | |
606 } else { | |
607 if( user.isRegistered() ) | |
608 throw ModelException.newInstance("email_already_registered","This email is already registered"); | |
609 validateEmail(user.getEmail()); | |
610 } | |
611 user.setName(name); | |
612 if( !user.record.isInDb() ) { | |
613 user.insert(); | |
614 } else if( !user.record.fields().isEmpty() ) { | |
615 user.update(); | |
616 } | |
617 return user; | |
618 } | |
619 | |
620 // registration | |
621 | |
622 static UserImpl createUser(SiteImpl site,String email,String password,String name) throws ModelException { | |
623 return createUser2(site, email, digestPassword(password), name); | |
624 } | |
625 | |
626 private static UserImpl createUser2(SiteImpl site,String email,String passwordDigest,String name) throws ModelException { | |
627 // transaction used because setName() may update user | |
628 DbDatabase db = site.getDb(); | |
629 if( !db.isInTransaction() ) { | |
630 db.beginTransaction(); | |
631 try { | |
632 UserImpl user = createUser2(site,email,passwordDigest,name); | |
633 db.commitTransaction(); | |
634 return user; | |
635 } finally { | |
636 db.endTransaction(); | |
637 } | |
638 } | |
639 if (!new MailAddress(email).isValid()) { | |
640 throw new ModelException.EmailFormat("invalid_email"); | |
641 } | |
642 UserImpl user = site.getUserImplFromEmail(email); | |
643 if( user==null ) { | |
644 user = new UserImpl(site); | |
645 user.setEmail(email); | |
646 } else { | |
647 if( user.isRegistered() ) | |
648 throw ModelException.newInstance("user_already_registered","User is already registered"); | |
649 validateEmail(user.getEmail()); | |
650 } | |
651 user.setPasswordDigest(passwordDigest); | |
652 user.setName(name); | |
653 return user; | |
654 } | |
655 | |
656 static UserImpl getOrCreateUser(SiteImpl site,String email) { | |
657 UserImpl user = site.getUserImplFromEmail(email); | |
658 if (user == null) { | |
659 String username = email.substring(0, email.indexOf('@')); | |
660 user = createGhost(site,email); | |
661 user.setNameLike(username, false); | |
662 user.insert(); | |
663 } | |
664 return user; | |
665 } | |
666 | |
667 private static final Object regLock = new Object(); | |
668 | |
669 String newRegistration(String nextUrl) { | |
670 if( nextUrl.equals("null") ) | |
671 throw new RuntimeException("nextUrl is \"null\""); | |
672 synchronized(regLock) { | |
673 String key; | |
674 try { | |
675 Connection con = db().getConnection(); | |
676 { | |
677 PreparedStatement stmt = con.prepareStatement( | |
678 "select 'x' from registration where key_=?" | |
679 ); | |
680 do { | |
681 key = Double.toString(Math.random()); | |
682 stmt.setString(1,key); | |
683 } while( stmt.executeQuery().next() ); | |
684 stmt.close(); | |
685 } | |
686 { | |
687 PreparedStatement stmt = con.prepareStatement( | |
688 "insert into registration" | |
689 +" ( key_, email, password_digest, name, next_url ) values (?,?,?,?,?)" | |
690 ); | |
691 int i = 0; | |
692 stmt.setString(++i,key); | |
693 stmt.setString(++i,getEmail()); | |
694 stmt.setString(++i,getPasswordDigest()); | |
695 stmt.setString(++i,getName()); | |
696 stmt.setString(++i,nextUrl); | |
697 stmt.executeUpdate(); | |
698 stmt.close(); | |
699 } | |
700 { | |
701 Statement stmt = con.createStatement(); | |
702 stmt.executeUpdate( | |
703 "delete from registration where date_<" + Db.arcana.dateSub("now()",7,"day") | |
704 ); | |
705 stmt.close(); | |
706 } | |
707 con.close(); | |
708 } catch(SQLException e) { | |
709 throw new RuntimeException(e); | |
710 } | |
711 return key; | |
712 } | |
713 } | |
714 | |
715 static User getRegistration(SiteImpl site,String registrationKey) | |
716 throws ModelException | |
717 { | |
718 try { | |
719 DbDatabase db = site.getDb(); | |
720 Connection con = db.getConnection(); | |
721 PreparedStatement stmt = con.prepareStatement( | |
722 "select * from registration where key_=?" | |
723 ); | |
724 stmt.setString(1,registrationKey); | |
725 ResultSet rs = stmt.executeQuery(); | |
726 try { | |
727 if( !rs.next() ) | |
728 return null; | |
729 String email = rs.getString("email"); | |
730 String passwordDigest = rs.getString("password_digest"); | |
731 String name = rs.getString("name"); | |
732 return createUser2(site,email,passwordDigest,name); | |
733 } finally { | |
734 rs.close(); | |
735 stmt.close(); | |
736 con.close(); | |
737 } | |
738 } catch(SQLException e) { | |
739 throw new RuntimeException(e); | |
740 } | |
741 } | |
742 | |
743 static String getNextUrl(SiteKey siteKey,String registrationKey) | |
744 { | |
745 try { | |
746 Connection con = siteKey.getDb().getConnection(); | |
747 PreparedStatement stmt = con.prepareStatement( | |
748 "select next_url from registration where key_=?" | |
749 ); | |
750 stmt.setString(1,registrationKey); | |
751 ResultSet rs = stmt.executeQuery(); | |
752 try { | |
753 if( !rs.next() ) | |
754 return null; | |
755 return rs.getString("next_url"); | |
756 } finally { | |
757 rs.close(); | |
758 stmt.close(); | |
759 con.close(); | |
760 } | |
761 } catch(SQLException e) { | |
762 throw new RuntimeException(e); | |
763 } | |
764 } | |
765 | |
766 // Called from beanshell | |
767 private static void deletePendingRegistration(Site site,String email, String username) { | |
768 try { | |
769 Connection con = site.getDb().getConnection(); | |
770 PreparedStatement stmt = con.prepareStatement( | |
771 "delete from registration where email=? or name = ?" | |
772 ); | |
773 stmt.setString(1,email); | |
774 stmt.setString(2,username); | |
775 stmt.executeUpdate(); | |
776 stmt.close(); | |
777 con.close(); | |
778 } catch(SQLException e) { | |
779 throw new RuntimeException(e); | |
780 } | |
781 } | |
782 | |
783 public void deactivate() { | |
784 db().beginTransaction(); | |
785 try { | |
786 UserImpl user = DbUtils.getGoodCopy(this); | |
787 user.setNoArchive(true); | |
788 user.setRegistered(null); | |
789 user.setPasswordDigest(null); | |
790 user.record.update(); | |
791 db().commitTransaction(); | |
792 logger.info("User removed his/her account: " + getEmail()); | |
793 } finally { | |
794 db().endTransaction(); | |
795 } | |
796 } | |
797 | |
798 private DbParamSetter simpleParamSetter() { | |
799 return new DbParamSetter() { | |
800 public void setParams(PreparedStatement stmt) throws SQLException { | |
801 stmt.setLong( 1, getId() ); | |
802 } | |
803 }; | |
804 } | |
805 | |
806 public NodeIterator<? extends Node> getPendingPosts() { | |
807 return new CursorNodeIterator( siteKey, | |
808 "select *" | |
809 +" from node" | |
810 +" where owner_id = ?" | |
811 +" and when_sent is not null" | |
812 +" order by when_sent" | |
813 , simpleParamSetter() | |
814 ); | |
815 } | |
816 | |
817 public Message getSignature() { | |
818 return signature; | |
819 } | |
820 | |
821 public User setSignature( String signatureRaw, Message.Format signatureFormat ) | |
822 throws ModelException | |
823 { | |
824 if( !db().isInTransaction() ) { | |
825 db().beginTransaction(); | |
826 try { | |
827 UserImpl user = DbUtils.getGoodCopy(this); | |
828 user.setSignature(signatureRaw,signatureFormat); | |
829 user.getDbRecord().update(); | |
830 db().commitTransaction(); | |
831 return DbUtils.getGoodCopy(user); | |
832 } finally { | |
833 db().endTransaction(); | |
834 } | |
835 } | |
836 if( signatureRaw==null || signatureRaw.trim().length()==0 ) { | |
837 if( signature != null ) { | |
838 signature = null; | |
839 record.fields().put("signature",DbNull.STRING); | |
840 record.fields().put("signature_format",DbNull.STRING); | |
841 } | |
842 } else { | |
843 Message newSignature = new Message(signatureRaw,signatureFormat); | |
844 if( !newSignature.equals(signature) ) { | |
845 signature = newSignature; | |
846 record.fields().put("signature",signatureRaw); | |
847 record.fields().put("signature_format",Character.toString(signatureFormat.getCode())); | |
848 } | |
849 } | |
850 return this; | |
851 } | |
852 | |
853 | |
854 public String getDecoratedAddress(Node node) { | |
855 return PostByEmail.getMailAddress(this, node); | |
856 } | |
857 | |
858 public void saveAvatar(BufferedImage smallImage,BufferedImage bigImage) throws ModelException { | |
859 if( !db().isInTransaction() ) { | |
860 db().beginTransaction(); | |
861 try { | |
862 DbUtils.getGoodCopy(this).saveAvatar(smallImage,bigImage); | |
863 db().commitTransaction(); | |
864 } finally { | |
865 db().endTransaction(); | |
866 } | |
867 return; | |
868 } | |
869 Message.AvatarSource as = new Message.AvatarSource(this); | |
870 FileUpload.saveImage(smallImage,ModelHome.AVATAR_SMALL,as); | |
871 FileUpload.saveImage(bigImage,ModelHome.AVATAR_BIG,as); | |
872 getSiteImpl().update(); // fire change listeners | |
873 DbUtils.uncache(this); | |
874 } | |
875 | |
876 public void deleteAvatar() { | |
877 if( !db().isInTransaction() ) { | |
878 db().beginTransaction(); | |
879 try { | |
880 DbUtils.getGoodCopy(this).deleteAvatar(); | |
881 db().commitTransaction(); | |
882 } finally { | |
883 db().endTransaction(); | |
884 } | |
885 return; | |
886 } | |
887 final Message.AvatarSource as = new Message.AvatarSource(this); | |
888 FileUpload.deleteFile(ModelHome.AVATAR_SMALL,as); | |
889 FileUpload.deleteFile(ModelHome.AVATAR_BIG,as); | |
890 getSiteImpl().update(); // fire change listeners | |
891 db().runAfterCommit(new Runnable(){public void run(){ | |
892 FileUpload.fireFileUpdateListeners(as); | |
893 }}); | |
894 DbUtils.uncache(this); | |
895 } | |
896 | |
897 private boolean hasAvatar; | |
898 private boolean checkedAvatar = false; | |
899 | |
900 public synchronized boolean hasAvatar() { | |
901 if( !checkedAvatar ) { | |
902 Message.AvatarSource as = new Message.AvatarSource(this); | |
903 hasAvatar = FileUpload.hasFile(as,ModelHome.AVATAR_SMALL) && FileUpload.hasFile(as,ModelHome.AVATAR_BIG); | |
904 checkedAvatar = true; | |
905 } | |
906 return hasAvatar; | |
907 } | |
908 | |
909 | |
910 | |
911 public Node newRootNode(Node.Kind kind,String subject,String message,Message.Format msgFmt,Site site,String type) throws ModelException { | |
912 return NodeImpl.newRootNode(kind,this,subject,message,msgFmt,(SiteImpl)site,type); | |
913 } | |
914 | |
915 public Node newChildNode(Node.Kind kind,String subject,String message,Message.Format msgFmt,Node parent) throws ModelException { | |
916 return NodeImpl.newChildNode(kind,this,subject,message,msgFmt,(NodeImpl)parent); | |
917 } | |
918 | |
919 public String getSearchId() { | |
920 return Long.toString(getId()); | |
921 } | |
922 | |
923 public String getIdString() { | |
924 return Long.toString(getId()); | |
925 } | |
926 | |
927 private void clearBounces() { | |
928 if( bounces==0 ) | |
929 return; | |
930 bounces = 0; | |
931 record.fields().put("bounces",DbNull.INTEGER); | |
932 record.update(); | |
933 } | |
934 | |
935 void bounced() { | |
936 record.fields().put("bounces",++bounces); | |
937 record.update(); | |
938 } | |
939 | |
940 int getBounces() { | |
941 return bounces; | |
942 } | |
943 | |
944 private static final int bounceLimit = Init.get("bounceLimit",100); | |
945 | |
946 boolean isAutoUnsubscribe() { | |
947 return isDeactivated() || bounces > bounceLimit; | |
948 } | |
949 | |
950 | |
951 | |
952 | |
953 private volatile Map<String, Integer> nodeCount = new HashMap<String, Integer>(); | |
954 | |
955 public final int getNodeCount(String cnd) { | |
956 String key = cnd == null? "none" : cnd; | |
957 if (!nodeCount.containsKey(key)) { | |
958 try { | |
959 Connection con = db().getConnection(); | |
960 PreparedStatement stmt = con.prepareStatement( | |
961 "select count(*) as n from node where owner_id = ?" + | |
962 (cnd == null? "" : " and " + cnd) | |
963 ); | |
964 stmt.setLong(1,getId()); | |
965 ResultSet rs = stmt.executeQuery(); | |
966 rs.next(); | |
967 nodeCount.put(key, rs.getInt("n")); | |
968 rs.close(); | |
969 stmt.close(); | |
970 con.close(); | |
971 } catch(SQLException e) { | |
972 throw new RuntimeException(e); | |
973 } | |
974 } | |
975 return nodeCount.get(key); | |
976 } | |
977 | |
978 void setNodeCount(int nodeCount) { | |
979 this.nodeCount.put("none", nodeCount); | |
980 } | |
981 | |
982 static { | |
983 Listener<NodeImpl> listener = new Listener<NodeImpl>() { | |
984 public void event(NodeImpl node) { | |
985 table(node.siteKey).uncache(new LongKey(node.getOwnerId())); | |
986 } | |
987 }; | |
988 NodeImpl.postInsertListeners.add(listener); | |
989 NodeImpl.postDeleteListeners.add(listener); | |
990 } | |
991 | |
992 | |
993 public void moveToRegisteredAccount(final String cookie) { | |
994 List<NodeImpl> nodes = new CursorNodeIterator( siteKey, | |
995 "select * from node where cookie=?" | |
996 , | |
997 new DbParamSetter() { | |
998 public void setParams(PreparedStatement stmt) throws SQLException { | |
999 stmt.setString(1,cookie); | |
1000 } | |
1001 } | |
1002 ).asList(); | |
1003 for( NodeImpl n : nodes ) { | |
1004 n.setOwner(this); | |
1005 n.update(); | |
1006 } | |
1007 } | |
1008 | |
1009 | |
1010 public NodeIterator<? extends Node> getNodesByDateDesc(String cnd) { | |
1011 return new CursorNodeIterator( siteKey, | |
1012 "select * from node where owner_id = ?" + | |
1013 (cnd == null? "" : " and " + cnd) + | |
1014 " order by when_created desc" | |
1015 , | |
1016 new DbParamSetter() { | |
1017 public void setParams(PreparedStatement stmt) throws SQLException { | |
1018 stmt.setLong( 1, getId() ); | |
1019 } | |
1020 } | |
1021 ); | |
1022 } | |
1023 | |
1024 | |
1025 public int deleteNodes() { | |
1026 List<NodeImpl> nodes = new CursorNodeIterator( siteKey, | |
1027 "select *" | |
1028 +" from node" | |
1029 +" where owner_id = ?" | |
1030 , simpleParamSetter() | |
1031 ).asList(); | |
1032 int n = 0; | |
1033 for( NodeImpl node : nodes ) { | |
1034 db().beginTransaction(); | |
1035 try { | |
1036 DbUtils.getGoodCopy(node).deleteMessageOrNode(); | |
1037 db().commitTransaction(); | |
1038 n++; | |
1039 } finally { | |
1040 db().endTransaction(); | |
1041 } | |
1042 } | |
1043 return n; | |
1044 } | |
1045 | |
1046 public int deleteNodesRecursively() { | |
1047 List<NodeImpl> nodes = new CursorNodeIterator( siteKey, | |
1048 "select *" | |
1049 +" from node" | |
1050 +" where owner_id = ?" | |
1051 , simpleParamSetter() | |
1052 ).asList(); | |
1053 int n = 0; | |
1054 for( NodeImpl node : nodes ) { | |
1055 db().beginTransaction(); | |
1056 try { | |
1057 DbUtils.getGoodCopy(node).deleteRecursively(); | |
1058 db().commitTransaction(); | |
1059 n++; | |
1060 } finally { | |
1061 db().endTransaction(); | |
1062 } | |
1063 } | |
1064 return n; | |
1065 } | |
1066 | |
1067 | |
1068 private Map<ExtensionFactory<User,?>,Object> extensionMap; | |
1069 | |
1070 private synchronized Map<ExtensionFactory<User, ?>, Object> getExtensionMap() { | |
1071 if (extensionMap == null) | |
1072 extensionMap = new HashMap<ExtensionFactory<User, ?>, Object>(); | |
1073 return extensionMap; | |
1074 } | |
1075 | |
1076 public <T> T getExtension(ExtensionFactory<User,T> factory) { | |
1077 synchronized(getExtensionMap()) { | |
1078 Object obj = extensionMap.get(factory); | |
1079 if( obj == null ) { | |
1080 obj = factory.construct(this); | |
1081 if( obj != null ) | |
1082 extensionMap.put(factory,obj); | |
1083 } | |
1084 return factory.extensionClass().cast(obj); | |
1085 } | |
1086 } | |
1087 | |
1088 private static Collection<ExtensionFactory<User,?>> extensionFactories = new CopyOnWriteArrayList<ExtensionFactory<User,?>>(); | |
1089 | |
1090 static <T> void addExtensionFactory(ExtensionFactory<User,T> factory) { | |
1091 extensionFactories.add(factory); | |
1092 Db.clearCache(); | |
1093 } | |
1094 | |
1095 | |
1096 | |
1097 | |
1098 // visited node | |
1099 | |
1100 private final Map<Long,Long> visitedNodeCache = new HashMap<Long,Long>(); | |
1101 | |
1102 public Long lastVisitedNodeId(long nodeId) { | |
1103 synchronized(visitedNodeCache) { | |
1104 return visitedNodeCache.containsKey(nodeId) ? visitedNodeCache.get(nodeId) : lastVisitedNodeIds(Collections.singletonList(nodeId)).get(nodeId); | |
1105 } | |
1106 } | |
1107 | |
1108 public Map<Long,Long> lastVisitedNodeIds(Collection<Long> nodeIds) { | |
1109 synchronized(visitedNodeCache) { | |
1110 Set<Long> notCached = new HashSet<Long>(); | |
1111 for( Long nodeId : nodeIds ) { | |
1112 if( !visitedNodeCache.containsKey(nodeId) ) | |
1113 notCached.add(nodeId); | |
1114 } | |
1115 if( !notCached.isEmpty() ) { | |
1116 StringBuilder sql = new StringBuilder(); | |
1117 sql | |
1118 .append( "select node_id, last_node_id from visited where user_id = " ) | |
1119 .append( getId() ) | |
1120 .append( " and node_id in (" ) | |
1121 ; | |
1122 Iterator<Long> iter = notCached.iterator(); | |
1123 sql.append( iter.next() ); | |
1124 while( iter.hasNext() ) { | |
1125 sql.append( ',' ).append( iter.next() ); | |
1126 } | |
1127 sql.append( ")" ); | |
1128 try { | |
1129 Connection con = db().getConnection(); | |
1130 Statement stmt = con.createStatement(); | |
1131 ResultSet rs = stmt.executeQuery(sql.toString()); | |
1132 while( rs.next() ) { | |
1133 Long nodeId = rs.getLong("node_id"); | |
1134 Long lastNodeId = rs.getLong("last_node_id"); | |
1135 visitedNodeCache.put(nodeId,lastNodeId); | |
1136 notCached.remove(nodeId); | |
1137 } | |
1138 rs.close(); | |
1139 stmt.close(); | |
1140 con.close(); | |
1141 } catch(SQLException e) { | |
1142 throw new RuntimeException(e); | |
1143 } | |
1144 for( Long nodeId : notCached ) { | |
1145 visitedNodeCache.put(nodeId,null); | |
1146 } | |
1147 } | |
1148 Map<Long,Long> map = new HashMap<Long,Long>(); | |
1149 for( Long nodeId : nodeIds ) { | |
1150 map.put( nodeId, visitedNodeCache.get(nodeId) ); | |
1151 } | |
1152 return map; | |
1153 } | |
1154 } | |
1155 | |
1156 public void markVisited(Node topic, long lastNodeId) { | |
1157 NodeImpl topicNode = (NodeImpl)topic; | |
1158 long nodeId = topicNode.getId(); | |
1159 boolean updated = false; | |
1160 try { | |
1161 Connection con = db().getConnection(); | |
1162 try { | |
1163 Long persistedLastVisitedNodeId = lastVisitedNodeId(nodeId); | |
1164 if( persistedLastVisitedNodeId == null ) { | |
1165 PreparedStatement stmt = con.prepareStatement( | |
1166 "insert into visited (user_id, node_id, last_node_id)" | |
1167 +" values (?, ?, ?)" | |
1168 ); | |
1169 stmt.setLong( 1, getId() ); | |
1170 stmt.setLong( 2, nodeId ); | |
1171 stmt.setLong( 3, lastNodeId ); | |
1172 DbDatabaseImpl.executeUpdateIgnoringDuplicateKeys(stmt); | |
1173 stmt.close(); | |
1174 updated = true; | |
1175 } else if (lastNodeId > persistedLastVisitedNodeId) { | |
1176 PreparedStatement stmt = con.prepareStatement( | |
1177 "update visited set last_node_id = ?" | |
1178 +" where user_id = ? and node_id = ?" | |
1179 ); | |
1180 stmt.setLong( 1, lastNodeId ); | |
1181 stmt.setLong( 2, getId() ); | |
1182 stmt.setLong( 3, nodeId ); | |
1183 stmt.executeUpdate(); | |
1184 stmt.close(); | |
1185 updated = true; | |
1186 } | |
1187 } finally { | |
1188 con.close(); | |
1189 } | |
1190 } catch(SQLException e) { | |
1191 if( !e.getMessage().contains("violates foreign key constraint \"visited_last_node_id_fkey\"") ) | |
1192 throw new RuntimeException(e); | |
1193 } | |
1194 if (updated) { | |
1195 synchronized(visitedNodeCache) { | |
1196 visitedNodeCache.remove(nodeId); | |
1197 } | |
1198 } | |
1199 } | |
1200 | |
1201 public void unmarkVisited(Node node) { | |
1202 long nodeId = node.getId(); | |
1203 try { | |
1204 Connection con = db().getConnection(); | |
1205 PreparedStatement stmt = con.prepareStatement( | |
1206 "delete from visited" | |
1207 +" where user_id = ? and node_id = ?" | |
1208 ); | |
1209 stmt.setLong( 1, getId() ); | |
1210 stmt.setLong( 2, nodeId ); | |
1211 stmt.executeUpdate(); | |
1212 stmt.close(); | |
1213 con.close(); | |
1214 } catch(SQLException e) { | |
1215 throw new RuntimeException(e); | |
1216 } | |
1217 synchronized(visitedNodeCache) { | |
1218 visitedNodeCache.remove(nodeId); | |
1219 } | |
1220 } | |
1221 | |
1222 | |
1223 static void addPostInsertListener(final Listener<? super UserImpl> listener) { | |
1224 postInsertListeners.add(listener); | |
1225 } | |
1226 | |
1227 | |
1228 | |
1229 private final Memoizer<String,String> propertyCache = new Memoizer<String,String>(new Computable<String,String>() { | |
1230 public String get(String key) { | |
1231 try { | |
1232 Connection con = db().getConnection(); | |
1233 PreparedStatement stmt = con.prepareStatement( | |
1234 "select value from user_property where user_id = ? and key = ?" | |
1235 ); | |
1236 stmt.setLong( 1, getId() ); | |
1237 stmt.setString( 2, key ); | |
1238 ResultSet rs = stmt.executeQuery(); | |
1239 try { | |
1240 return rs.next() ? rs.getString("value") : null; | |
1241 } finally { | |
1242 rs.close(); | |
1243 stmt.close(); | |
1244 con.close(); | |
1245 } | |
1246 } catch(SQLException e) { | |
1247 throw new RuntimeException(e); | |
1248 } | |
1249 } | |
1250 }); | |
1251 | |
1252 public String getProperty(String key) { | |
1253 return propertyCache.get(key); | |
1254 } | |
1255 | |
1256 public void setProperty(String key,String value) { | |
1257 try { | |
1258 Connection con = db().getConnection(); | |
1259 PreparedStatement stmt = con.prepareStatement( | |
1260 "delete from user_property where user_id = ? and key = ?" | |
1261 ); | |
1262 stmt.setLong( 1, getId() ); | |
1263 stmt.setString( 2, key ); | |
1264 stmt.executeUpdate(); | |
1265 stmt.close(); | |
1266 if( value != null ) { | |
1267 stmt = con.prepareStatement( | |
1268 "insert into user_property (user_id,key,value) values (?,?,?)" | |
1269 ); | |
1270 stmt.setLong( 1, getId() ); | |
1271 stmt.setString( 2, key ); | |
1272 stmt.setString( 3, value ); | |
1273 stmt.executeUpdate(); | |
1274 stmt.close(); | |
1275 } | |
1276 con.close(); | |
1277 } catch(SQLException e) { | |
1278 throw new RuntimeException(e); | |
1279 } finally { | |
1280 propertyCache.remove(key); | |
1281 } | |
1282 } | |
1283 | |
1284 | |
1285 final Memoizer<String,Boolean> tagCache = new Memoizer<String,Boolean>(new Computable<String,Boolean>() { | |
1286 public Boolean get(String sqlCondition) { | |
1287 return TagImpl.countTags(siteKey,sqlCondition) > 0; | |
1288 } | |
1289 }); | |
1290 | |
1291 | |
1292 | |
1293 | |
1294 | |
1295 private final static PooledStringDigester passwordDigester = new PooledStringDigester(); | |
1296 | |
1297 static { | |
1298 passwordDigester.setAlgorithm(Init.get("passwordDigestAlgorithm","SHA-256")); | |
1299 passwordDigester.setIterations(Init.get("passwordDigestIterations",100000)); | |
1300 passwordDigester.setSaltSizeBytes(Init.get("passwordDigestSaltSize",16)); | |
1301 passwordDigester.setPoolSize(Init.get("passwordDigestPoolSize",4)); | |
1302 passwordDigester.initialize(); | |
1303 } | |
1304 | |
1305 private final static PooledStringDigester passcookieDigester = new PooledStringDigester(); | |
1306 | |
1307 static { | |
1308 passcookieDigester.setAlgorithm(Init.get("passcookieDigestAlgorithm","SHA-256")); | |
1309 passcookieDigester.setIterations(Init.get("passcookieDigestIterations",100000)); | |
1310 FixedByteArraySaltGenerator sg = new FixedByteArraySaltGenerator(); | |
1311 // this fixed salt needs to be kept secret | |
1312 sg.setSalt(Init.get("passcookieSalt", new byte[]{105, 4, 40, 78, 24, 46, 30, 100, 18, -27, 114, -21, -44, -59, 103, 43})); | |
1313 passcookieDigester.setSaltGenerator(sg); | |
1314 passcookieDigester.setPoolSize(Init.get("passcookieDigestPoolSize",4)); | |
1315 passcookieDigester.initialize(); | |
1316 } | |
1317 | |
1318 private final static PooledStringDigester resetcodeDigester = new PooledStringDigester(); | |
1319 | |
1320 static { | |
1321 resetcodeDigester.setAlgorithm(Init.get("resetcodeDigestAlgorithm","SHA-256")); | |
1322 resetcodeDigester.setIterations(Init.get("resetcodeDigestIterations",100000)); | |
1323 FixedByteArraySaltGenerator sg = new FixedByteArraySaltGenerator(); | |
1324 // this fixed salt needs to be kept secret | |
1325 sg.setSalt(Init.get("resetcodeSalt", new byte[]{-47, 9, -128, 109, 112, -88, -91, 39, 77, 111, 57, -102, 120, 12, 54, 16})); | |
1326 resetcodeDigester.setSaltGenerator(sg); | |
1327 resetcodeDigester.setPoolSize(Init.get("resetcodeDigestPoolSize",4)); | |
1328 resetcodeDigester.initialize(); | |
1329 } | |
1330 | |
1331 public boolean checkPassword(String password) { | |
1332 return passwordDigest!=null && passwordDigester.matches(password, passwordDigest); | |
1333 } | |
1334 | |
1335 private String calcPasscookie() { | |
1336 return passcookieDigester.digest(passwordDigest); | |
1337 } | |
1338 | |
1339 public boolean checkPasscookie(String passcookie) { | |
1340 return passwordDigest!=null && getPasscookie().equals(passcookie); | |
1341 } | |
1342 | |
1343 public String getResetcode() { | |
1344 return resetcodeDigester.digest(passwordDigest); | |
1345 } | |
1346 | |
1347 public boolean checkResetcode(String resetcode) { | |
1348 return passwordDigest!=null && getResetcode().equals(resetcode); | |
1349 } | |
1350 | |
1351 private static String digestPassword(String password) { | |
1352 return passwordDigester.digest(password); | |
1353 } | |
1354 | |
1355 } |