}
- public static final int CURRENT_SCHEMA_VERSION = 9;
+ public static final int CURRENT_SCHEMA_VERSION = 10;
public static final int CONNECTION_TIMEOUT = 24 * 60 * 60;
Statement s = getInstance().c.createStatement();
try {
while (version < CURRENT_SCHEMA_VERSION) {
- try (InputStream resourceAsStream = DatabaseConnection.class.getResourceAsStream("upgrade/from_" + version + ".sql")) {
- if (resourceAsStream == null) {
- throw new Error("Upgrade script from version " + version + " was not found.");
- }
- SQLFileManager.addFile(s, resourceAsStream, ImportType.PRODUCTION);
- }
+ addUpgradeScript(Integer.toString(version), s);
version++;
}
s.addBatch("UPDATE \"schemeVersion\" SET version='" + version + "'");
}
}
+ private static void addUpgradeScript(String version, Statement s) throws Error, IOException, SQLException {
+ try (InputStream resourceAsStream = DatabaseConnection.class.getResourceAsStream("upgrade/from_" + version + ".sql")) {
+ if (resourceAsStream == null) {
+ throw new Error("Upgrade script from version " + version + " was not found.");
+ }
+ SQLFileManager.addFile(s, resourceAsStream, ImportType.PRODUCTION);
+ }
+ }
+
public static final String preprocessQuery(String originalQuery) {
originalQuery = originalQuery.replace('`', '"');
if (originalQuery.matches("^INSERT INTO [^ ]+ SET .*")) {
"created" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"modified" timestamp NULL DEFAULT NULL,
"deleted" timestamp NULL DEFAULT NULL,
- "hash" varchar(50) NOT NULL DEFAULT '',
- "attempts" smallint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
);
CREATE INDEX ON "emails" ("memid");
-CREATE INDEX ON "emails" ("hash");
CREATE INDEX ON "emails" ("deleted");
CREATE INDEX ON "emails" ("email");
"email" varchar(255) NOT NULL,
"type" "emailPingType" NOT NULL,
"status" "pingState" NOT NULL,
- "result" varchar(255) NOT NULL
+ "result" varchar(255) NOT NULL,
+ "challenge" varchar(255) NULL DEFAULT NULL
);
DROP TABLE IF EXISTS "pingconfig";
"version" smallint NOT NULL,
PRIMARY KEY ("version")
);
-INSERT INTO "schemeVersion" (version) VALUES(9);
+INSERT INTO "schemeVersion" (version) VALUES(10);
DROP TABLE IF EXISTS `passwordResetTickets`;
CREATE TABLE `passwordResetTickets` (
--- /dev/null
+ALTER TABLE "emailPinglog" ADD COLUMN "challenge" varchar(255) NULL DEFAULT NULL;
+
+INSERT INTO "emailPinglog" SELECT CURRENT_TIMESTAMP AS "when", "memid" AS "uid", "email", 'active'::"emailPingType" AS "type", CASE WHEN "hash"='' THEN 'success'::"pingState" ELSE 'open'::"pingState" END AS state, '' AS result, "hash" AS "challenge" FROM "emails";
+ALTER TABLE "emails" DROP COLUMN "attempts";
+ALTER TABLE "emails" DROP COLUMN "hash";
package org.cacert.gigi.dbObjects;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
import org.cacert.gigi.GigiApiException;
import org.cacert.gigi.database.GigiPreparedStatement;
import org.cacert.gigi.email.EmailProvider;
import org.cacert.gigi.email.MailProbe;
import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.template.Scope;
+import org.cacert.gigi.output.template.SprintfCommand;
import org.cacert.gigi.util.RandomToken;
public class EmailAddress implements IdCachable, Verifyable {
+ public static final int REPING_MINIMUM_DELAY = 5 * 60 * 1000;
+
private String address;
private int id;
private User owner;
- private String hash = null;
-
private EmailAddress(int id) {
- try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `memid`, `email`, `hash` FROM `emails` WHERE `id`=? AND `deleted` IS NULL")) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `memid`, `email` FROM `emails` WHERE `id`=? AND `deleted` IS NULL")) {
ps.setInt(1, id);
GigiResultSet rs = ps.executeQuery();
this.id = id;
owner = User.getById(rs.getInt(1));
address = rs.getString(2);
- hash = rs.getString(3);
}
}
}
this.address = address;
this.owner = owner;
- this.hash = RandomToken.generateToken(16);
insert(Language.getInstance(mailLocale));
}
if (id != 0) {
throw new IllegalStateException("already inserted.");
}
- try (GigiPreparedStatement psCheck = new GigiPreparedStatement("SELECT 1 FROM `emails` WHERE email=? AND deleted is NULL"); GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `emails` SET memid=?, hash=?, email=?")) {
+ try (GigiPreparedStatement psCheck = new GigiPreparedStatement("SELECT 1 FROM `emails` WHERE email=? AND deleted is NULL"); GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `emails` SET memid=?, email=?")) {
ps.setInt(1, owner.getId());
- ps.setString(2, hash);
- ps.setString(3, address);
+ ps.setString(2, address);
psCheck.setString(1, address);
GigiResultSet res = psCheck.executeQuery();
if (res.next()) {
}
myCache.put(this);
}
- MailProbe.sendMailProbe(l, "email", id, hash, address);
+ ping(l);
} catch (IOException e) {
e.printStackTrace();
}
}
+ private void ping(Language l) throws IOException {
+ String hash = RandomToken.generateToken(16);
+ try (GigiPreparedStatement statmt = new GigiPreparedStatement("INSERT INTO `emailPinglog` SET `when`=NOW(), `email`=?, `result`='', `uid`=?, `type`='active', `status`='open'::`pingState`, `challenge`=?")) {
+ statmt.setString(1, address);
+ statmt.setInt(2, owner.getId());
+ statmt.setString(3, hash);
+ statmt.execute();
+ }
+
+ MailProbe.sendMailProbe(l, "email", id, hash, address);
+ }
+
public int getId() {
return id;
}
}
public synchronized void verify(String hash) throws GigiApiException {
- if (this.hash.equals(hash)) {
- try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `emails` SET hash='' WHERE id=?")) {
- ps.setInt(1, id);
- ps.execute();
- }
- hash = "";
+ try (GigiPreparedStatement stmt = new GigiPreparedStatement("UPDATE `emailPinglog` SET `status`='success'::`pingState` WHERE `email`=? AND `uid`=? AND `type`='active' AND `challenge`=?")) {
+ stmt.setString(1, address);
+ stmt.setInt(2, owner.getId());
+ stmt.setString(3, hash);
+ stmt.executeUpdate();
+ }
+ // Verify user with that primary email
+ try (GigiPreparedStatement ps2 = new GigiPreparedStatement("update `users` set `verified`='1' where `id`=? and `email`=? and `verified`='0'")) {
+ ps2.setInt(1, owner.getId());
+ ps2.setString(2, address);
+ ps2.execute();
+ }
+ }
- // Verify user with that primary email
- try (GigiPreparedStatement ps2 = new GigiPreparedStatement("update `users` set `verified`='1' where `id`=? and `email`=? and `verified`='0'")) {
- ps2.setInt(1, owner.getId());
- ps2.setString(2, address);
- ps2.execute();
- }
- this.hash = "";
+ public boolean isVerified() {
+ try (GigiPreparedStatement statmt = new GigiPreparedStatement("SELECT 1 FROM `emailPinglog` WHERE `email`=? AND `uid`=? AND `type`='active' AND `status`='success'")) {
+ statmt.setString(1, address);
+ statmt.setInt(2, owner.getId());
+ GigiResultSet e = statmt.executeQuery();
+ return e.next();
+ }
+ }
- } else {
- throw new GigiApiException("Email verification hash is invalid.");
+ public Date getLastPing(boolean onlySuccess) {
+ Date lastExecution;
+ try (GigiPreparedStatement statmt = new GigiPreparedStatement("SELECT MAX(`when`) FROM `emailPinglog` WHERE `email`=? AND `uid`=? AND `type`='active'" + (onlySuccess ? " AND `status`='success'" : ""))) {
+ statmt.setString(1, address);
+ statmt.setInt(2, owner.getId());
+ GigiResultSet e = statmt.executeQuery();
+ if ( !e.next()) {
+ return null;
+ }
+ lastExecution = e.getTimestamp(1);
}
+ return lastExecution;
}
- public boolean isVerified() {
- return hash.isEmpty();
+ public synchronized void requestReping(Language l) throws IOException, GigiApiException {
+ Date lastExecution = getLastPing(false);
+
+ if (lastExecution != null && lastExecution.getTime() + REPING_MINIMUM_DELAY >= System.currentTimeMillis()) {
+ Map<String, Object> data = new HashMap<String, Object>();
+ data.put("data", new Date(lastExecution.getTime() + REPING_MINIMUM_DELAY));
+ throw new GigiApiException(new Scope(new SprintfCommand("Reping is only allowed after 5 minutes, yours end at {0}.", Arrays.asList("${data}")), data));
+ }
+ ping(l);
+ return;
}
private static ObjectCache<EmailAddress> myCache = new ObjectCache<>();
package org.cacert.gigi.pages.account.mail;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
+import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
@Override
public boolean submit(PrintWriter out, HttpServletRequest req) {
- if (req.getParameter("makedefault") != null) {
- try {
- String mailid = req.getParameter("emailid");
- if (mailid == null) {
- return false;
+ Map<String, String[]> map = req.getParameterMap();
+ try {
+ for (Entry<String, String[]> e : map.entrySet()) {
+ String k = e.getKey();
+ String[] p = k.split(":", 2);
+ if (p[0].equals("default")) {
+ target.updateDefaultEmail(EmailAddress.getById(Integer.parseInt(p[1])));
}
- target.updateDefaultEmail(EmailAddress.getById(Integer.parseInt(mailid.trim())));
- } catch (GigiApiException e) {
- e.format(out, Page.getLanguage(req));
- return false;
- }
- return true;
- }
- if (req.getParameter("delete") != null) {
- String[] toDel = req.getParameterValues("delid[]");
- if (toDel == null) {
- return false;
- }
- for (int i = 0; i < toDel.length; i++) {
- try {
- target.deleteEmail(EmailAddress.getById(Integer.parseInt(toDel[i].trim())));
- } catch (GigiApiException e) {
- e.format(out, Page.getLanguage(req));
- return false;
+ if (p[0].equals("delete")) {
+ target.deleteEmail(EmailAddress.getById(Integer.parseInt(p[1])));
+ }
+ if (p[0].equals("reping")) {
+ EmailAddress.getById(Integer.parseInt(p[1])).requestReping(Page.getLanguage(req));
}
}
- return true;
-
+ } catch (GigiApiException e) {
+ e.format(out, Page.getLanguage(req));
+ return false;
+ } catch (IOException e1) {
+ new GigiApiException("Error while doing reping.").format(out, Page.getLanguage(req));
+ return false;
}
- return false;
+ return true;
}
@Override
int mailID = emailAddress.getId();
vars.put("id", mailID);
if (emailAddress.getAddress().equals(target.getEmail())) {
- vars.put("checked", "checked");
+ vars.put("default", " disabled");
} else {
- vars.put("checked", "");
+ vars.put("default", "");
}
if (emailAddress.isVerified()) {
vars.put("verification", "Verified");
<tr>
<td><?=_Default?></td>
<td><?=_Status?></td>
- <td><?=_Delete?></td>
<td><?=_Address?></td>
+ <td><?=_Delete?></td>
+ <td><?=_Request reping?></td>
</tr>
<? foreach($emails) {?>
<tr>
- <td><input type="radio" name="emailid" value="<?=$id?>" <?=$!checked?>></td>
+ <td><input type="submit" name="default:<?=$id?>" value="<?=_Set as Default?>"<?=$default?>></td>
<td><?=$verification?></td>
- <td><?=$!delete?></td>
<td><?=$address?></td>
+ <td><input type="submit" name="delete:<?=$id?>" value="<?=_Delete?>"<?=$default?>></td>
+ <td><input type="submit" name="reping:<?=$id?>" value="<?=_Request reping?>"></td>
</tr>
<? } ?>
- <tr>
- <td colspan="2"><input type="submit" name="makedefault" value="Setze als Standard"></td>
- <td colspan="2"><input type="submit" name="delete" value="Löschen"></td>
- </tr>
</tbody>
</table>
if (f.submit(out, req)) {
resp.sendRedirect(MailOverview.DEFAULT_PATH);
}
- } else if (req.getParameter("makedefault") != null || req.getParameter("delete") != null) {
+ } else {
MailManagementForm f = Form.getForm(req, MailManagementForm.class);
if (f.submit(out, req)) {
resp.sendRedirect(MailOverview.DEFAULT_PATH);
@Test
public void testMailSetDefaultWeb() throws MalformedURLException, UnsupportedEncodingException, IOException, InterruptedException, GigiApiException {
- EmailAddress adrr = createVerifiedEmail(u);
- assertNull(executeBasicWebInteraction(cookie, path, "makedefault&emailid=" + adrr.getId()));
+ EmailAddress addr = createVerifiedEmail(u);
+ assertNull(executeBasicWebInteraction(cookie, path, "default:" + addr.getId()));
ObjectCache.clearAllCaches();
- assertEquals(User.getById(u.getId()).getEmail(), adrr.getAddress());
+ assertEquals(User.getById(u.getId()).getEmail(), addr.getAddress());
}
@Test
public void testMailSetDefaultWebUnverified() throws MalformedURLException, UnsupportedEncodingException, IOException, InterruptedException, GigiApiException {
- EmailAddress adrr = new EmailAddress(u, createUniqueName() + "test@test.tld", Locale.ENGLISH);
- assertNotNull(executeBasicWebInteraction(cookie, path, "makedefault&emailid=" + adrr.getId()));
- assertNotEquals(User.getById(u.getId()).getEmail(), adrr.getAddress());
+ EmailAddress addr = new EmailAddress(u, createUniqueName() + "test@test.tld", Locale.ENGLISH);
+ assertNotNull(executeBasicWebInteraction(cookie, path, "default:" + addr.getId()));
+ assertNotEquals(User.getById(u.getId()).getEmail(), addr.getAddress());
getMailReciever().clearMails();
}
}
}
assertNotEquals(id, -1);
- assertNotNull(executeBasicWebInteraction(cookie, path, "makedefault&emailid=" + id));
+ assertNotNull(executeBasicWebInteraction(cookie, path, "default:" + id));
assertNotEquals(User.getById(u.getId()).getEmail(), u2.getEmail());
getMailReciever().clearMails();
}
@Test
public void testMailDeleteWeb() throws InterruptedException, GigiApiException, MalformedURLException, UnsupportedEncodingException, IOException {
EmailAddress addr = createVerifiedEmail(u);
- assertNull(executeBasicWebInteraction(cookie, path, "delete&delid[]=" + addr.getId(), 0));
+ assertNull(executeBasicWebInteraction(cookie, path, "delete:" + addr.getId(), 0));
User u = User.getById(this.u.getId());
EmailAddress[] addresses = u.getEmails();
for (int i = 0; i < addresses.length; i++) {
EmailAddress[] addr = new EmailAddress[] {
createVerifiedEmail(u), createVerifiedEmail(u)
};
- assertNull(executeBasicWebInteraction(cookie, path, "delete&delid[]=" + addr[0].getId() + "&delid[]=" + addr[1].getId(), 0));
+ assertNull(executeBasicWebInteraction(cookie, path, "delete:" + addr[0].getId() + "&delete:" + addr[1].getId(), 0));
User u = User.getById(this.u.getId());
EmailAddress[] addresses = u.getEmails();
for (int i = 0; i < addresses.length; i++) {
public void testMailDeleteWebFaulty() throws MalformedURLException, UnsupportedEncodingException, IOException {
User u2 = User.getById(createVerifiedUser("fn", "ln", createUniqueName() + "uni@test.tld", TEST_PASSWORD));
EmailAddress em = u2.getEmails()[0];
- assertNotNull(executeBasicWebInteraction(cookie, path, "delete&delid[]=" + em.getId(), 0));
+ assertNotNull(executeBasicWebInteraction(cookie, path, "delete:" + em.getId(), 0));
u2 = User.getById(u2.getId());
assertNotEquals(u2.getEmails().length, 0);
}
@Test
public void testMailDeleteWebPrimary() throws MalformedURLException, UnsupportedEncodingException, IOException {
- assertNotNull(executeBasicWebInteraction(cookie, path, "delete&delid[]=" + u.getEmails()[0].getId(), 0));
+ assertNotNull(executeBasicWebInteraction(cookie, path, "delete:" + u.getEmails()[0].getId(), 0));
assertNotEquals(u.getEmails().length, 0);
}
}
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public static final String PATH = "/manager";
- Field f;
-
private static HashMap<DomainPingType, DomainPinger> dps;
private Manager() {
super("Test Manager");
- try {
- f = EmailAddress.class.getDeclaredField("hash");
- f.setAccessible(true);
- } catch (ReflectiveOperationException e) {
- // TODO
- System.out.println("I don't have 'hash', we are working probably in layered mode. Test Manager may not work.");
- // throw new Error(e);
- }
try {
Field gigiInstance = Gigi.class.getDeclaredField("instance");
gc.set(1990, 0, 1);
User u = new User(email, "xvXV12°§", new Name("Först", "Läst", "Müddle", "Süffix"), new DayDate(gc.getTime().getTime()), Locale.ENGLISH);
EmailAddress ea = u.getEmails()[0];
- if (f == null) {
- System.out.println("verification failed");
- return;
- }
- String hash = (String) f.get(ea);
+ verify(email, ea);
+ }
- ea.verify(hash);
+ private void verify(String email, EmailAddress ea) throws GigiApiException {
+ LinkedList<String> i = emails.get(email);
+ while (i.size() > 0 && !ea.isVerified()) {
+ String lst = i.getLast();
+ Pattern p = Pattern.compile("hash=([a-zA-Z0-9]+)");
+ Matcher m = p.matcher(lst);
+ if (m.find()) {
+ ea.verify(m.group(1));
+ }
+ i.removeLast();
+ }
+ // ea.verify(hash);
}
User[] assurers = new User[25];
User u = User.getByEmail(req.getParameter("addEmailEmail"));
try {
EmailAddress ea = new EmailAddress(u, req.getParameter("addEmailNew"), Locale.ENGLISH);
- if (f != null) {
- String hash = (String) f.get(ea);
- ea.verify(hash);
- resp.getWriter().println("Email added and verified");
- } else {
- resp.getWriter().println("Email added but verificatio failed.");
- }
+ verify(ea.getAddress(), ea);
} catch (IllegalArgumentException e) {
e.printStackTrace();
resp.getWriter().println("An internal error occured.");
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- resp.getWriter().println("An internal error occured.");
} catch (GigiApiException e) {
e.format(resp.getWriter(), Language.getInstance(Locale.ENGLISH));
}