]> WPIA git - gigi.git/blob - src/org/cacert/gigi/User.java
41396ae102e53ea121ad67364d764f78f3af6469
[gigi.git] / src / org / cacert / gigi / User.java
1 package org.cacert.gigi;
2
3 import java.sql.Date;
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.util.Calendar;
8 import java.util.Locale;
9
10 import org.cacert.gigi.database.DatabaseConnection;
11 import org.cacert.gigi.localisation.Language;
12 import org.cacert.gigi.util.Notary;
13 import org.cacert.gigi.util.PasswordHash;
14 import org.cacert.gigi.util.PasswordStrengthChecker;
15
16 public class User implements IdCachable {
17
18     private int id;
19
20     private Name name = new Name(null, null, null, null);
21
22     private Date dob;
23
24     private String email;
25
26     private Assurance[] receivedAssurances, madeAssurances;
27
28     private Locale locale;
29
30     public User(int id) {
31         this.id = id;
32         updateName(id);
33     }
34
35     private void updateName(int id) {
36         try {
37             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `fname`, `lname`,`mname`, `suffix`, `dob`, `email`, `language` FROM `users` WHERE id=?");
38             ps.setInt(1, id);
39             ResultSet rs = ps.executeQuery();
40             if (rs.next()) {
41                 name = new Name(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4));
42                 dob = rs.getDate(5);
43                 email = rs.getString(6);
44                 String localeStr = rs.getString(7);
45                 if (localeStr == null || localeStr.equals("")) {
46                     locale = Locale.getDefault();
47                 } else {
48                     locale = Language.getLocaleFromString(localeStr);
49                 }
50             }
51             rs.close();
52         } catch (SQLException e) {
53             e.printStackTrace();
54         }
55     }
56
57     public User() {}
58
59     public int getId() {
60         return id;
61     }
62
63     public String getFname() {
64         return name.fname;
65     }
66
67     public String getLname() {
68         return name.lname;
69     }
70
71     public String getMname() {
72         return name.mname;
73     }
74
75     public Name getName() {
76         return name;
77     }
78
79     public void setMname(String mname) {
80         this.name.mname = mname;
81     }
82
83     public String getSuffix() {
84         return name.suffix;
85     }
86
87     public void setSuffix(String suffix) {
88         this.name.suffix = suffix;
89     }
90
91     public Date getDob() {
92         return dob;
93     }
94
95     public void setDob(Date dob) {
96         this.dob = dob;
97     }
98
99     public String getEmail() {
100         return email;
101     }
102
103     public void setEmail(String email) {
104         this.email = email;
105     }
106
107     public void setId(int id) {
108         this.id = id;
109     }
110
111     public void setFname(String fname) {
112         this.name.fname = fname;
113     }
114
115     public void setLname(String lname) {
116         this.name.lname = lname;
117     }
118
119     public void insert(String password) throws SQLException {
120         if (id != 0) {
121             throw new Error("refusing to insert");
122         }
123         PreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `created`=NOW(), locked=0, `language`=?");
124         query.setString(1, email);
125         query.setString(2, PasswordHash.hash(password));
126         query.setString(3, name.fname);
127         query.setString(4, name.mname);
128         query.setString(5, name.lname);
129         query.setString(6, name.suffix);
130         query.setDate(7, new java.sql.Date(dob.getTime()));
131         query.setString(8, locale.toString());
132         synchronized (User.class) {
133             query.execute();
134             id = DatabaseConnection.lastInsertId(query);
135             myCache.put(this);
136         }
137     }
138
139     public void changePassword(String oldPass, String newPass) throws GigiApiException {
140         try {
141             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `password` FROM users WHERE id=?");
142             ps.setInt(1, id);
143             ResultSet rs = ps.executeQuery();
144             if ( !rs.next()) {
145                 throw new GigiApiException("User not found... very bad.");
146             }
147             if ( !PasswordHash.verifyHash(oldPass, rs.getString(1))) {
148                 throw new GigiApiException("Old password does not match.");
149             }
150             rs.close();
151             PasswordStrengthChecker.assertStrongPassword(newPass, this);
152             ps = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=? WHERE id=?");
153             ps.setString(1, PasswordHash.hash(newPass));
154             ps.setInt(2, id);
155             if (ps.executeUpdate() != 1) {
156                 throw new GigiApiException("Password update failed.");
157             }
158         } catch (SQLException e) {
159             throw new GigiApiException(e);
160         }
161     }
162
163     public boolean canAssure() throws SQLException {
164         if (getAssurancePoints() < 100) {
165             return false;
166         }
167
168         return hasPassedCATS();
169
170     }
171
172     public boolean hasPassedCATS() throws SQLException {
173         PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `cats_passed` where `user_id`=?");
174         query.setInt(1, id);
175         ResultSet rs = query.executeQuery();
176         if (rs.next()) {
177             return true;
178         } else {
179             return false;
180         }
181     }
182
183     public int getAssurancePoints() throws SQLException {
184         PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT sum(points) FROM `notary` where `to`=? AND `deleted`=0");
185         query.setInt(1, id);
186         ResultSet rs = query.executeQuery();
187         int points = 0;
188         if (rs.next()) {
189             points = rs.getInt(1);
190         }
191         rs.close();
192         return points;
193     }
194
195     public int getExperiencePoints() throws SQLException {
196         PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT count(*) FROM `notary` where `from`=? AND `deleted`=0");
197         query.setInt(1, id);
198         ResultSet rs = query.executeQuery();
199         int points = 0;
200         if (rs.next()) {
201             points = rs.getInt(1) * 2;
202         }
203         rs.close();
204         return points;
205     }
206
207     @Override
208     public boolean equals(Object obj) {
209         if ( !(obj instanceof User)) {
210             return false;
211         }
212         User s = (User) obj;
213         return name.equals(s.name) && email.equals(s.email) && dob.toString().equals(s.dob.toString()); // This
214                                                                                                         // is
215                                                                                                         // due
216                                                                                                         // to
217                                                                                                         // day
218                                                                                                         // cutoff
219     }
220
221     /**
222      * Gets the maximum allowed points NOW. Note that an assurance needs to
223      * re-check PoJam as it has taken place in the past.
224      * 
225      * @return the maximal points
226      * @throws SQLException
227      */
228     public int getMaxAssurePoints() throws SQLException {
229         int exp = getExperiencePoints();
230         int points = 10;
231         Calendar c = Calendar.getInstance();
232         c.setTime(dob);
233         int year = c.get(Calendar.YEAR);
234         int month = c.get(Calendar.MONTH);
235         int day = c.get(Calendar.DAY_OF_MONTH);
236         c.set(year + 18, month, day);
237         if (System.currentTimeMillis() < c.getTime().getTime()) {
238             return points; // not 18 Years old.
239         }
240
241         if (exp >= 10) {
242             points += 5;
243         }
244         if (exp >= 20) {
245             points += 5;
246         }
247         if (exp >= 30) {
248             points += 5;
249         }
250         if (exp >= 40) {
251             points += 5;
252         }
253         if (exp >= 50) {
254             points += 5;
255         }
256         return points;
257     }
258
259     public EmailAddress[] getEmails() {
260         try {
261             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM emails WHERE memid=? AND deleted=0");
262             ps.setInt(1, id);
263             ResultSet rs = ps.executeQuery();
264             rs.last();
265             int count = rs.getRow();
266             EmailAddress[] data = new EmailAddress[count];
267             rs.beforeFirst();
268             for (int i = 0; i < data.length; i++) {
269                 if ( !rs.next()) {
270                     throw new Error("Internal sql api violation.");
271                 }
272                 data[i] = EmailAddress.getById(rs.getInt(1));
273             }
274             rs.close();
275             return data;
276         } catch (SQLException e) {
277             e.printStackTrace();
278         }
279
280         return null;
281     }
282
283     public Domain[] getDomains() {
284         try {
285             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM domains WHERE memid=? AND deleted IS NULL");
286             ps.setInt(1, id);
287             ResultSet rs = ps.executeQuery();
288             rs.last();
289             int count = rs.getRow();
290             Domain[] data = new Domain[count];
291             rs.beforeFirst();
292             for (int i = 0; i < data.length; i++) {
293                 if ( !rs.next()) {
294                     throw new Error("Internal sql api violation.");
295                 }
296                 data[i] = Domain.getById(rs.getInt(1));
297             }
298             rs.close();
299             return data;
300         } catch (SQLException e) {
301             e.printStackTrace();
302         }
303
304         return null;
305     }
306
307     public Certificate[] getCertificates() {
308         try {
309             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT serial FROM certs WHERE memid=? AND revoked=0");
310             ps.setInt(1, id);
311             ResultSet rs = ps.executeQuery();
312             rs.last();
313             int count = rs.getRow();
314             Certificate[] data = new Certificate[count];
315             rs.beforeFirst();
316             for (int i = 0; i < data.length; i++) {
317                 if ( !rs.next()) {
318                     throw new Error("Internal sql api violation.");
319                 }
320                 data[i] = Certificate.getBySerial(rs.getString(1));
321             }
322             rs.close();
323             return data;
324         } catch (SQLException e) {
325             e.printStackTrace();
326         }
327
328         return null;
329     }
330
331     public boolean isValidDomain(String domainname) {
332         for (Domain d : getDomains()) {
333             String sfx = d.getSuffix();
334             if (domainname.equals(sfx) || domainname.endsWith("." + sfx)) {
335                 return true;
336             }
337         }
338         return false;
339     }
340
341     public boolean isValidEmail(String email) {
342         for (EmailAddress em : getEmails()) {
343             if (em.getAddress().equals(email)) {
344                 return true;
345             }
346         }
347         return false;
348     }
349
350     public boolean isValidName(String name) {
351         return getName().matches(name);
352     }
353
354     public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
355         try {
356             EmailAddress[] adrs = getEmails();
357             for (int i = 0; i < adrs.length; i++) {
358                 if (adrs[i].getAddress().equals(newMail.getAddress())) {
359                     if ( !adrs[i].isVerified()) {
360                         throw new GigiApiException("Email not verified.");
361                     }
362                     PreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE users SET email=? WHERE id=?");
363                     ps.setString(1, newMail.getAddress());
364                     ps.setInt(2, getId());
365                     ps.execute();
366                     email = newMail.getAddress();
367                     return;
368                 }
369             }
370             throw new GigiApiException("Given address not an address of the user.");
371         } catch (SQLException e) {
372             throw new GigiApiException(e);
373         }
374     }
375
376     public void deleteEmail(EmailAddress mail) throws GigiApiException {
377         if (getEmail().equals(mail.getAddress())) {
378             throw new GigiApiException("Can't delete user's default e-mail.");
379         }
380         EmailAddress[] emails = getEmails();
381         for (int i = 0; i < emails.length; i++) {
382             if (emails[i].getId() == mail.getId()) {
383                 try {
384                     PreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE emails SET deleted=? WHERE id=?");
385                     ps.setDate(1, new Date(System.currentTimeMillis()));
386                     ps.setInt(2, mail.getId());
387                     ps.execute();
388                 } catch (SQLException e) {
389                     e.printStackTrace();
390                     throw new GigiApiException(e);
391                 }
392                 return;
393             }
394         }
395         throw new GigiApiException("Email not one of user's email addresses.");
396     }
397
398     public Assurance[] getReceivedAssurances() throws SQLException {
399         if (receivedAssurances == null) {
400             PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `to`=? AND deleted=0");
401             query.setInt(1, getId());
402             ResultSet res = query.executeQuery();
403             res.last();
404             Assurance[] assurances = new Assurance[res.getRow()];
405             res.beforeFirst();
406             for (int i = 0; i < assurances.length; i++) {
407                 res.next();
408                 assurances[i] = new Assurance(res);
409             }
410             this.receivedAssurances = assurances;
411             return assurances;
412         }
413         return receivedAssurances;
414     }
415
416     public Assurance[] getMadeAssurances() throws SQLException {
417         if (madeAssurances == null) {
418             PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted=0");
419             query.setInt(1, getId());
420             ResultSet res = query.executeQuery();
421             res.last();
422             Assurance[] assurances = new Assurance[res.getRow()];
423             res.beforeFirst();
424             for (int i = 0; i < assurances.length; i++) {
425                 res.next();
426                 assurances[i] = new Assurance(res);
427             }
428             this.madeAssurances = assurances;
429             return assurances;
430         }
431         return madeAssurances;
432     }
433
434     public void invalidateMadeAssurances() {
435         madeAssurances = null;
436     }
437
438     public void invalidateReceivedAssurances() {
439         receivedAssurances = null;
440     }
441
442     public void updateUserData() throws SQLException, GigiApiException {
443         synchronized (Notary.class) {
444             if (getAssurancePoints() != 0) {
445                 throw new GigiApiException("No change after assurance allowed.");
446             }
447             PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET fname=?, lname=?, mname=?, suffix=?, dob=? WHERE id=?");
448             update.setString(1, getFname());
449             update.setString(2, getLname());
450             update.setString(3, getMname());
451             update.setString(4, getSuffix());
452             update.setDate(5, getDob());
453             update.setInt(6, getId());
454             update.executeUpdate();
455         }
456     }
457
458     public Locale getPreferredLocale() {
459         return locale;
460     }
461
462     public void setPreferredLocale(Locale locale) {
463         this.locale = locale;
464
465     }
466
467     public boolean wantsDirectoryListing() throws SQLException {
468         PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
469         get.setInt(1, getId());
470         ResultSet exec = get.executeQuery();
471         exec.next();
472         return exec.getBoolean("listme");
473     }
474
475     public String getContactInformation() throws SQLException {
476         PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
477         get.setInt(1, getId());
478         ResultSet exec = get.executeQuery();
479         exec.next();
480         return exec.getString("contactinfo");
481     }
482
483     public void setDirectoryListing(boolean on) throws SQLException {
484         PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
485         update.setBoolean(1, on);
486         update.setInt(2, getId());
487         update.executeUpdate();
488     }
489
490     public void setContactInformation(String contactInfo) throws SQLException {
491         PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
492         update.setString(1, contactInfo);
493         update.setInt(2, getId());
494         update.executeUpdate();
495     }
496
497     private static ObjectCache<User> myCache = new ObjectCache<>();
498
499     public static User getById(int id) {
500         User u = myCache.get(id);
501         if (u == null) {
502             synchronized (User.class) {
503                 myCache.put(u = new User(id));
504             }
505         }
506         return u;
507     }
508 }