]> WPIA git - gigi.git/commitdiff
Merge branch 'autotesting'
authorFelix Dörre <felix@dogcraft.de>
Wed, 25 Jun 2014 14:33:48 +0000 (16:33 +0200)
committerFelix Dörre <felix@dogcraft.de>
Wed, 25 Jun 2014 14:33:48 +0000 (16:33 +0200)
Conflicts:
tests/org/cacert/gigi/pages/main/RegisterPageTest.java

Port config is in "test.properties"

34 files changed:
.classpath
config/.gitignore
config/gigi.properties.template [new file with mode: 0644]
config/sql.properties.template [deleted file]
config/test.properties.template [new file with mode: 0644]
doc/tableStructure.sql [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/Octal.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarConstants.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarEntry.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarHeader.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarInputStream.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarOutputStream.java [new file with mode: 0644]
lib/jtar/org/kamranzafar/jtar/TarUtils.java [new file with mode: 0644]
src/org/cacert/gigi/DevelLauncher.java [new file with mode: 0644]
src/org/cacert/gigi/Gigi.java
src/org/cacert/gigi/GigiConfig.java [new file with mode: 0644]
src/org/cacert/gigi/Launcher.java
src/org/cacert/gigi/User.java
src/org/cacert/gigi/database/DatabaseConnection.java
src/org/cacert/gigi/email/CommandlineEmailProvider.java [new file with mode: 0644]
src/org/cacert/gigi/email/EmailChecker.java [deleted file]
src/org/cacert/gigi/email/EmailProvider.java
src/org/cacert/gigi/email/Sendmail.java
src/org/cacert/gigi/email/TestEmailProvider.java [new file with mode: 0644]
src/org/cacert/gigi/pages/LoginPage.java
src/org/cacert/gigi/pages/Verify.java [new file with mode: 0644]
src/org/cacert/gigi/pages/main/Signup.java
src/org/cacert/gigi/util/Notary.java
src/org/cacert/gigi/util/ServerConstants.java
tests/org/cacert/gigi/LoginTest.java [new file with mode: 0644]
tests/org/cacert/gigi/pages/main/RegisterPageTest.java
tests/org/cacert/gigi/testUtils/ManagedTest.java [new file with mode: 0644]
tests/org/cacert/gigi/testUtils/TestEmailReciever.java [new file with mode: 0644]
util/org/cacert/gigi/util/DatabaseManager.java [new file with mode: 0644]

index 938989c9cf02f3698f1aeba08717f2b299ede318..c9c9607c035e390db37c9fb602bcd860e83be660 100644 (file)
@@ -2,6 +2,7 @@
 <classpath>
        <classpathentry kind="src" path="lib/servlet-api"/>
        <classpathentry kind="src" path="lib/jetty"/>
+       <classpathentry kind="src" path="lib/jtar"/>
        <classpathentry kind="src" path="src"/>
        <classpathentry kind="src" path="util"/>
        <classpathentry kind="src" path="tests"/>
index ef8da91c0f7ba976e2a97412261e5a466e626e45..0aa754e94a4359acbd5cab51f58abf6941d6297c 100644 (file)
@@ -2,3 +2,5 @@
 keystore.pkcs12
 cacerts.jks
 sql.properties
+gigi.properties
+test.properties
diff --git a/config/gigi.properties.template b/config/gigi.properties.template
new file mode 100644 (file)
index 0000000..ab46068
--- /dev/null
@@ -0,0 +1,7 @@
+host=127.0.0.1
+port=443
+emailProvider=org.cacert.gigi.email.Sendmail
+sql.driver=com.mysql.jdbc.Driver
+sql.url=jdbc:mysql://
+sql.user=
+sql.password=
diff --git a/config/sql.properties.template b/config/sql.properties.template
deleted file mode 100644 (file)
index 8257406..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-driver=com.mysql.jdbc.Driver
-url=
-user=
-password=
diff --git a/config/test.properties.template b/config/test.properties.template
new file mode 100644 (file)
index 0000000..b15e01f
--- /dev/null
@@ -0,0 +1,9 @@
+type=local
+server=localhost:443
+mail=localhost:8474
+
+==== OR ===
+type=autonomous
+java=java -cp bin;/path/to/mysqlConnector.jar org.cacert.gigi.Launcher
+serverPort=4443
+mailPort=8473
diff --git a/doc/tableStructure.sql b/doc/tableStructure.sql
new file mode 100644 (file)
index 0000000..24007a8
--- /dev/null
@@ -0,0 +1,121 @@
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `email` varchar(255) NOT NULL DEFAULT '',
+  `password` varchar(255) NOT NULL DEFAULT '',
+  `fname` varchar(255) NOT NULL DEFAULT '',
+  `mname` varchar(255) NOT NULL DEFAULT '',
+  `lname` varchar(255) NOT NULL DEFAULT '',
+  `suffix` varchar(50) NOT NULL DEFAULT '',
+  `dob` date NOT NULL DEFAULT '0000-00-00',
+  `verified` int(1) NOT NULL DEFAULT '0',
+  `ccid` int(3) NOT NULL DEFAULT '0',
+  `regid` int(5) NOT NULL DEFAULT '0',
+  `locid` int(7) NOT NULL DEFAULT '0',
+  `listme` int(1) NOT NULL DEFAULT '0',
+  `admin` tinyint(1) NOT NULL DEFAULT '0',
+  `language` varchar(5) NOT NULL DEFAULT '',
+  `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `deleted` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `locked` tinyint(1) NOT NULL DEFAULT '0',
+  `assurer_blocked` tinyint(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  KEY `ccid` (`ccid`),
+  KEY `regid` (`regid`),
+  KEY `locid` (`locid`),
+  KEY `email` (`email`),
+  KEY `stats_users_created` (`created`),
+  KEY `stats_users_verified` (`verified`),
+  KEY `userverified` (`verified`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `email`;
+CREATE TABLE `email` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `memid` int(11) NOT NULL DEFAULT '0',
+  `email` varchar(255) NOT NULL DEFAULT '',
+  `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `deleted` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `hash` varchar(50) NOT NULL DEFAULT '',
+  `attempts` int(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  KEY `memid` (`memid`),
+  KEY `stats_email_hash` (`hash`),
+  KEY `stats_email_deleted` (`deleted`),
+  KEY `email` (`email`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `pinglog`;
+CREATE TABLE `pinglog` (
+  `when` datetime NOT NULL,
+  `uid` int(11) NOT NULL,
+  `email` varchar(255) NOT NULL,
+  `result` varchar(255) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `baddomains`;
+CREATE TABLE `baddomains` (
+  `domain` varchar(255) NOT NULL DEFAULT ''
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+
+DROP TABLE IF EXISTS `alerts`;
+CREATE TABLE `alerts` (
+  `memid` int(11) NOT NULL DEFAULT '0',
+  `general` tinyint(1) NOT NULL DEFAULT '0',
+  `country` tinyint(1) NOT NULL DEFAULT '0',
+  `regional` tinyint(1) NOT NULL DEFAULT '0',
+  `radius` tinyint(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`memid`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `user_agreements`;
+CREATE TABLE `user_agreements` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `memid` int(11) NOT NULL,
+  `secmemid` int(11) DEFAULT NULL,
+  `document` varchar(50) DEFAULT NULL,
+  `date` datetime DEFAULT NULL,
+  `active` int(1) NOT NULL,
+  `method` varchar(100) NOT NULL,
+  `comment` varchar(100) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `emailcerts`;
+CREATE TABLE `emailcerts` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `memid` int(11) NOT NULL DEFAULT '0',
+  `serial` varchar(50) NOT NULL DEFAULT '',
+  `CN` varchar(255) NOT NULL DEFAULT '',
+  `subject` text NOT NULL,
+  `keytype` char(2) NOT NULL DEFAULT 'NS',
+  `codesign` tinyint(1) NOT NULL DEFAULT '0',
+  `csr_name` varchar(255) NOT NULL DEFAULT '',
+  `crt_name` varchar(255) NOT NULL DEFAULT '',
+  `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `revoked` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `expire` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `warning` tinyint(1) NOT NULL DEFAULT '0',
+  `renewed` tinyint(1) NOT NULL DEFAULT '0',
+  `rootcert` int(2) NOT NULL DEFAULT '1',
+  `md` enum('md5','sha1','sha256','sha512') NOT NULL DEFAULT 'sha512',
+  `type` tinyint(4) DEFAULT NULL,
+  `disablelogin` int(1) NOT NULL DEFAULT '0',
+  `pkhash` char(40) DEFAULT NULL,
+  `certhash` char(40) DEFAULT NULL,
+  `coll_found` tinyint(1) NOT NULL,
+  `description` varchar(100) NOT NULL DEFAULT '',
+  PRIMARY KEY (`id`),
+  KEY `emailcerts_pkhash` (`pkhash`),
+  KEY `revoked` (`revoked`),
+  KEY `created` (`created`),
+  KEY `memid` (`memid`),
+  KEY `serial` (`serial`),
+  KEY `stats_emailcerts_expire` (`expire`),
+  KEY `emailcrt` (`crt_name`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
\ No newline at end of file
diff --git a/lib/jtar/org/kamranzafar/jtar/Octal.java b/lib/jtar/org/kamranzafar/jtar/Octal.java
new file mode 100644 (file)
index 0000000..7a40ea1
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+/**
+ * @author Kamran Zafar
+ * 
+ */
+public class Octal {
+
+    /**
+     * Parse an octal string from a header buffer. This is used for the file
+     * permission mode value.
+     * 
+     * @param header
+     *            The header buffer from which to parse.
+     * @param offset
+     *            The offset into the buffer from which to parse.
+     * @param length
+     *            The number of header bytes to parse.
+     * 
+     * @return The long value of the octal string.
+     */
+    public static long parseOctal(byte[] header, int offset, int length) {
+        long result = 0;
+        boolean stillPadding = true;
+
+        int end = offset + length;
+        for (int i = offset; i < end; ++i) {
+            if (header[i] == 0)
+                break;
+
+            if (header[i] == (byte) ' ' || header[i] == '0') {
+                if (stillPadding)
+                    continue;
+
+                if (header[i] == (byte) ' ')
+                    break;
+            }
+
+            stillPadding = false;
+
+            result = ( result << 3 ) + ( header[i] - '0' );
+        }
+
+        return result;
+    }
+
+    /**
+     * Parse an octal integer from a header buffer.
+     * 
+     * @param value
+     * @param buf
+     *            The header buffer from which to parse.
+     * @param offset
+     *            The offset into the buffer from which to parse.
+     * @param length
+     *            The number of header bytes to parse.
+     * 
+     * @return The integer value of the octal bytes.
+     */
+    public static int getOctalBytes(long value, byte[] buf, int offset, int length) {
+        int idx = length - 1;
+
+        buf[offset + idx] = 0;
+        --idx;
+        buf[offset + idx] = (byte) ' ';
+        --idx;
+
+        if (value == 0) {
+            buf[offset + idx] = (byte) '0';
+            --idx;
+        } else {
+            for (long val = value; idx >= 0 && val > 0; --idx) {
+                buf[offset + idx] = (byte) ( (byte) '0' + (byte) ( val & 7 ) );
+                val = val >> 3;
+            }
+        }
+
+        for (; idx >= 0; --idx) {
+            buf[offset + idx] = (byte) ' ';
+        }
+
+        return offset + length;
+    }
+
+    /**
+     * Parse the checksum octal integer from a header buffer.
+     * 
+     * @param value
+     * @param buf
+     *            The header buffer from which to parse.
+     * @param offset
+     *            The offset into the buffer from which to parse.
+     * @param length
+     *            The number of header bytes to parse.
+     * @return The integer value of the entry's checksum.
+     */
+    public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
+        getOctalBytes( value, buf, offset, length );
+        buf[offset + length - 1] = (byte) ' ';
+        buf[offset + length - 2] = 0;
+        return offset + length;
+    }
+
+    /**
+     * Parse an octal long integer from a header buffer.
+     * 
+     * @param value
+     * @param buf
+     *            The header buffer from which to parse.
+     * @param offset
+     *            The offset into the buffer from which to parse.
+     * @param length
+     *            The number of header bytes to parse.
+     * 
+     * @return The long value of the octal bytes.
+     */
+    public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) {
+        byte[] temp = new byte[length + 1];
+        getOctalBytes( value, temp, 0, length + 1 );
+        System.arraycopy( temp, 0, buf, offset, length );
+        return offset + length;
+    }
+
+}
diff --git a/lib/jtar/org/kamranzafar/jtar/TarConstants.java b/lib/jtar/org/kamranzafar/jtar/TarConstants.java
new file mode 100644 (file)
index 0000000..c85d0a7
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+/**
+ * @author Kamran Zafar
+ * 
+ */
+public class TarConstants {
+    public static final int EOF_BLOCK = 1024;
+    public static final int DATA_BLOCK = 512;
+    public static final int HEADER_BLOCK = 512;
+}
diff --git a/lib/jtar/org/kamranzafar/jtar/TarEntry.java b/lib/jtar/org/kamranzafar/jtar/TarEntry.java
new file mode 100644 (file)
index 0000000..c91eac6
--- /dev/null
@@ -0,0 +1,322 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+import java.io.File;
+import java.util.Date;
+
+/**
+ * @author Kamran Zafar
+ * 
+ */
+public class TarEntry {
+       protected File file;
+       protected TarHeader header;
+
+       private TarEntry() {
+               this.file = null;
+               header = new TarHeader();
+       }
+
+       public TarEntry(File file, String entryName) {
+               this();
+               this.file = file;
+               this.extractTarHeader(entryName);
+       }
+
+       public TarEntry(byte[] headerBuf) {
+               this();
+               this.parseTarHeader(headerBuf);
+       }
+
+       /**
+        * Constructor to create an entry from an existing TarHeader object.
+        * 
+        * This method is useful to add new entries programmatically (e.g. for
+        * adding files or directories that do not exist in the file system).
+        * 
+        * @param header
+        * 
+        */
+       public TarEntry(TarHeader header) {
+               this.file = null;
+               this.header = header;
+       }
+
+       @Override
+       public boolean equals(Object it) {
+               if (!(it instanceof TarEntry)) {
+                       return false;
+               }
+               return header.name.toString().equals(
+                               ((TarEntry) it).header.name.toString());
+       }
+
+       @Override
+       public int hashCode() {
+               return header.name.hashCode();
+       }
+
+       public boolean isDescendent(TarEntry desc) {
+               return desc.header.name.toString().startsWith(header.name.toString());
+       }
+
+       public TarHeader getHeader() {
+               return header;
+       }
+
+       public String getName() {
+               String name = header.name.toString();
+               if (header.namePrefix != null
+                               && !header.namePrefix.toString().equals("")) {
+                       name = header.namePrefix.toString() + "/" + name;
+               }
+
+               return name;
+       }
+
+       public void setName(String name) {
+               header.name = new StringBuffer(name);
+       }
+
+       public int getUserId() {
+               return header.userId;
+       }
+
+       public void setUserId(int userId) {
+               header.userId = userId;
+       }
+
+       public int getGroupId() {
+               return header.groupId;
+       }
+
+       public void setGroupId(int groupId) {
+               header.groupId = groupId;
+       }
+
+       public String getUserName() {
+               return header.userName.toString();
+       }
+
+       public void setUserName(String userName) {
+               header.userName = new StringBuffer(userName);
+       }
+
+       public String getGroupName() {
+               return header.groupName.toString();
+       }
+
+       public void setGroupName(String groupName) {
+               header.groupName = new StringBuffer(groupName);
+       }
+
+       public void setIds(int userId, int groupId) {
+               this.setUserId(userId);
+               this.setGroupId(groupId);
+       }
+
+       public void setModTime(long time) {
+               header.modTime = time / 1000;
+       }
+
+       public void setModTime(Date time) {
+               header.modTime = time.getTime() / 1000;
+       }
+
+       public Date getModTime() {
+               return new Date(header.modTime * 1000);
+       }
+
+       public File getFile() {
+               return this.file;
+       }
+
+       public long getSize() {
+               return header.size;
+       }
+
+       public void setSize(long size) {
+               header.size = size;
+       }
+
+       /**
+        * Checks if the org.kamrazafar.jtar entry is a directory
+        * 
+        * @return
+        */
+       public boolean isDirectory() {
+               if (this.file != null) {
+                       return this.file.isDirectory();
+               }
+
+               if (header != null) {
+                       if (header.linkFlag == TarHeader.LF_DIR) {
+                               return true;
+                       }
+
+                       if (header.name.toString().endsWith("/")) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Extract header from File
+        * 
+        * @param entryName
+        */
+       public void extractTarHeader(String entryName) {
+               header = TarHeader.createHeader(entryName, file.length(),
+                               file.lastModified() / 1000, file.isDirectory());
+       }
+
+       /**
+        * Calculate checksum
+        * 
+        * @param buf
+        * @return
+        */
+       public long computeCheckSum(byte[] buf) {
+               long sum = 0;
+
+               for (int i = 0; i < buf.length; ++i) {
+                       sum += 255 & buf[i];
+               }
+
+               return sum;
+       }
+
+       /**
+        * Writes the header to the byte buffer
+        * 
+        * @param outbuf
+        */
+       public void writeEntryHeader(byte[] outbuf) {
+               int offset = 0;
+
+               offset = TarHeader.getNameBytes(header.name, outbuf, offset,
+                               TarHeader.NAMELEN);
+               offset = Octal.getOctalBytes(header.mode, outbuf, offset,
+                               TarHeader.MODELEN);
+               offset = Octal.getOctalBytes(header.userId, outbuf, offset,
+                               TarHeader.UIDLEN);
+               offset = Octal.getOctalBytes(header.groupId, outbuf, offset,
+                               TarHeader.GIDLEN);
+
+               long size = header.size;
+
+               offset = Octal.getLongOctalBytes(size, outbuf, offset,
+                               TarHeader.SIZELEN);
+               offset = Octal.getLongOctalBytes(header.modTime, outbuf, offset,
+                               TarHeader.MODTIMELEN);
+
+               int csOffset = offset;
+               for (int c = 0; c < TarHeader.CHKSUMLEN; ++c) {
+                       outbuf[offset++] = (byte) ' ';
+               }
+
+               outbuf[offset++] = header.linkFlag;
+
+               offset = TarHeader.getNameBytes(header.linkName, outbuf, offset,
+                               TarHeader.NAMELEN);
+               offset = TarHeader.getNameBytes(header.magic, outbuf, offset,
+                               TarHeader.USTAR_MAGICLEN);
+               offset = TarHeader.getNameBytes(header.userName, outbuf, offset,
+                               TarHeader.USTAR_USER_NAMELEN);
+               offset = TarHeader.getNameBytes(header.groupName, outbuf, offset,
+                               TarHeader.USTAR_GROUP_NAMELEN);
+               offset = Octal.getOctalBytes(header.devMajor, outbuf, offset,
+                               TarHeader.USTAR_DEVLEN);
+               offset = Octal.getOctalBytes(header.devMinor, outbuf, offset,
+                               TarHeader.USTAR_DEVLEN);
+               offset = TarHeader.getNameBytes(header.namePrefix, outbuf, offset,
+                               TarHeader.USTAR_FILENAME_PREFIX);
+
+               for (; offset < outbuf.length;) {
+                       outbuf[offset++] = 0;
+               }
+
+               long checkSum = this.computeCheckSum(outbuf);
+
+               Octal.getCheckSumOctalBytes(checkSum, outbuf, csOffset,
+                               TarHeader.CHKSUMLEN);
+       }
+
+       /**
+        * Parses the tar header to the byte buffer
+        * 
+        * @param header
+        * @param bh
+        */
+       public void parseTarHeader(byte[] bh) {
+               int offset = 0;
+
+               header.name = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
+               offset += TarHeader.NAMELEN;
+
+               header.mode = (int) Octal.parseOctal(bh, offset, TarHeader.MODELEN);
+               offset += TarHeader.MODELEN;
+
+               header.userId = (int) Octal.parseOctal(bh, offset, TarHeader.UIDLEN);
+               offset += TarHeader.UIDLEN;
+
+               header.groupId = (int) Octal.parseOctal(bh, offset, TarHeader.GIDLEN);
+               offset += TarHeader.GIDLEN;
+
+               header.size = Octal.parseOctal(bh, offset, TarHeader.SIZELEN);
+               offset += TarHeader.SIZELEN;
+
+               header.modTime = Octal.parseOctal(bh, offset, TarHeader.MODTIMELEN);
+               offset += TarHeader.MODTIMELEN;
+
+               header.checkSum = (int) Octal.parseOctal(bh, offset,
+                               TarHeader.CHKSUMLEN);
+               offset += TarHeader.CHKSUMLEN;
+
+               header.linkFlag = bh[offset++];
+
+               header.linkName = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
+               offset += TarHeader.NAMELEN;
+
+               header.magic = TarHeader
+                               .parseName(bh, offset, TarHeader.USTAR_MAGICLEN);
+               offset += TarHeader.USTAR_MAGICLEN;
+
+               header.userName = TarHeader.parseName(bh, offset,
+                               TarHeader.USTAR_USER_NAMELEN);
+               offset += TarHeader.USTAR_USER_NAMELEN;
+
+               header.groupName = TarHeader.parseName(bh, offset,
+                               TarHeader.USTAR_GROUP_NAMELEN);
+               offset += TarHeader.USTAR_GROUP_NAMELEN;
+
+               header.devMajor = (int) Octal.parseOctal(bh, offset,
+                               TarHeader.USTAR_DEVLEN);
+               offset += TarHeader.USTAR_DEVLEN;
+
+               header.devMinor = (int) Octal.parseOctal(bh, offset,
+                               TarHeader.USTAR_DEVLEN);
+               offset += TarHeader.USTAR_DEVLEN;
+
+               header.namePrefix = TarHeader.parseName(bh, offset,
+                               TarHeader.USTAR_FILENAME_PREFIX);
+       }
+}
\ No newline at end of file
diff --git a/lib/jtar/org/kamranzafar/jtar/TarHeader.java b/lib/jtar/org/kamranzafar/jtar/TarHeader.java
new file mode 100644 (file)
index 0000000..deecaa0
--- /dev/null
@@ -0,0 +1,243 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+import java.io.File;
+
+/**
+ * Header
+ * 
+ * <pre>
+ * Offset  Size     Field
+ * 0       100      File name
+ * 100     8        File mode
+ * 108     8        Owner's numeric user ID
+ * 116     8        Group's numeric user ID
+ * 124     12       File size in bytes
+ * 136     12       Last modification time in numeric Unix time format
+ * 148     8        Checksum for header block
+ * 156     1        Link indicator (file type)
+ * 157     100      Name of linked file
+ * </pre>
+ * 
+ * 
+ * File Types
+ * 
+ * <pre>
+ * Value        Meaning
+ * '0'          Normal file
+ * (ASCII NUL)  Normal file (now obsolete)
+ * '1'          Hard link
+ * '2'          Symbolic link
+ * '3'          Character special
+ * '4'          Block special
+ * '5'          Directory
+ * '6'          FIFO
+ * '7'          Contigous
+ * </pre>
+ * 
+ * 
+ * 
+ * Ustar header
+ * 
+ * <pre>
+ * Offset  Size    Field
+ * 257     6       UStar indicator "ustar"
+ * 263     2       UStar version "00"
+ * 265     32      Owner user name
+ * 297     32      Owner group name
+ * 329     8       Device major number
+ * 337     8       Device minor number
+ * 345     155     Filename prefix
+ * </pre>
+ */
+
+public class TarHeader {
+
+       /*
+        * Header
+        */
+       public static final int NAMELEN = 100;
+       public static final int MODELEN = 8;
+       public static final int UIDLEN = 8;
+       public static final int GIDLEN = 8;
+       public static final int SIZELEN = 12;
+       public static final int MODTIMELEN = 12;
+       public static final int CHKSUMLEN = 8;
+       public static final byte LF_OLDNORM = 0;
+
+       /*
+        * File Types
+        */
+       public static final byte LF_NORMAL = (byte) '0';
+       public static final byte LF_LINK = (byte) '1';
+       public static final byte LF_SYMLINK = (byte) '2';
+       public static final byte LF_CHR = (byte) '3';
+       public static final byte LF_BLK = (byte) '4';
+       public static final byte LF_DIR = (byte) '5';
+       public static final byte LF_FIFO = (byte) '6';
+       public static final byte LF_CONTIG = (byte) '7';
+
+       /*
+        * Ustar header
+        */
+
+       public static final String USTAR_MAGIC = "ustar"; // POSIX
+
+       public static final int USTAR_MAGICLEN = 8;
+       public static final int USTAR_USER_NAMELEN = 32;
+       public static final int USTAR_GROUP_NAMELEN = 32;
+       public static final int USTAR_DEVLEN = 8;
+       public static final int USTAR_FILENAME_PREFIX = 155;
+
+       // Header values
+       public StringBuffer name;
+       public int mode;
+       public int userId;
+       public int groupId;
+       public long size;
+       public long modTime;
+       public int checkSum;
+       public byte linkFlag;
+       public StringBuffer linkName;
+       public StringBuffer magic; // ustar indicator and version
+       public StringBuffer userName;
+       public StringBuffer groupName;
+       public int devMajor;
+       public int devMinor;
+       public StringBuffer namePrefix;
+
+       public TarHeader() {
+               this.magic = new StringBuffer(TarHeader.USTAR_MAGIC);
+
+               this.name = new StringBuffer();
+               this.linkName = new StringBuffer();
+
+               String user = System.getProperty("user.name", "");
+
+               if (user.length() > 31)
+                       user = user.substring(0, 31);
+
+               this.userId = 0;
+               this.groupId = 0;
+               this.userName = new StringBuffer(user);
+               this.groupName = new StringBuffer("");
+               this.namePrefix = new StringBuffer();
+       }
+
+       /**
+        * Parse an entry name from a header buffer.
+        * 
+        * @param name
+        * @param header
+        *            The header buffer from which to parse.
+        * @param offset
+        *            The offset into the buffer from which to parse.
+        * @param length
+        *            The number of header bytes to parse.
+        * @return The header's entry name.
+        */
+       public static StringBuffer parseName(byte[] header, int offset, int length) {
+               StringBuffer result = new StringBuffer(length);
+
+               int end = offset + length;
+               for (int i = offset; i < end; ++i) {
+                       if (header[i] == 0)
+                               break;
+                       result.append((char) header[i]);
+               }
+
+               return result;
+       }
+
+       /**
+        * Determine the number of bytes in an entry name.
+        * 
+        * @param name
+        * @param header
+        *            The header buffer from which to parse.
+        * @param offset
+        *            The offset into the buffer from which to parse.
+        * @param length
+        *            The number of header bytes to parse.
+        * @return The number of bytes in a header's entry name.
+        */
+       public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) {
+               int i;
+
+               for (i = 0; i < length && i < name.length(); ++i) {
+                       buf[offset + i] = (byte) name.charAt(i);
+               }
+
+               for (; i < length; ++i) {
+                       buf[offset + i] = 0;
+               }
+
+               return offset + length;
+       }
+
+       /**
+        * Creates a new header for a file/directory entry.
+        * 
+        * 
+        * @param name
+        *            File name
+        * @param size
+        *            File size in bytes
+        * @param modTime
+        *            Last modification time in numeric Unix time format
+        * @param dir
+        *            Is directory
+        * 
+        * @return
+        */
+       public static TarHeader createHeader(String entryName, long size, long modTime, boolean dir) {
+               String name = entryName;
+               name = TarUtils.trim(name.replace(File.separatorChar, '/'), '/');
+
+               TarHeader header = new TarHeader();
+               header.linkName = new StringBuffer("");
+
+               if (name.length() > 100) {
+                       header.namePrefix = new StringBuffer(name.substring(0, name.lastIndexOf('/')));
+                       header.name = new StringBuffer(name.substring(name.lastIndexOf('/') + 1));
+               } else {
+                       header.name = new StringBuffer(name);
+               }
+
+               if (dir) {
+                       header.mode = 040755;
+                       header.linkFlag = TarHeader.LF_DIR;
+                       if (header.name.charAt(header.name.length() - 1) != '/') {
+                               header.name.append("/");
+                       }
+                       header.size = 0;
+               } else {
+                       header.mode = 0100644;
+                       header.linkFlag = TarHeader.LF_NORMAL;
+                       header.size = size;
+               }
+
+               header.modTime = modTime;
+               header.checkSum = 0;
+               header.devMajor = 0;
+               header.devMinor = 0;
+
+               return header;
+       }
+}
\ No newline at end of file
diff --git a/lib/jtar/org/kamranzafar/jtar/TarInputStream.java b/lib/jtar/org/kamranzafar/jtar/TarInputStream.java
new file mode 100644 (file)
index 0000000..cd48ae0
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author Kamran Zafar
+ * 
+ */
+public class TarInputStream extends FilterInputStream {
+
+       private static final int SKIP_BUFFER_SIZE = 2048;
+       private TarEntry currentEntry;
+       private long currentFileSize;
+       private long bytesRead;
+       private boolean defaultSkip = false;
+
+       public TarInputStream(InputStream in) {
+               super(in);
+               currentFileSize = 0;
+               bytesRead = 0;
+       }
+
+       @Override
+       public boolean markSupported() {
+               return false;
+       }
+
+       /**
+        * Not supported
+        * 
+        */
+       @Override
+       public synchronized void mark(int readlimit) {
+       }
+
+       /**
+        * Not supported
+        * 
+        */
+       @Override
+       public synchronized void reset() throws IOException {
+               throw new IOException("mark/reset not supported");
+       }
+
+       /**
+        * Read a byte
+        * 
+        * @see java.io.FilterInputStream#read()
+        */
+       @Override
+       public int read() throws IOException {
+               byte[] buf = new byte[1];
+
+               int res = this.read(buf, 0, 1);
+
+               if (res != -1) {
+                       return 0xFF & buf[0];
+               }
+
+               return res;
+       }
+
+       /**
+        * Checks if the bytes being read exceed the entry size and adjusts the byte
+        * array length. Updates the byte counters
+        * 
+        * 
+        * @see java.io.FilterInputStream#read(byte[], int, int)
+        */
+       @Override
+       public int read(byte[] b, int off, int len) throws IOException {
+               if (currentEntry != null) {
+                       if (currentFileSize == currentEntry.getSize()) {
+                               return -1;
+                       } else if ((currentEntry.getSize() - currentFileSize) < len) {
+                               len = (int) (currentEntry.getSize() - currentFileSize);
+                       }
+               }
+
+               int br = super.read(b, off, len);
+
+               if (br != -1) {
+                       if (currentEntry != null) {
+                               currentFileSize += br;
+                       }
+
+                       bytesRead += br;
+               }
+
+               return br;
+       }
+
+       /**
+        * Returns the next entry in the tar file
+        * 
+        * @return TarEntry
+        * @throws IOException
+        */
+       public TarEntry getNextEntry() throws IOException {
+               closeCurrentEntry();
+
+               byte[] header = new byte[TarConstants.HEADER_BLOCK];
+               byte[] theader = new byte[TarConstants.HEADER_BLOCK];
+               int tr = 0;
+
+               // Read full header
+               while (tr < TarConstants.HEADER_BLOCK) {
+                       int res = read(theader, 0, TarConstants.HEADER_BLOCK - tr);
+
+                       if (res < 0) {
+                               break;
+                       }
+
+                       System.arraycopy(theader, 0, header, tr, res);
+                       tr += res;
+               }
+
+               // Check if record is null
+               boolean eof = true;
+               for (byte b : header) {
+                       if (b != 0) {
+                               eof = false;
+                               break;
+                       }
+               }
+
+               if (!eof) {
+                       currentEntry = new TarEntry(header);
+               }
+
+               return currentEntry;
+       }
+
+       /**
+        * Returns the current offset (in bytes) from the beginning of the stream. 
+        * This can be used to find out at which point in a tar file an entry's content begins, for instance. 
+        */
+       public long getCurrentOffset() {
+               return bytesRead;
+       }
+       
+       /**
+        * Closes the current tar entry
+        * 
+        * @throws IOException
+        */
+       protected void closeCurrentEntry() throws IOException {
+               if (currentEntry != null) {
+                       if (currentEntry.getSize() > currentFileSize) {
+                               // Not fully read, skip rest of the bytes
+                               long bs = 0;
+                               while (bs < currentEntry.getSize() - currentFileSize) {
+                                       long res = skip(currentEntry.getSize() - currentFileSize - bs);
+
+                                       if (res == 0 && currentEntry.getSize() - currentFileSize > 0) {
+                                               // I suspect file corruption
+                                               throw new IOException("Possible tar file corruption");
+                                       }
+
+                                       bs += res;
+                               }
+                       }
+
+                       currentEntry = null;
+                       currentFileSize = 0L;
+                       skipPad();
+               }
+       }
+
+       /**
+        * Skips the pad at the end of each tar entry file content
+        * 
+        * @throws IOException
+        */
+       protected void skipPad() throws IOException {
+               if (bytesRead > 0) {
+                       int extra = (int) (bytesRead % TarConstants.DATA_BLOCK);
+
+                       if (extra > 0) {
+                               long bs = 0;
+                               while (bs < TarConstants.DATA_BLOCK - extra) {
+                                       long res = skip(TarConstants.DATA_BLOCK - extra - bs);
+                                       bs += res;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Skips 'n' bytes on the InputStream<br>
+        * Overrides default implementation of skip
+        * 
+        */
+       @Override
+       public long skip(long n) throws IOException {
+               if (defaultSkip) {
+                       // use skip method of parent stream
+                       // may not work if skip not implemented by parent
+                       long bs = super.skip(n);
+                       bytesRead += bs;
+
+                       return bs;
+               }
+
+               if (n <= 0) {
+                       return 0;
+               }
+
+               long left = n;
+               byte[] sBuff = new byte[SKIP_BUFFER_SIZE];
+
+               while (left > 0) {
+                       int res = read(sBuff, 0, (int) (left < SKIP_BUFFER_SIZE ? left : SKIP_BUFFER_SIZE));
+                       if (res < 0) {
+                               break;
+                       }
+                       left -= res;
+               }
+
+               return n - left;
+       }
+
+       public boolean isDefaultSkip() {
+               return defaultSkip;
+       }
+
+       public void setDefaultSkip(boolean defaultSkip) {
+               this.defaultSkip = defaultSkip;
+       }
+}
diff --git a/lib/jtar/org/kamranzafar/jtar/TarOutputStream.java b/lib/jtar/org/kamranzafar/jtar/TarOutputStream.java
new file mode 100644 (file)
index 0000000..e17413c
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * @author Kamran Zafar
+ * 
+ */
+public class TarOutputStream extends OutputStream {
+       private final OutputStream out;
+    private long bytesWritten;
+    private long currentFileSize;
+    private TarEntry currentEntry;
+
+    public TarOutputStream(OutputStream out) {
+        this.out = out;
+        bytesWritten = 0;
+        currentFileSize = 0;
+    }
+
+       public TarOutputStream(final File fout) throws FileNotFoundException {
+               this.out = new BufferedOutputStream(new FileOutputStream(fout));
+               bytesWritten = 0;
+               currentFileSize = 0;
+       }
+
+       /**
+        * Opens a file for writing. 
+        */
+       public TarOutputStream(final File fout, final boolean append) throws IOException {
+               @SuppressWarnings("resource")
+               RandomAccessFile raf = new RandomAccessFile(fout, "rw");
+               final long fileSize = fout.length();
+               if (append && fileSize > TarConstants.EOF_BLOCK) {
+                       raf.seek(fileSize - TarConstants.EOF_BLOCK);
+               }
+               out = new BufferedOutputStream(new FileOutputStream(raf.getFD()));
+       }
+
+    /**
+     * Appends the EOF record and closes the stream
+     * 
+     * @see java.io.FilterOutputStream#close()
+     */
+    @Override
+    public void close() throws IOException {
+        closeCurrentEntry();
+        write( new byte[TarConstants.EOF_BLOCK] );
+        out.close();
+    }
+    /**
+     * Writes a byte to the stream and updates byte counters
+     * 
+     * @see java.io.FilterOutputStream#write(int)
+     */
+    @Override
+    public void write(int b) throws IOException {
+        out.write( b );
+        bytesWritten += 1;
+
+        if (currentEntry != null) {
+            currentFileSize += 1;
+        }
+    }
+
+    /**
+     * Checks if the bytes being written exceed the current entry size.
+     * 
+     * @see java.io.FilterOutputStream#write(byte[], int, int)
+     */
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (currentEntry != null && !currentEntry.isDirectory()) {
+            if (currentEntry.getSize() < currentFileSize + len) {
+                throw new IOException( "The current entry[" + currentEntry.getName() + "] size["
+                        + currentEntry.getSize() + "] is smaller than the bytes[" + ( currentFileSize + len )
+                        + "] being written." );
+            }
+        }
+
+        out.write( b, off, len );
+        
+        bytesWritten += len;
+
+        if (currentEntry != null) {
+            currentFileSize += len;
+        }        
+    }
+
+    /**
+     * Writes the next tar entry header on the stream
+     * 
+     * @param entry
+     * @throws IOException
+     */
+    public void putNextEntry(TarEntry entry) throws IOException {
+        closeCurrentEntry();
+
+        byte[] header = new byte[TarConstants.HEADER_BLOCK];
+        entry.writeEntryHeader( header );
+
+        write( header );
+
+        currentEntry = entry;
+    }
+
+    /**
+     * Closes the current tar entry
+     * 
+     * @throws IOException
+     */
+    protected void closeCurrentEntry() throws IOException {
+        if (currentEntry != null) {
+            if (currentEntry.getSize() > currentFileSize) {
+                throw new IOException( "The current entry[" + currentEntry.getName() + "] of size["
+                        + currentEntry.getSize() + "] has not been fully written." );
+            }
+
+            currentEntry = null;
+            currentFileSize = 0;
+
+            pad();
+        }
+    }
+
+    /**
+     * Pads the last content block
+     * 
+     * @throws IOException
+     */
+    protected void pad() throws IOException {
+        if (bytesWritten > 0) {
+            int extra = (int) ( bytesWritten % TarConstants.DATA_BLOCK );
+
+            if (extra > 0) {
+                write( new byte[TarConstants.DATA_BLOCK - extra] );
+            }
+        }
+    }
+}
diff --git a/lib/jtar/org/kamranzafar/jtar/TarUtils.java b/lib/jtar/org/kamranzafar/jtar/TarUtils.java
new file mode 100644 (file)
index 0000000..8dccc37
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2012 Kamran Zafar 
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ * 
+ */
+
+package org.kamranzafar.jtar;
+
+import java.io.File;
+
+/**
+ * @author Kamran
+ * 
+ */
+public class TarUtils {
+       /**
+        * Determines the tar file size of the given folder/file path
+        * 
+        * @param path
+        * @return
+        */
+       public static long calculateTarSize(File path) {
+               return tarSize(path) + TarConstants.EOF_BLOCK;
+       }
+
+       private static long tarSize(File dir) {
+               long size = 0;
+
+               if (dir.isFile()) {
+                       return entrySize(dir.length());
+               } else {
+                       File[] subFiles = dir.listFiles();
+
+                       if (subFiles != null && subFiles.length > 0) {
+                               for (File file : subFiles) {
+                                       if (file.isFile()) {
+                                               size += entrySize(file.length());
+                                       } else {
+                                               size += tarSize(file);
+                                       }
+                               }
+                       } else {
+                               // Empty folder header
+                               return TarConstants.HEADER_BLOCK;
+                       }
+               }
+
+               return size;
+       }
+
+       private static long entrySize(long fileSize) {
+               long size = 0;
+               size += TarConstants.HEADER_BLOCK; // Header
+               size += fileSize; // File size
+
+               long extra = size % TarConstants.DATA_BLOCK;
+
+               if (extra > 0) {
+                       size += (TarConstants.DATA_BLOCK - extra); // pad
+               }
+
+               return size;
+       }
+
+       public static String trim(String s, char c) {
+               StringBuffer tmp = new StringBuffer(s);
+               for (int i = 0; i < tmp.length(); i++) {
+                       if (tmp.charAt(i) != c) {
+                               break;
+                       } else {
+                               tmp.deleteCharAt(i);
+                       }
+               }
+
+               for (int i = tmp.length() - 1; i >= 0; i--) {
+                       if (tmp.charAt(i) != c) {
+                               break;
+                       } else {
+                               tmp.deleteCharAt(i);
+                       }
+               }
+
+               return tmp.toString();
+       }
+}
diff --git a/src/org/cacert/gigi/DevelLauncher.java b/src/org/cacert/gigi/DevelLauncher.java
new file mode 100644 (file)
index 0000000..74a4ae6
--- /dev/null
@@ -0,0 +1,77 @@
+package org.cacert.gigi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.kamranzafar.jtar.TarEntry;
+import org.kamranzafar.jtar.TarHeader;
+import org.kamranzafar.jtar.TarOutputStream;
+
+public class DevelLauncher {
+       public static void main(String[] args) throws Exception {
+               Properties mainProps = new Properties();
+               mainProps.load(new FileInputStream("config/gigi.properties"));
+               for (int i = 0; i < args.length; i++) {
+                       if (args[i].equals("--port")) {
+                               mainProps.setProperty("port", args[i + 1]);
+                       }
+                       i++;
+               }
+
+               ByteArrayOutputStream chunkConfig = new ByteArrayOutputStream();
+               DataOutputStream dos = new DataOutputStream(chunkConfig);
+               byte[] cacerts = Files.readAllBytes(Paths.get("config/cacerts.jks"));
+               byte[] keystore = Files.readAllBytes(Paths
+                               .get("config/keystore.pkcs12"));
+
+               DevelLauncher.writeGigiConfig(dos, new byte[]{}, "changeit".getBytes(),
+                               mainProps, cacerts, keystore);
+               dos.flush();
+               InputStream oldin = System.in;
+               System.setIn(new ByteArrayInputStream(chunkConfig.toByteArray()));
+               Launcher.main(args);
+               System.setIn(oldin);
+       }
+       public static void writeGigiConfig(OutputStream target, byte[] keystorepw,
+                       byte[] truststorepw, Properties mainprop, byte[] cacerts,
+                       byte[] keystore) throws IOException {
+               TarOutputStream tos = new TarOutputStream(target);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               mainprop.store(baos, "");
+
+               putTarEntry(baos.toByteArray(), tos, "gigi.properties");
+               putTarEntry(keystorepw, tos, "keystorepw");
+               putTarEntry(truststorepw, tos, "truststorepw");
+               putTarEntry(keystore, tos, "keystore.pkcs12");
+               putTarEntry(cacerts, tos, "cacerts.jks");
+               tos.close();
+
+       }
+       private static void putTarEntry(byte[] data, TarOutputStream tos,
+                       String name) throws IOException {
+               TarHeader th = new TarHeader();
+               th.name = new StringBuffer(name);
+               th.size = data.length;
+               tos.putNextEntry(new TarEntry(th));
+               tos.write(data);
+       }
+       public static void writeChunk(DataOutputStream dos, byte[] chunk)
+                       throws IOException {
+               dos.writeInt(chunk.length);
+               dos.write(chunk);
+       }
+       public static void launch(Properties props, File cacerts, File keystore)
+                       throws IOException {
+               ByteArrayOutputStream config = new ByteArrayOutputStream();
+               props.store(config, "");
+       }
+}
index 28a7394e859587709d57e37e32aaf845bb5389dc..78924e822687909511c231cfa8dd5a52417804c3 100644 (file)
@@ -7,6 +7,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.Calendar;
 import java.util.HashMap;
+import java.util.Properties;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -14,10 +15,13 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.email.EmailProvider;
 import org.cacert.gigi.pages.LoginPage;
 import org.cacert.gigi.pages.MainPage;
 import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.pages.TestSecure;
+import org.cacert.gigi.pages.Verify;
 import org.cacert.gigi.pages.account.MailCertificates;
 import org.cacert.gigi.pages.account.MyDetails;
 import org.cacert.gigi.pages.main.RegisterPage;
@@ -30,11 +34,16 @@ public class Gigi extends HttpServlet {
        private String[] baseTemplate;
        private HashMap<String, Page> pages = new HashMap<String, Page>();
 
+       public Gigi(Properties conf) {
+               EmailProvider.init(conf);
+               DatabaseConnection.init(conf);
+       }
        @Override
        public void init() throws ServletException {
                pages.put("/login", new LoginPage("CACert - Login"));
                pages.put("/", new MainPage("CACert - Home"));
                pages.put("/secure", new TestSecure());
+               pages.put(Verify.PATH, new Verify());
                pages.put(MailCertificates.PATH, new MailCertificates());
                pages.put(MyDetails.PATH, new MyDetails());
                pages.put(RegisterPage.PATH, new RegisterPage());
diff --git a/src/org/cacert/gigi/GigiConfig.java b/src/org/cacert/gigi/GigiConfig.java
new file mode 100644 (file)
index 0000000..69c95bb
--- /dev/null
@@ -0,0 +1,87 @@
+package org.cacert.gigi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Properties;
+
+import org.kamranzafar.jtar.TarEntry;
+import org.kamranzafar.jtar.TarInputStream;
+
+public class GigiConfig {
+       public static final String GIGI_CONFIG_VERSION = "GigiConfigV1.0";
+       byte[] cacerts;
+       byte[] keystore;
+       Properties mainProps = new Properties();
+       private char[] keystorpw;
+       private char[] truststorepw;
+
+       private GigiConfig() {
+       }
+       public byte[] getCacerts() {
+               return cacerts;
+       }
+       public byte[] getKeystore() {
+               return keystore;
+       }
+       public Properties getMainProps() {
+               return mainProps;
+       }
+
+       public static GigiConfig parse(InputStream input) throws IOException {
+               TarInputStream tis = new TarInputStream(input);
+               TarEntry t;
+               GigiConfig gc = new GigiConfig();
+               while ((t = tis.getNextEntry()) != null) {
+                       if (t.getName().equals("gigi.properties")) {
+                               gc.mainProps.load(tis);
+                       } else if (t.getName().equals("cacerts.jks")) {
+                               gc.cacerts = readFully(tis);
+                       } else if (t.getName().equals("keystore.pkcs12")) {
+                               gc.keystore = readFully(tis);
+                       } else if (t.getName().equals("keystorepw")) {
+                               gc.keystorpw = transformSafe(readFully(tis));
+                       } else if (t.getName().equals("truststorepw")) {
+                               gc.truststorepw = transformSafe(readFully(tis));
+                       } else {
+                               System.out.println("Unknown config: " + t.getName());
+                       }
+               }
+               tis.close();
+               return gc;
+       }
+       public static byte[] readFully(InputStream is) throws IOException {
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               byte[] buffer = new byte[1024];
+               int len = 0;
+               while ((len = is.read(buffer)) > 0) {
+                       baos.write(buffer, 0, len);
+               }
+               baos.close();
+               return baos.toByteArray();
+       }
+       private static char[] transformSafe(byte[] readChunk) {
+               char[] res = new char[readChunk.length];
+               for (int i = 0; i < res.length; i++) {
+                       res[i] = (char) readChunk[i];
+                       readChunk[i] = 0;
+               }
+               return res;
+       }
+
+       public KeyStore getPrivateStore() throws GeneralSecurityException,
+                       IOException {
+               KeyStore ks1 = KeyStore.getInstance("pkcs12");
+               ks1.load(new ByteArrayInputStream(keystore), keystorpw);
+               return ks1;
+       }
+       public KeyStore getTrustStore() throws GeneralSecurityException,
+                       IOException {
+               KeyStore ks1 = KeyStore.getInstance("jks");
+               ks1.load(new ByteArrayInputStream(cacerts), truststorepw);
+               return ks1;
+       }
+}
index 12add4362de2d4c0c45ae7eb50f02f9bf1f799fd..4753025442d22b0895c2c24e103bc5e6e3c44d69 100644 (file)
@@ -1,15 +1,11 @@
 package org.cacert.gigi;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
+import java.util.Properties;
 
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
-import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 
 import org.cacert.gigi.natives.SetUID;
@@ -33,13 +29,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
 
 public class Launcher {
        public static void main(String[] args) throws Exception {
-               int port = 443;
-               for (int i = 0; i < args.length; i++) {
-                       if (args[i].equals("--port")) {
-                               port = Integer.parseInt(args[i + 1]);
-                       }
-                       i++;
-               }
+               GigiConfig conf = GigiConfig.parse(System.in);
+
                Server s = new Server();
                // === SSL HTTP Configuration ===
                HttpConfiguration https_config = new HttpConfiguration();
@@ -50,15 +41,16 @@ public class Launcher {
                https_config.addCustomizer(new SecureRequestCustomizer());
 
                ServerConnector connector = new ServerConnector(s,
-                               new SslConnectionFactory(generateSSLContextFactory(),
+                               new SslConnectionFactory(generateSSLContextFactory(conf),
                                                "http/1.1"), new HttpConnectionFactory(https_config));
-               connector.setHost("127.0.0.1");
-               connector.setPort(port);
+               connector.setHost(conf.getMainProps().getProperty("host"));
+               connector.setPort(Integer.parseInt(conf.getMainProps().getProperty(
+                               "port")));
                s.setConnectors(new Connector[]{connector});
 
                HandlerList hl = new HandlerList();
                hl.setHandlers(new Handler[]{generateStaticContext(),
-                               generateGigiContext()});
+                               generateGigiContext(conf.getMainProps())});
                s.setHandler(hl);
                s.start();
                if (connector.getPort() <= 1024
@@ -70,12 +62,12 @@ public class Launcher {
                }
        }
 
-       private static ServletContextHandler generateGigiContext() {
+       private static ServletContextHandler generateGigiContext(Properties conf) {
                ServletContextHandler servlet = new ServletContextHandler(
                                ServletContextHandler.SESSIONS);
                servlet.setInitParameter(SessionManager.__SessionCookieProperty,
                                "CACert-Session");
-               servlet.addServlet(new ServletHolder(new Gigi()), "/*");
+               servlet.addServlet(new ServletHolder(new Gigi(conf)), "/*");
                return servlet;
        }
 
@@ -88,14 +80,11 @@ public class Launcher {
                return ch;
        }
 
-       private static SslContextFactory generateSSLContextFactory()
-                       throws NoSuchAlgorithmException, KeyStoreException, IOException,
-                       CertificateException, FileNotFoundException {
+       private static SslContextFactory generateSSLContextFactory(GigiConfig conf)
+                       throws GeneralSecurityException, IOException {
                TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("PKIX");
                tmFactory.init((KeyStore) null);
 
-               final TrustManager[] tm = tmFactory.getTrustManagers();
-
                SslContextFactory scf = new SslContextFactory() {
 
                        String[] ciphers = null;
@@ -119,13 +108,10 @@ public class Launcher {
                };
                scf.setRenegotiationAllowed(false);
                scf.setWantClientAuth(true);
-               KeyStore ks1 = KeyStore.getInstance("pkcs12");
-               ks1.load(new FileInputStream("config/keystore.pkcs12"),
-                               "".toCharArray());
-               scf.setTrustStorePath("config/cacerts.jks");
-               scf.setTrustStorePassword("changeit");
+
                scf.setProtocol("TLS");
-               scf.setKeyStore(ks1);
+               scf.setTrustStore(conf.getTrustStore());
+               scf.setKeyStore(conf.getPrivateStore());
                return scf;
        }
 }
index b049bc3ffde11edb06d49e4263dfad0001f604f7..bf47552ac56a7b3e7285eefe20588f895e8aa984 100644 (file)
@@ -85,9 +85,7 @@ public class User {
                PreparedStatement query = DatabaseConnection.getInstance().prepare(
                                "insert into `users` set `email`=?, `password`=?, "
                                                + "`fname`=?, `mname`=?, `lname`=?, "
-                                               + "`suffix`=?, `dob`=?, `created`=NOW(),"
-                                               + " `orgadmin`=0, `adadmin`=0, `locked`=0,"
-                                               + " `uniqueID`=0, `otphash`='', `otppin`=0");
+                                               + "`suffix`=?, `dob`=?, `created`=NOW(), locked=0");
                query.setString(1, email);
                query.setString(2, PasswordHash.hash(password));
                query.setString(3, fname);
index c1eed41b5391d6ff18e75faf3fd5a916137aeb6b..9cbcaeb5140fc58487d4602949942634166e123f 100644 (file)
@@ -1,7 +1,5 @@
 package org.cacert.gigi.database;
 
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -9,35 +7,41 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Properties;
+import java.sql.Statement;
 
 public class DatabaseConnection {
+       public static final int CONNECTION_TIMEOUT = 24 * 60 * 60;
        Connection c;
        HashMap<String, PreparedStatement> statements = new HashMap<String, PreparedStatement>();
-       static Properties credentials = new Properties();
-       static {
-               try {
-                       credentials.load(new FileInputStream("config/sql.properties"));
-               } catch (IOException e) {
-                       e.printStackTrace();
-               }
-       }
+       private static Properties credentials;
+       Statement adHoc;
        public DatabaseConnection() {
                try {
-                       Class.forName(credentials.getProperty("driver"));
+                       Class.forName(credentials.getProperty("sql.driver"));
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                }
+               tryConnect();
+
+       }
+       private void tryConnect() {
                try {
-                       c = DriverManager.getConnection(credentials.getProperty("url")
+                       c = DriverManager.getConnection(credentials.getProperty("sql.url")
                                        + "?zeroDateTimeBehavior=convertToNull",
-                                       credentials.getProperty("user"),
-                                       credentials.getProperty("password"));
+                                       credentials.getProperty("sql.user"),
+                                       credentials.getProperty("sql.password"));
+                       PreparedStatement ps = c
+                                       .prepareStatement("SET SESSION wait_timeout=?;");
+                       ps.setInt(1, CONNECTION_TIMEOUT);
+                       ps.execute();
+                       ps.close();
+                       adHoc = c.createStatement();
                } catch (SQLException e) {
                        e.printStackTrace();
                }
-
        }
        public PreparedStatement prepare(String query) throws SQLException {
+               ensureOpen();
                PreparedStatement statement = statements.get(query);
                if (statement == null) {
                        statement = c.prepareStatement(query);
@@ -45,7 +49,21 @@ public class DatabaseConnection {
                }
                return statement;
        }
-
+       long lastAction = System.currentTimeMillis();
+       private void ensureOpen() {
+               if (System.currentTimeMillis() - lastAction > CONNECTION_TIMEOUT * 1000L) {
+                       try {
+                               ResultSet rs = adHoc.executeQuery("SELECT 1");
+                               rs.close();
+                               lastAction = System.currentTimeMillis();
+                               return;
+                       } catch (SQLException e) {
+                       }
+                       statements.clear();
+                       tryConnect();
+               }
+               lastAction = System.currentTimeMillis();
+       }
        public static int lastInsertId(PreparedStatement query) throws SQLException {
                ResultSet rs = query.getGeneratedKeys();
                rs.next();
@@ -62,4 +80,27 @@ public class DatabaseConnection {
        public static DatabaseConnection getInstance() {
                return instances.get();
        }
+       public static void init(Properties conf) {
+               if (credentials != null) {
+                       throw new Error("Re-initiaizing is forbidden.");
+               }
+               credentials = conf;
+       }
+       public void beginTransaction() throws SQLException {
+               c.setAutoCommit(false);
+       }
+       public void commitTransaction() throws SQLException {
+               c.commit();
+               c.setAutoCommit(true);
+       }
+       public void quitTransaction() {
+               try {
+                       if (!c.getAutoCommit()) {
+                               c.rollback();
+                               c.setAutoCommit(true);
+                       }
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               }
+       }
 }
diff --git a/src/org/cacert/gigi/email/CommandlineEmailProvider.java b/src/org/cacert/gigi/email/CommandlineEmailProvider.java
new file mode 100644 (file)
index 0000000..2f7502b
--- /dev/null
@@ -0,0 +1,32 @@
+package org.cacert.gigi.email;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class CommandlineEmailProvider extends EmailProvider {
+       public CommandlineEmailProvider(Properties p) {
+       }
+
+       @Override
+       public void sendmail(String to, String subject, String message,
+                       String from, String replyto, String toname, String fromname,
+                       String errorsto, boolean extra) throws IOException {
+               synchronized (System.out) {
+                       System.out.println("== MAIL ==");
+                       System.out.println("To: " + to);
+                       System.out.println("Subject: " + subject);
+                       System.out.println("From: " + from);
+                       System.out.println("Errors-To: " + errorsto);
+                       System.out.println("Extra: " + extra);
+                       System.out.println(message);
+               }
+
+       }
+       @Override
+       public String checkEmailServer(int forUid, String address)
+                       throws IOException {
+               System.out.println("checkMailBox: " + address);
+               return OK;
+       }
+
+}
diff --git a/src/org/cacert/gigi/email/EmailChecker.java b/src/org/cacert/gigi/email/EmailChecker.java
deleted file mode 100644 (file)
index 7e18f68..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.cacert.gigi.email;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.Socket;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.LinkedList;
-import java.util.regex.Pattern;
-
-import org.cacert.gigi.database.DatabaseConnection;
-
-public class EmailChecker {
-       public static final String OK = "OK";
-       public static final String FAIL = "FAIL";
-       private static final Pattern MAIL = Pattern
-                       .compile("^([a-zA-Z0-9])+([a-zA-Z0-9\\+\\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\\._-]+)+$");
-       private EmailChecker() {
-       }
-       public static String checkEmailServer(int forUid, String address)
-                       throws IOException {
-               if (MAIL.matcher(address).matches()) {
-                       String[] parts = address.split("@", 2);
-                       String domain = parts[1];
-
-                       LinkedList<String> mxhosts = getMxHosts(domain);
-
-                       for (String host : mxhosts) {
-                               try (Socket s = new Socket(host, 25);
-                                               BufferedReader br = new BufferedReader(
-                                                               new InputStreamReader(s.getInputStream()));
-                                               PrintWriter pw = new PrintWriter(s.getOutputStream())) {
-                                       String line;
-                                       while ((line = br.readLine()) != null
-                                                       && line.startsWith("220-")) {
-                                       }
-                                       if (line == null || !line.startsWith("220")) {
-                                               continue;
-                                       }
-
-                                       pw.print("HELO www.cacert.org\r\n");
-                                       pw.flush();
-
-                                       while ((line = br.readLine()) != null
-                                                       && line.startsWith("220")) {
-                                       }
-
-                                       if (line == null || !line.startsWith("250")) {
-                                               continue;
-                                       }
-                                       pw.print("MAIL FROM: <returns@cacert.org>\r\n");
-                                       pw.flush();
-
-                                       line = br.readLine();
-
-                                       if (line == null || !line.startsWith("250")) {
-                                               continue;
-                                       }
-                                       pw.print("RCPT TO: <" + address + ">\r\n");
-                                       pw.flush();
-
-                                       line = br.readLine();
-                                       pw.print("QUIT\r\n");
-                                       pw.flush();
-
-                                       try {
-                                               PreparedStatement statmt = DatabaseConnection
-                                                               .getInstance()
-                                                               .prepare(
-                                                                               "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
-                                               statmt.setString(1, address);
-                                               statmt.setString(2, line);
-                                               statmt.setInt(3, forUid);
-                                               statmt.execute();
-                                       } catch (SQLException e) {
-                                               e.printStackTrace();
-                                       }
-
-                                       if (line == null || !line.startsWith("250")) {
-                                               return line;
-                                       } else {
-                                               return OK;
-                                       }
-                               }
-
-                       }
-               }
-               try {
-                       PreparedStatement statmt = DatabaseConnection
-                                       .getInstance()
-                                       .prepare(
-                                                       "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
-                       statmt.setString(1, address);
-                       statmt.setString(2,
-                                       "Failed to make a connection to the mail server");
-                       statmt.setInt(3, forUid);
-                       statmt.execute();
-               } catch (SQLException e) {
-                       e.printStackTrace();
-               }
-               return FAIL;
-       }
-       private static LinkedList<String> getMxHosts(String domain)
-                       throws IOException {
-               LinkedList<String> mxhosts = new LinkedList<String>();
-               Process dig = Runtime.getRuntime().exec(
-                               new String[]{"dig", "+short", "MX", domain});
-               try (BufferedReader br = new BufferedReader(new InputStreamReader(
-                               dig.getInputStream()))) {
-                       String line;
-                       while ((line = br.readLine()) != null) {
-                               String[] mxparts = line.split(" ", 2);
-                               if (mxparts.length != 2) {
-                                       continue;
-                               }
-                               mxhosts.add(mxparts[1].substring(0, mxparts[1].length() - 1));
-                       }
-               }
-               return mxhosts;
-       }
-
-}
index ba40bb849cf208bdceef4292efd978a6edda6d29..644c46218709f6fc8d28e6551c093d1fceae12ec 100644 (file)
 package org.cacert.gigi.email;
 
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.cacert.gigi.database.DatabaseConnection;
 
 public abstract class EmailProvider {
        public abstract void sendmail(String to, String subject, String message,
                        String from, String replyto, String toname, String fromname,
                        String errorsto, boolean extra) throws IOException;
-       private static EmailProvider instance = new Sendmail();
+       private static EmailProvider instance;
        public static EmailProvider getInstance() {
                return instance;
        }
+       public static void init(Properties conf) {
+               try {
+                       Class<?> c = Class.forName(conf.getProperty("emailProvider"));
+                       instance = (EmailProvider) c.getDeclaredConstructor(
+                                       Properties.class).newInstance(conf);
+               } catch (ReflectiveOperationException e) {
+                       e.printStackTrace();
+               }
+       }
+
+       public static final String OK = "OK";
+       public static final String FAIL = "FAIL";
+       private static final Pattern MAIL = Pattern
+                       .compile("^([a-zA-Z0-9])+([a-zA-Z0-9\\+\\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\\._-]+)+$");
+
+       public String checkEmailServer(int forUid, String address)
+                       throws IOException {
+               if (MAIL.matcher(address).matches()) {
+                       String[] parts = address.split("@", 2);
+                       String domain = parts[1];
+
+                       LinkedList<String> mxhosts = getMxHosts(domain);
+
+                       for (String host : mxhosts) {
+                               try (Socket s = new Socket(host, 25);
+                                               BufferedReader br = new BufferedReader(
+                                                               new InputStreamReader(s.getInputStream()));
+                                               PrintWriter pw = new PrintWriter(s.getOutputStream())) {
+                                       String line;
+                                       while ((line = br.readLine()) != null
+                                                       && line.startsWith("220-")) {
+                                       }
+                                       if (line == null || !line.startsWith("220")) {
+                                               continue;
+                                       }
+
+                                       pw.print("HELO www.cacert.org\r\n");
+                                       pw.flush();
+
+                                       while ((line = br.readLine()) != null
+                                                       && line.startsWith("220")) {
+                                       }
+
+                                       if (line == null || !line.startsWith("250")) {
+                                               continue;
+                                       }
+                                       pw.print("MAIL FROM: <returns@cacert.org>\r\n");
+                                       pw.flush();
+
+                                       line = br.readLine();
+
+                                       if (line == null || !line.startsWith("250")) {
+                                               continue;
+                                       }
+                                       pw.print("RCPT TO: <" + address + ">\r\n");
+                                       pw.flush();
+
+                                       line = br.readLine();
+                                       pw.print("QUIT\r\n");
+                                       pw.flush();
+
+                                       try {
+                                               PreparedStatement statmt = DatabaseConnection
+                                                               .getInstance()
+                                                               .prepare(
+                                                                               "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
+                                               statmt.setString(1, address);
+                                               statmt.setString(2, line);
+                                               statmt.setInt(3, forUid);
+                                               statmt.execute();
+                                       } catch (SQLException e) {
+                                               e.printStackTrace();
+                                       }
+
+                                       if (line == null || !line.startsWith("250")) {
+                                               return line;
+                                       } else {
+                                               return OK;
+                                       }
+                               }
+
+                       }
+               }
+               try {
+                       PreparedStatement statmt = DatabaseConnection
+                                       .getInstance()
+                                       .prepare(
+                                                       "insert into `pinglog` set `when`=NOW(), `email`=?, `result`=?, `uid`=?");
+                       statmt.setString(1, address);
+                       statmt.setString(2,
+                                       "Failed to make a connection to the mail server");
+                       statmt.setInt(3, forUid);
+                       statmt.execute();
+               } catch (SQLException e) {
+                       e.printStackTrace();
+               }
+               return FAIL;
+       }
+       private static LinkedList<String> getMxHosts(String domain)
+                       throws IOException {
+               LinkedList<String> mxhosts = new LinkedList<String>();
+               Process dig = Runtime.getRuntime().exec(
+                               new String[]{"dig", "+short", "MX", domain});
+               try (BufferedReader br = new BufferedReader(new InputStreamReader(
+                               dig.getInputStream()))) {
+                       String line;
+                       while ((line = br.readLine()) != null) {
+                               String[] mxparts = line.split(" ", 2);
+                               if (mxparts.length != 2) {
+                                       continue;
+                               }
+                               mxhosts.add(mxparts[1].substring(0, mxparts[1].length() - 1));
+                       }
+               }
+               return mxhosts;
+       }
 }
index 5a85cd78baa6c98f2a471cffd6f19d58683b0be1..c61494b7eb4fe96cf9923c41e687b21ea7f69660 100644 (file)
@@ -9,10 +9,11 @@ import java.text.SimpleDateFormat;
 import java.util.Base64;
 import java.util.Date;
 import java.util.Locale;
+import java.util.Properties;
 import java.util.regex.Pattern;
 
 public class Sendmail extends EmailProvider {
-       protected Sendmail() {
+       protected Sendmail(Properties props) {
        }
        private static final Pattern NON_ASCII = Pattern
                        .compile("[^a-zA-Z0-9 .-\\[\\]!_@]");
diff --git a/src/org/cacert/gigi/email/TestEmailProvider.java b/src/org/cacert/gigi/email/TestEmailProvider.java
new file mode 100644 (file)
index 0000000..009ecf9
--- /dev/null
@@ -0,0 +1,70 @@
+package org.cacert.gigi.email;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Properties;
+
+class TestEmailProvider extends EmailProvider {
+       ServerSocket servs;
+       Socket client;
+       DataOutputStream out;
+       DataInputStream in;
+       protected TestEmailProvider(Properties props) {
+               try {
+                       servs = new ServerSocket(Integer.parseInt(props
+                                       .getProperty("emailProvider.port")), 10,
+                                       InetAddress.getByName("127.0.0.1"));
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+       }
+       @Override
+       public synchronized void sendmail(String to, String subject,
+                       String message, String from, String replyto, String toname,
+                       String fromname, String errorsto, boolean extra) throws IOException {
+               boolean sent = false;
+               while (!sent) {
+                       assureLocalConnection();
+                       try {
+                               out.writeUTF("mail");
+                               write(to);
+                               write(subject);
+                               write(message);
+                               write(from);
+                               write(replyto);
+                               out.flush();
+                               sent = true;
+                       } catch (IOException e) {
+                               client = null;
+                       }
+               }
+       }
+       private void assureLocalConnection() throws IOException {
+               if (client == null || client.isClosed()) {
+                       client = servs.accept();
+                       out = new DataOutputStream(client.getOutputStream());
+                       in = new DataInputStream(client.getInputStream());
+               }
+       }
+       @Override
+       public String checkEmailServer(int forUid, String address)
+                       throws IOException {
+               assureLocalConnection();
+               out.writeUTF("challengeAddrBox");
+               out.writeUTF(address);
+               return in.readUTF();
+       }
+
+       private void write(String to) throws IOException {
+               if (to == null) {
+                       out.writeUTF("<null>");
+               } else {
+                       out.writeUTF(to);
+               }
+       }
+
+}
index 583a6da88168dd380ab70e3864f6bd94e2613d47..d88b6983b23611eefda46801db4a6dd6202b2790 100644 (file)
@@ -71,8 +71,10 @@ public class LoginPage extends Page {
                String un = req.getParameter("username");
                String pw = req.getParameter("password");
                try {
-                       PreparedStatement ps = DatabaseConnection.getInstance().prepare(
-                                       "SELECT `password`, `id` FROM `users` WHERE `email`=?");
+                       PreparedStatement ps = DatabaseConnection
+                                       .getInstance()
+                                       .prepare(
+                                                       "SELECT `password`, `id` FROM `users` WHERE `email`=? AND locked='0' AND verified='1'");
                        ps.setString(1, un);
                        ResultSet rs = ps.executeQuery();
                        if (rs.next()) {
diff --git a/src/org/cacert/gigi/pages/Verify.java b/src/org/cacert/gigi/pages/Verify.java
new file mode 100644 (file)
index 0000000..8d29266
--- /dev/null
@@ -0,0 +1,72 @@
+package org.cacert.gigi.pages;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.cacert.gigi.database.DatabaseConnection;
+
+public class Verify extends Page {
+       public static final String PATH = "/verify";
+       public Verify() {
+               super("Verify email");
+       }
+       @Override
+       public boolean needsLogin() {
+               return false;
+       }
+       @Override
+       public void doGet(HttpServletRequest req, HttpServletResponse resp)
+                       throws IOException {
+               PrintWriter out = resp.getWriter();
+               String hash = req.getParameter("hash");
+               String type = req.getParameter("type");
+               String id = req.getParameter("id");
+               if ("email".equals(type)) {
+                       try {
+                               PreparedStatement ps = DatabaseConnection
+                                               .getInstance()
+                                               .prepare(
+                                                               "select email, memid from `email` where `id`=? and `hash`=? and `hash` != '' and `deleted` = 0");
+                               ps.setString(1, id);
+                               ps.setString(2, hash);
+                               ResultSet rs = ps.executeQuery();
+                               rs.last();
+                               if (rs.getRow() == 1) {
+                                       PreparedStatement ps1 = DatabaseConnection
+                                                       .getInstance()
+                                                       .prepare(
+                                                                       "update `email` set `hash`='', `modified`=NOW() where `id`=?");
+                                       ps1.setString(1, id);
+                                       ps1.execute();
+                                       PreparedStatement ps2 = DatabaseConnection
+                                                       .getInstance()
+                                                       .prepare(
+                                                                       "update `users` set `verified`='1' where `id`=? and `email`=? and `verified`='0'");
+                                       ps2.setString(1, rs.getString(2));
+                                       ps2.setString(2, rs.getString(1));
+                                       ps2.execute();
+                                       out.println("Your email is good.");
+                               } else {
+                                       out.println("Your request is invalid");
+                               }
+                       } catch (SQLException e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+       @Override
+       public void doPost(HttpServletRequest req, HttpServletResponse resp)
+                       throws IOException {
+               String hash = req.getParameter("hash");
+               String type = req.getParameter("type");
+               if ("email".equals(type)) {
+
+               }
+       }
+}
index 58203edf361554126a7de6ef6307c6da3d5bb198..39e91a273fba0901d6f89ce4d15778b8fdefca30 100644 (file)
@@ -16,7 +16,6 @@ import javax.servlet.http.HttpServletRequest;
 import org.cacert.gigi.Language;
 import org.cacert.gigi.User;
 import org.cacert.gigi.database.DatabaseConnection;
-import org.cacert.gigi.email.EmailChecker;
 import org.cacert.gigi.email.EmailProvider;
 import org.cacert.gigi.output.DateSelector;
 import org.cacert.gigi.output.Template;
@@ -172,12 +171,13 @@ public class Signup {
                        e.printStackTrace();
                        failed = true;
                }
-               String mailResult = EmailChecker.FAIL;
+               String mailResult = EmailProvider.FAIL;
                try {
-                       mailResult = EmailChecker.checkEmailServer(0, buildup.getEmail());
+                       mailResult = EmailProvider.getInstance().checkEmailServer(0,
+                                       buildup.getEmail());
                } catch (IOException e) {
                }
-               if (!mailResult.equals(EmailChecker.OK)) {
+               if (!mailResult.equals(EmailProvider.OK)) {
                        if (mailResult.startsWith("4")) {
                                outputError(
                                                out,
@@ -193,7 +193,7 @@ public class Signup {
                                                                + " couldn't be made to your server, or the server"
                                                                + " rejected the email address as invalid");
                        }
-                       if (mailResult.equals(EmailChecker.FAIL)) {
+                       if (mailResult.equals(EmailProvider.FAIL)) {
                                outputError(out, req,
                                                "Failed to make a connection to the mail server");
                        } else {
@@ -223,54 +223,60 @@ public class Signup {
 
        private void run(HttpServletRequest req, String password)
                        throws SQLException {
-               String hash = RandomToken.generateToken(16);
+               try {
+                       DatabaseConnection.getInstance().beginTransaction();
+                       String hash = RandomToken.generateToken(16);
 
-               buildup.insert(password);
-               int memid = buildup.getId();
-               PreparedStatement ps = DatabaseConnection.getInstance().prepare(
-                               "insert into `email` set `email`=?,"
-                                               + " `hash`=?, `created`=NOW(),`memid`=?");
-               ps.setString(1, buildup.getEmail());
-               ps.setString(2, hash);
-               ps.setInt(3, memid);
-               ps.execute();
-               int emailid = DatabaseConnection.lastInsertId(ps);
-               ps = DatabaseConnection
-                               .getInstance()
-                               .prepare(
-                                               "insert into `alerts` set `memid`=?,"
-                                                               + " `general`=?, `country`=?, `regional`=?, `radius`=?");
-               ps.setInt(1, memid);
-               ps.setString(2, general ? "1" : "0");
-               ps.setString(3, country ? "1" : "0");
-               ps.setString(4, regional ? "1" : "0");
-               ps.setString(5, radius ? "1" : "0");
-               ps.execute();
-               Notary.writeUserAgreement(memid, "CCA", "account creation", "", true, 0);
+                       buildup.insert(password);
+                       int memid = buildup.getId();
+                       PreparedStatement ps = DatabaseConnection.getInstance().prepare(
+                                       "insert into `email` set `email`=?,"
+                                                       + " `hash`=?, `created`=NOW(),`memid`=?");
+                       ps.setString(1, buildup.getEmail());
+                       ps.setString(2, hash);
+                       ps.setInt(3, memid);
+                       ps.execute();
+                       int emailid = DatabaseConnection.lastInsertId(ps);
+                       ps = DatabaseConnection
+                                       .getInstance()
+                                       .prepare(
+                                                       "insert into `alerts` set `memid`=?,"
+                                                                       + " `general`=?, `country`=?, `regional`=?, `radius`=?");
+                       ps.setInt(1, memid);
+                       ps.setString(2, general ? "1" : "0");
+                       ps.setString(3, country ? "1" : "0");
+                       ps.setString(4, regional ? "1" : "0");
+                       ps.setString(5, radius ? "1" : "0");
+                       ps.execute();
+                       Notary.writeUserAgreement(memid, "CCA", "account creation", "",
+                                       true, 0);
 
-               StringBuffer body = new StringBuffer();
-               body.append(Page
-                               .translate(
-                                               req,
-                                               "Thanks for signing up with CAcert.org, below is the link you need to open to verify your account. Once your account is verified you will be able to start issuing certificates till your hearts' content!"));
-               body.append("\n\n");
-               body.append("http://");
-               body.append(ServerConstants.NORMAL_HOST_NAME);
-               body.append("/verify.php?type=email&emailid=");
-               body.append(emailid);
-               body.append("&hash=");
-               body.append(hash);
-               body.append("\n\n");
-               body.append(Page.translate(req, "Best regards"));
-               body.append("\n");
-               body.append(Page.translate(req, "CAcert.org Support!"));
-               try {
-                       EmailProvider.getInstance().sendmail(buildup.getEmail(),
-                                       "[CAcert.org] " + Page.translate(req, "Mail Probe"),
-                                       body.toString(), "support@cacert.org", null, null, null,
-                                       null, false);
-               } catch (IOException e) {
-                       e.printStackTrace();
+                       StringBuffer body = new StringBuffer();
+                       body.append(Page
+                                       .translate(
+                                                       req,
+                                                       "Thanks for signing up with CAcert.org, below is the link you need to open to verify your account. Once your account is verified you will be able to start issuing certificates till your hearts' content!"));
+                       body.append("\n\n");
+                       body.append(ServerConstants.NORMAL_HOST_NAME);
+                       body.append("/verify?type=email&id=");
+                       body.append(emailid);
+                       body.append("&hash=");
+                       body.append(hash);
+                       body.append("\n\n");
+                       body.append(Page.translate(req, "Best regards"));
+                       body.append("\n");
+                       body.append(Page.translate(req, "CAcert.org Support!"));
+                       try {
+                               EmailProvider.getInstance().sendmail(buildup.getEmail(),
+                                               "[CAcert.org] " + Page.translate(req, "Mail Probe"),
+                                               body.toString(), "support@cacert.org", null, null,
+                                               null, null, false);
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                       }
+                       DatabaseConnection.getInstance().commitTransaction();
+               } finally {
+                       DatabaseConnection.getInstance().quitTransaction();
                }
 
        }
index 92609ff5e6b8cc6296f640bee9c9fdd0848df209..92b231da5b3bf82abb266dde9fddb32aba629b88 100644 (file)
@@ -7,22 +7,19 @@ import org.cacert.gigi.database.DatabaseConnection;
 
 public class Notary {
        public static void writeUserAgreement(int memid, String document,
-                       String method, String comment, boolean active, int secmemid) {
-               try {
-                       PreparedStatement q = DatabaseConnection
-                                       .getInstance()
-                                       .prepare(
-                                                       "insert into `user_agreements` set `memid`=?, `secmemid`=?,"
-                                                                       + " `document`=?,`date`=NOW(), `active`=?,`method`=?,`comment`=?");
-                       q.setInt(1, memid);
-                       q.setInt(2, secmemid);
-                       q.setString(3, document);
-                       q.setInt(4, active ? 1 : 0);
-                       q.setString(5, method);
-                       q.setString(6, comment);
-                       q.execute();
-               } catch (SQLException e) {
-                       e.printStackTrace();
-               }
+                       String method, String comment, boolean active, int secmemid)
+                       throws SQLException {
+               PreparedStatement q = DatabaseConnection
+                               .getInstance()
+                               .prepare(
+                                               "insert into `user_agreements` set `memid`=?, `secmemid`=?,"
+                                                               + " `document`=?,`date`=NOW(), `active`=?,`method`=?,`comment`=?");
+               q.setInt(1, memid);
+               q.setInt(2, secmemid);
+               q.setString(3, document);
+               q.setInt(4, active ? 1 : 0);
+               q.setString(5, method);
+               q.setString(6, comment);
+               q.execute();
        }
 }
index c2d7fd853a9a2be010334d52d760a167635efd1e..82b124c42f5391a6f8a23273581ed515d0d2cfaf 100644 (file)
@@ -1,5 +1,5 @@
 package org.cacert.gigi.util;
 
 public class ServerConstants {
-       public static final String NORMAL_HOST_NAME = "www.cacert.org";
+       public static final String NORMAL_HOST_NAME = "http://www.cacert.org";
 }
diff --git a/tests/org/cacert/gigi/LoginTest.java b/tests/org/cacert/gigi/LoginTest.java
new file mode 100644 (file)
index 0000000..9aa24c7
--- /dev/null
@@ -0,0 +1,52 @@
+package org.cacert.gigi;
+
+import java.io.IOException;
+import static org.junit.Assert.*;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+import org.cacert.gigi.testUtils.ManagedTest;
+import org.junit.Test;
+
+public class LoginTest extends ManagedTest {
+       public static final String secureReference = "/account/certs/email";
+       @Test
+       public void testLoginUnverified() throws IOException {
+               long uniq = System.currentTimeMillis();
+               String email = "system" + uniq + "@testmail.org";
+               String pw = "1'aAaA";
+               registerUser("an", "bn", email, pw);
+               waitForMail();
+               assertFalse(isLoggedin(login(email, pw)));
+       }
+       @Test
+       public void testLoginVerified() throws IOException {
+               long uniq = System.currentTimeMillis();
+               String email = "system2" + uniq + "@testmail.org";
+               String pw = "1'aAaA";
+               createVerifiedUser("an", "bn", email, pw);
+               assertTrue(isLoggedin(login(email, pw)));
+       }
+       public boolean isLoggedin(String cookie) throws IOException {
+               URL u = new URL("https://" + getServerName() + secureReference);
+               HttpURLConnection huc = (HttpURLConnection) u.openConnection();
+               huc.addRequestProperty("Cookie", cookie);
+               return huc.getResponseCode() == 200;
+       }
+       public String login(String email, String pw) throws IOException {
+               URL u = new URL("https://" + getServerName() + "/login");
+               HttpURLConnection huc = (HttpURLConnection) u.openConnection();
+               huc.setDoOutput(true);
+               OutputStream os = huc.getOutputStream();
+               String data = "username=" + URLEncoder.encode(email, "UTF-8")
+                               + "&password=" + URLEncoder.encode(pw, "UTF-8");
+               os.write(data.getBytes());
+               os.flush();
+               String headerField = huc.getHeaderField("Set-Cookie");
+               headerField = headerField.substring(0, headerField.indexOf(';'));
+               return headerField;
+       }
+
+}
index 633fddc60f5273b68f91601b7f8c6d4b3f73ddc0..fa2e080df53474b5b7cd6d7305e4137bb0608af9 100644 (file)
@@ -1,32 +1,19 @@
 package org.cacert.gigi.pages.main;
 
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
+import java.util.regex.Pattern;
 
-import org.cacert.gigi.IOUtils;
 import org.cacert.gigi.InitTruststore;
+import org.cacert.gigi.testUtils.ManagedTest;
+import org.cacert.gigi.testUtils.TestEmailReciever.TestMail;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
-public class RegisterPageTest {
-       private static final URL registerService;
-       private static final int PORT = 4431;
+public class RegisterPageTest extends ManagedTest {
        static {
-               URL u = null;
-               try {
-                       u = new URL("https://localhost:" + PORT + "/register");
-               } catch (MalformedURLException e) {
-                       e.printStackTrace();
-               }
-               registerService = u;
                InitTruststore.run();
                HttpURLConnection.setFollowRedirects(false);
        }
@@ -36,10 +23,12 @@ public class RegisterPageTest {
        }
        @Test
        public void testSuccess() throws IOException {
-               String startError = fetchStartErrorMessage("fname=a&lname=b&email=e&pword1=ap&pword2=ap&day=1&month=1&year=1910&cca_agree=1");
-               assertTrue(startError, startError.startsWith("</div>"));
+               long uniq = System.currentTimeMillis();
+               registerUser("ab", "b", "correct" + uniq + "@email.de", "ap12UI.'");
+               TestMail tm = waitForMail();
+               String link = tm.extractLink();
+               assertTrue(link, link.startsWith("http://"));
        }
-
        @Test
        public void testNoFname() throws IOException {
                testFailedForm("lname=b&email=e&pword1=ap&pword2=ap&day=1&month=1&year=1910&cca_agree=1");
@@ -132,12 +121,11 @@ public class RegisterPageTest {
                                .contains("name=\"radius\" value=\"1\" checked=\"checked\">"));
        }
 
-       @Ignore
        @Test
        public void testDoubleMail() throws IOException {
                long uniq = System.currentTimeMillis();
                registerUser("RegisterTest", "User", "testmail" + uniq + "@cacert.org",
-                               "registerPW");
+                               "registerPW'1");
                try {
                        registerUser("RegisterTest", "User", "testmail" + uniq
                                        + "@cacert.org", "registerPW");
@@ -147,45 +135,22 @@ public class RegisterPageTest {
 
                }
        }
+       @Test
+       public void testInvalidMailbox() {
+               getMailReciever().setApproveRegex(Pattern.compile("a"));
+               long uniq = System.currentTimeMillis();
+               try {
+                       registerUser("RegisterTest", "User", "testInvalidMailbox" + uniq
+                                       + "@cacert.org", "registerPW");
+                       throw new Error(
+                                       "Registering a user with invalid mailbox must fail.");
+               } catch (AssertionError e) {
 
-       private static void testFailedForm(String query) throws IOException {
+               }
+       }
+       private void testFailedForm(String query) throws IOException {
                String startError = fetchStartErrorMessage(query);
                assertTrue(startError, !startError.startsWith("</div>"));
        }
-       private static String fetchStartErrorMessage(String query)
-                       throws IOException {
-               String d = runRegister(query);
-               String formFail = "<div class='formError'>";
-               int idx = d.indexOf(formFail);
-               assertNotEquals(-1, idx);
-               String startError = d.substring(idx + formFail.length(), idx + 100)
-                               .trim();
-               return startError;
-       }
 
-       public static void registerUser(String firstName, String lastName,
-                       String email, String password) {
-               try {
-                       String query = "fname=" + URLEncoder.encode(firstName, "UTF-8")
-                                       + "&lname=" + URLEncoder.encode(lastName, "UTF-8")
-                                       + "&email=" + URLEncoder.encode(email, "UTF-8")
-                                       + "&pword1=" + URLEncoder.encode(password, "UTF-8")
-                                       + "&pword2=" + URLEncoder.encode(password, "UTF-8")
-                                       + "&day=1&month=1&year=1910&cca_agree=1";
-                       String data = fetchStartErrorMessage(query);
-                       assertTrue(data, data.startsWith("</div>"));
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error(e);
-               } catch (IOException e) {
-                       throw new Error(e);
-               }
-       }
-       private static String runRegister(String param) throws IOException {
-               HttpURLConnection uc = (HttpURLConnection) registerService
-                               .openConnection();
-               uc.setDoOutput(true);
-               uc.getOutputStream().write(param.getBytes());
-               String d = IOUtils.readURL(uc);
-               return d;
-       }
 }
diff --git a/tests/org/cacert/gigi/testUtils/ManagedTest.java b/tests/org/cacert/gigi/testUtils/ManagedTest.java
new file mode 100644 (file)
index 0000000..c9a44b8
--- /dev/null
@@ -0,0 +1,189 @@
+package org.cacert.gigi.testUtils;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.cacert.gigi.DevelLauncher;
+import org.cacert.gigi.IOUtils;
+import org.cacert.gigi.InitTruststore;
+import org.cacert.gigi.testUtils.TestEmailReciever.TestMail;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class ManagedTest {
+       private final String registerService = "/register";
+
+       private static TestEmailReciever ter;
+       private static Process gigi;
+       private static String url = "localhost:4443";
+
+       public static String getServerName() {
+               return url;
+       }
+       static Properties testProps = new Properties();
+       static {
+               InitTruststore.run();
+               HttpURLConnection.setFollowRedirects(false);
+       }
+
+       @BeforeClass
+       public static void connectToServer() {
+               try {
+                       testProps.load(new FileInputStream("config/test.properties"));
+                       String type = testProps.getProperty("type");
+                       if (type.equals("local")) {
+                               url = testProps.getProperty("server");
+                               String[] parts = testProps.getProperty("mail").split(":", 2);
+                               ter = new TestEmailReciever(new InetSocketAddress(parts[0],
+                                               Integer.parseInt(parts[1])));
+                               return;
+                       }
+                       url = "localhost:" + testProps.getProperty("serverPort");
+                       gigi = Runtime.getRuntime().exec(testProps.getProperty("java"));
+                       DataOutputStream toGigi = new DataOutputStream(
+                                       gigi.getOutputStream());
+                       System.out.println("... starting server");
+                       Properties mainProps = new Properties();
+                       mainProps.load(new FileInputStream("config/gigi.properties"));
+                       mainProps.setProperty("host", "127.0.0.1");
+                       mainProps.setProperty("port", testProps.getProperty("serverPort"));
+                       mainProps.setProperty("emailProvider",
+                                       "org.cacert.gigi.email.TestEmailProvider");
+                       mainProps.setProperty("emailProvider.port", "8473");
+
+                       byte[] cacerts = Files
+                                       .readAllBytes(Paths.get("config/cacerts.jks"));
+                       byte[] keystore = Files.readAllBytes(Paths
+                                       .get("config/keystore.pkcs12"));
+
+                       DevelLauncher.writeGigiConfig(toGigi, new byte[]{},
+                                       "changeit".getBytes(), mainProps, cacerts, keystore);
+                       toGigi.flush();
+                       // TODO wait for ready
+                       try {
+                               Thread.sleep(3000);
+                       } catch (InterruptedException e) {
+                               e.printStackTrace();
+                       }
+                       final BufferedReader br = new BufferedReader(new InputStreamReader(
+                                       gigi.getErrorStream()));
+                       String line;
+                       while ((line = br.readLine()) != null
+                                       && !line.contains("Server:main: Started")) {
+                               System.err.println(line);
+                       }
+                       new Thread() {
+                               @Override
+                               public void run() {
+                                       String line;
+                                       try {
+                                               while ((line = br.readLine()) != null) {
+                                                       System.err.println(line);
+                                               }
+                                       } catch (IOException e) {
+                                               e.printStackTrace();
+                                       }
+                               }
+                       }.start();
+                       System.err.println(line);
+                       if (line == null) {
+                               throw new Error("Server startup failed");
+                       }
+                       ter = new TestEmailReciever(
+                                       new InetSocketAddress("localhost", 8473));
+               } catch (IOException e) {
+                       throw new Error(e);
+               }
+
+       }
+       @AfterClass
+       public static void tearDownServer() {
+               String type = testProps.getProperty("type");
+               if (type.equals("local")) {
+                       return;
+               }
+               gigi.destroy();
+       }
+
+       @After
+       public void removeMails() {
+               ter.reset();
+       }
+
+       public TestMail waitForMail() {
+               try {
+                       return ter.recieve();
+               } catch (InterruptedException e) {
+                       throw new Error(e);
+               }
+       }
+       public static TestEmailReciever getMailReciever() {
+               return ter;
+       }
+       public String runRegister(String param) throws IOException {
+               HttpURLConnection uc = (HttpURLConnection) new URL("https://"
+                               + getServerName() + registerService).openConnection();
+               uc.setDoOutput(true);
+               uc.getOutputStream().write(param.getBytes());
+               String d = IOUtils.readURL(uc);
+               return d;
+       }
+       public String fetchStartErrorMessage(String query) throws IOException {
+               String d = runRegister(query);
+               String formFail = "<div class='formError'>";
+               int idx = d.indexOf(formFail);
+               assertNotEquals(-1, idx);
+               String startError = d.substring(idx + formFail.length(), idx + 100)
+                               .trim();
+               return startError;
+       }
+
+       public void registerUser(String firstName, String lastName, String email,
+                       String password) {
+               try {
+                       String query = "fname=" + URLEncoder.encode(firstName, "UTF-8")
+                                       + "&lname=" + URLEncoder.encode(lastName, "UTF-8")
+                                       + "&email=" + URLEncoder.encode(email, "UTF-8")
+                                       + "&pword1=" + URLEncoder.encode(password, "UTF-8")
+                                       + "&pword2=" + URLEncoder.encode(password, "UTF-8")
+                                       + "&day=1&month=1&year=1910&cca_agree=1";
+                       String data = fetchStartErrorMessage(query);
+                       assertTrue(data, data.startsWith("</div>"));
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error(e);
+               } catch (IOException e) {
+                       throw new Error(e);
+               }
+       }
+       public void createVerifiedUser(String firstName, String lastName,
+                       String email, String password) {
+               registerUser(firstName, lastName, email, password);
+               try {
+                       TestMail tm = ter.recieve();
+                       String verifyLink = tm.extractLink();
+                       String[] parts = verifyLink.split("\\?");
+                       URL u = new URL("https://" + getServerName() + "/verify?"
+                                       + parts[1]);
+                       u.openStream().close();;
+               } catch (InterruptedException e) {
+                       throw new Error(e);
+               } catch (IOException e) {
+                       throw new Error(e);
+               }
+       }
+}
diff --git a/tests/org/cacert/gigi/testUtils/TestEmailReciever.java b/tests/org/cacert/gigi/testUtils/TestEmailReciever.java
new file mode 100644 (file)
index 0000000..446d2aa
--- /dev/null
@@ -0,0 +1,113 @@
+package org.cacert.gigi.testUtils;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.Socket;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestEmailReciever implements Runnable {
+       public class TestMail {
+               String to;
+               String subject;
+               String message;
+               String from;
+               String replyto;
+               public TestMail(String to, String subject, String message, String from,
+                               String replyto) {
+                       this.to = to;
+                       this.subject = subject;
+                       this.message = message;
+                       this.from = from;
+                       this.replyto = replyto;
+               }
+               public String getTo() {
+                       return to;
+               }
+               public String getSubject() {
+                       return subject;
+               }
+               public String getMessage() {
+                       return message;
+               }
+               public String getFrom() {
+                       return from;
+               }
+               public String getReplyto() {
+                       return replyto;
+               }
+               public String extractLink() {
+                       Pattern link = Pattern.compile("http://[^\\s]+(?=\\s)");
+                       Matcher m = link.matcher(getMessage());
+                       m.find();
+                       return m.group(0);
+               }
+
+       }
+       private Socket s;
+       private DataInputStream dis;
+       private DataOutputStream dos;
+
+       public TestEmailReciever(SocketAddress target) throws IOException {
+               s = new Socket();
+               s.connect(target);
+               s.setKeepAlive(true);
+               s.setSoTimeout(1000 * 60 * 60);
+               dis = new DataInputStream(s.getInputStream());
+               dos = new DataOutputStream(s.getOutputStream());
+               new Thread(this).start();
+       }
+       LinkedBlockingQueue<TestMail> mails = new LinkedBlockingQueue<TestEmailReciever.TestMail>();
+
+       public TestMail recieve() throws InterruptedException {
+               return mails.poll(5, TimeUnit.SECONDS);
+       }
+       @Override
+       public void run() {
+               try {
+                       while (true) {
+                               String type = dis.readUTF();
+                               if (type.equals("mail")) {
+                                       String to = dis.readUTF();
+                                       String subject = dis.readUTF();
+                                       String message = dis.readUTF();
+                                       String from = dis.readUTF();
+                                       String replyto = dis.readUTF();
+                                       mails.add(new TestMail(to, subject, message, from, replyto));
+                               } else if (type.equals("challengeAddrBox")) {
+                                       String email = dis.readUTF();
+                                       if (approveRegex.matcher(email).matches()) {
+                                               System.out.println("approving mbox: " + email);
+                                               dos.writeUTF("OK");
+                                       } else {
+                                               System.out.println("rejecting mbox: " + email);
+                                               dos.writeUTF("FAIL");
+                                       }
+                                       dos.flush();
+                               } else {
+                                       System.err.println("Unknown type: " + type);
+                               }
+                       }
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+
+       }
+       Pattern approveRegex = Pattern.compile(".*");
+       public void setApproveRegex(Pattern approveRegex) {
+               this.approveRegex = approveRegex;
+       }
+
+       public void clearMails() {
+               mails.clear();
+       }
+       public void reset() {
+               clearMails();
+               approveRegex = Pattern.compile(".*");
+       }
+
+}
diff --git a/util/org/cacert/gigi/util/DatabaseManager.java b/util/org/cacert/gigi/util/DatabaseManager.java
new file mode 100644 (file)
index 0000000..34f80f9
--- /dev/null
@@ -0,0 +1,36 @@
+package org.cacert.gigi.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class DatabaseManager {
+       public static String readFile(File f) throws IOException {
+               return new String(Files.readAllBytes(f.toPath()));
+       }
+       public static void main(String[] args) throws SQLException,
+                       ClassNotFoundException, IOException {
+               if (args.length < 4) {
+                       System.err
+                                       .println("Usage: com.mysql.jdbc.Driver jdbc:mysql://localhost/cacert user password");
+                       return;
+               }
+               Class.forName(args[0]);
+               Connection conn = DriverManager
+                               .getConnection(args[1], args[2], args[3]);
+               Statement stmt = conn.createStatement();
+               String sql = readFile(new File("doc/tableStructure.sql"));
+               String[] stmts = sql.split(";");
+               for (String string : stmts) {
+                       if (!string.trim().equals("")) {
+                               stmt.addBatch(string);
+                       }
+               }
+               stmt.executeBatch();
+               stmt.close();
+       }
+}