HSAdmin Backend Domains, E-Mail, Datenbanken
Peter Hormanns
2010-10-05 726244a061d9df921a7a40d7e1f067e24cf6d77d
Module member, emailaddress, domain
24 files added
1 files deleted
17 files modified
3240 ■■■■■ changed files
hsarback/build.xml 1 ●●●● patch | view | raw | blame | history
hsarback/conf/META-INF/persistence.xml 3 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/cliClientConnector/Base64.java 244 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/cliClientConnector/CLIClientConnectorServlet.java 12 ●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/model/GenericModuleImpl.java 20 ●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/util/DNSCheck.java 51 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/util/TextUtil.java 45 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/util/dns/DNSQuery.java 395 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/util/dns/DNSResourceRecord.java 190 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/core/util/dns/DNSService.java 80 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/cust/Customer.java 12 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/cust/CustomerModuleImpl.java 67 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/Domain.java 232 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/DomainModuleImpl.java 198 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/DomainProcessorFactory.java 207 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/htaccess.jtpl 2 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/index.html.jtpl 22 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/test.cgi.jtpl 5 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/dom/zonefile.jtpl 29 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAddress.java 208 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAddressModuleImpl.java 75 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAddressProcessorFactory.java 70 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAlias.java 200 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAliasModuleImpl.java 20 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/email/EMailAliasProcessorFactory.java 46 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/mods/user/UnixUserModuleImpl.java 14 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/remote/AbstractRemote.java 6 ●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/remote/CustomerRemote.java 4 ●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/remote/DomainRemote.java 63 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/remote/EMailAddressRemote.java 74 ●●●●● patch | view | raw | blame | history
hsarback/src/de/hsadmin/remote/EMailAliasRemote.java 46 ●●●●● patch | view | raw | blame | history
hsarback/src/org/apache/xmlrpc/webserver/XmlRpcServlet.properties 3 ●●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/CustomerTest.java 63 ●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/DomainTest.java 140 ●●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/EMailAddressTest.java 246 ●●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/EMailAliasTest.java 61 ●●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/QueueTaskTest.java 8 ●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/RemoteCASHelper.java 15 ●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/RemoteTest.java 5 ●●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/RemoteTestHelper.java 14 ●●●● patch | view | raw | blame | history
hsarback/test/de/hsadmin/remote/UnixUserTest.java 18 ●●●● patch | view | raw | blame | history
hsarback/webapp/WEB-INF/web.xml 26 ●●●●● patch | view | raw | blame | history
hsarback/build.xml
@@ -41,6 +41,7 @@
            <classes dir="build/cls" />
            <classes dir="src">
                <include name="**/*.properties"/>
                <include name="**/*.jtpl"/>
            </classes>
            <classes dir="conf">
                <include name="**/*.xml"/>
hsarback/conf/META-INF/persistence.xml
@@ -16,6 +16,9 @@
        <class>de.hsadmin.mods.pac.Hive</class> 
        <class>de.hsadmin.mods.pac.INetAddress</class>
        <class>de.hsadmin.mods.user.UnixUser</class> 
        <class>de.hsadmin.mods.dom.Domain</class>
        <class>de.hsadmin.mods.email.EMailAddress</class>
        <class>de.hsadmin.mods.email.EMailAlias</class>
        <properties>
            <property name="openjpa.ConnectionDriverName" value="org.postgresql.Driver"/>
            <!-- 
hsarback/src/de/hsadmin/cliClientConnector/Base64.java
File was deleted
hsarback/src/de/hsadmin/cliClientConnector/CLIClientConnectorServlet.java
@@ -16,6 +16,8 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.GenericModuleImpl;
import de.hsadmin.core.model.ModuleInterface;
@@ -32,7 +34,7 @@
 */
public class CLIClientConnectorServlet extends HttpServlet {
    private static final long serialVersionUID = 7150004719303750077L;
    public static final String version = "1.0.8 (2009/May/27 18:22)";
    public static final String version = "1.0.9 (2010/Oct/05 18:34)";
    private Map<String, Class<?>> componentmap;
    private Map<String, String> componentDescriptions;
    private ArgumentParser parser;
@@ -445,11 +447,9 @@
                // parse login information
                String[] a = auth.split(" ", 2);
                String ticket = a[1];
                byte[] decoded = Base64.base64ToByteArray(ticket.trim());
                StringBuffer s = new StringBuffer();
                for (int i = 0; i < decoded.length; i++)
                    s.append((char) decoded[i]);
                a = s.toString().split(":", 2);
                byte[] decoded = Base64.decodeBase64(ticket.trim().getBytes());
                String s = new String(decoded);
                a = s.split(":", 2);
                // try to log in
                String login = a[0];
                ticket = a[1];
hsarback/src/de/hsadmin/core/model/GenericModuleImpl.java
@@ -37,12 +37,8 @@
            EntitySessionHelper.createEntitySessionWrapper(newEntity.getClass());
        wrapper.construct(tx);
        AbstractEntity result = null;
        try {
            result = wrapper.add(newEntity);
            return result;
        } catch (Exception e) {
            throw new HSAdminException(e);
        }
        result = wrapper.add(newEntity);
        return result;
    }
    @Override
@@ -76,11 +72,7 @@
        log.debug("update(" + existingEntity + ")");
        AbstractModuleImpl wrapper = EntitySessionHelper.createEntitySessionWrapper(existingEntity.getClass());
        wrapper.construct(tx);
        try {
            return wrapper.update(existingEntity);
        } catch (Exception e) {
            throw new HSAdminException(e);
        }
        return wrapper.update(existingEntity);
    }
    @Override
@@ -89,11 +81,7 @@
        AbstractModuleImpl wrapper = 
            EntitySessionHelper.createEntitySessionWrapper(existingEntity.getClass());
        wrapper.construct(tx);
        try {
            wrapper.delete(existingEntity);
        } catch (Exception e) {
            throw new HSAdminException(e);
        }
        wrapper.delete(existingEntity);
    }
}
hsarback/src/de/hsadmin/core/util/DNSCheck.java
New file
@@ -0,0 +1,51 @@
package de.hsadmin.core.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.util.dns.DNSQuery;
import de.hsadmin.core.util.dns.DNSService;
public class DNSCheck {
    private String dnsServer;
    public DNSCheck(String dnsServer) {
        this.dnsServer = dnsServer;
        InetAddress dnsInetAddress;
        try {
            dnsInetAddress = InetAddress.getByName(dnsServer);
            DNSService.SetDNSAddress(dnsInetAddress);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public boolean checkDomain(String domain) throws HSAdminException {
        boolean hasNSRecord = false;
        boolean hasARecord = false;
        DNSQuery dnsQuery = new DNSQuery();
        dnsQuery.SetQuery(DNSQuery.TYPE_NS, DNSQuery.CLASS_INTERNET, domain);
        if (DNSService.PerformDNSQuery(dnsQuery)) {
            hasNSRecord = dnsQuery.getDNSAuthorityRecords() != null && dnsQuery.getDNSAuthorityRecords().length > 0;
            if (dnsQuery.getDNSAuthorityRecords() != null) {
                System.out.println("NS:" + dnsQuery.getDNSAuthorityRecords().length);
            }
        } else {
            throw new HSAdminException("domain " + domain + " is not delegated to " + dnsServer);
        }
        dnsQuery.SetQuery(DNSQuery.TYPE_A, DNSQuery.CLASS_INTERNET, domain);
        if (DNSService.PerformDNSQuery(dnsQuery)) {
            hasARecord = dnsQuery.getDNSAnswerRecords() != null && dnsQuery.getDNSAnswerRecords().length > 0;
            if (dnsQuery.getDNSAnswerRecords() != null) {
                System.out.println("A: " + dnsQuery.getDNSAnswerRecords().length);
            }
        } else {
            throw new HSAdminException("domain " + domain + " is not delegated to " + dnsServer);
        }
        return hasNSRecord & !hasARecord;
    }
}
hsarback/src/de/hsadmin/core/util/TextUtil.java
New file
@@ -0,0 +1,45 @@
package de.hsadmin.core.util;
public class TextUtil {
    public static String replaceUmlauts(String umlautString) {
        StringBuffer buffer = new StringBuffer();
        for (char c : umlautString.toCharArray()) {
            if (    (c >= 'a' && c <= 'z') ||
                    (c >= 'A' && c <= 'Z') ||
                    (c >= '0' && c <= '9') ||
                    (c == ' ') ) {
                        buffer.append(c);
            } else {
                switch (c) {
                case 'ä':
                    buffer.append("ae");
                    break;
                case 'ö':
                    buffer.append("oe");
                    break;
                case 'ü':
                    buffer.append("ue");
                    break;
                case 'Ä':
                    buffer.append("Ae");
                    break;
                case 'Ö':
                    buffer.append("Oe");
                    break;
                case 'Ü':
                    buffer.append("Ue");
                    break;
                case 'ß':
                    buffer.append("ss");
                    break;
                default:
                    buffer.append('-');
                    break;
                }
            }
        }
        return buffer.toString();
    }
}
hsarback/src/de/hsadmin/core/util/dns/DNSQuery.java
New file
@@ -0,0 +1,395 @@
package de.hsadmin.core.util.dns;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class DNSQuery {
    // DNS constants
    private final static int MAX_LABEL = 128;
    // DNS flag values
    private final static int QR_MASK = 0x8000;
    private final static int QR_SHIFT = 0;
    private final static int OPCODE_MASK = 0x7800;
    private final static int AA_MASK = 0x0400;
    private final static int TC_MASK = 0x0200;
    private final static int RD_MASK = 0x0100;
    private final static int RA_MASK = 0x0080;
    private final static int ZERO_MASK = 0x0070;
    private final static int RCODE_MASK = 0x000f;
    // DNS query/type constants
    public final static int TYPE_A = 1;
    public final static int TYPE_NS = 2;
    public final static int TYPE_MD = 3;
    public final static int TYPE_MF = 4;
    public final static int TYPE_CNAME = 5;
    public final static int TYPE_SOA = 6;
    public final static int TYPE_MB = 7;
    public final static int TYPE_MG = 8;
    public final static int TYPE_MR = 9;
    public final static int TYPE_NULL = 10;
    public final static int TYPE_WKS = 11;
    public final static int TYPE_PTR = 12;
    public final static int TYPE_HINFO = 13;
    public final static int TYPE_MINFO = 14;
    public final static int TYPE_MX = 15;
    public final static int TYPE_AXFR = 252;
    public final static int TYPE_ANY = 255;
    // DNS query classes
    public final static byte CLASS_INTERNET = 1;
    // query data members
    public int m_idQuery = 0x0101;
    // opcodes
    public final static int OP_NORM_QRY = 0x0000;
    public final static int OP_INVERSE_QRY = 0x0800;
    public final static int OP_SERVER_STAT = 0x1000;
    public int m_iOpCode = OP_NORM_QRY;
    // flags
    public boolean m_fQueryResult = false;
    public boolean m_fAuthAns = false;
    public boolean m_fTruncated = false;
    public boolean m_fRecurse = true;
    public boolean m_fRecursionAvail = false;
    private int m_iQryType = TYPE_A;
    private int m_iQryClass = CLASS_INTERNET;
    private String m_strQryName = null;
    // return codes
    public int m_iRCode = 0;
    public final static int RCODE_SUCCESS = 0;
    public final static int RCODE_NAME_ERROR = 0x0003;
    public int m_cQuestions = 1;
    public int m_cAnswerRRs = 0;
    public int m_cAuthRRs = 0;
    public int m_cInfoRRs = 0;
    DNSResourceRecord [] m_arrAns;
    DNSResourceRecord [] m_arrAuth;
    DNSResourceRecord [] m_arrInfo;
    public static String GetTypeDesc(int iType) {
        String str = null;
        switch (iType) {
        case TYPE_A: { str = new String("TYPE_A"); } break;
        case TYPE_NS: { str = new String("TYPE_NS"); } break;
        case TYPE_CNAME: { str = new String("TYPE_CNAME"); } break;
        case TYPE_SOA: { str = new String("TYPE_SOA"); } break;
        case TYPE_PTR: { str = new String("TYPE_PTR"); } break;
        case TYPE_HINFO: { str = new String("TYPE_HINFO"); } break;
        case TYPE_MX: { str = new String("TYPE_MX"); } break;
        case TYPE_AXFR: { str = new String("TYPE_AXFR"); } break;
        case TYPE_ANY: { str = new String("TYPE_ANY"); } break;
        default: { str = new String(Integer.toString(iType)); };
        }
        return str;
    }
    public DNSResourceRecord[] getDNSAnswerRecords() {
        return m_arrAns;
    }
    public DNSResourceRecord[] getDNSAuthorityRecords() {
        return m_arrAuth;
    }
    public static String GetClassDesc(int iClass) {
        String str = null;
        switch (iClass) {
        case CLASS_INTERNET: { str = new String("CLASS_INTERNET"); } break;
        default: { str = new String(); };
        }
        return str;
    }
    private int BuildFlags() {
        int iFlags = 0;
        iFlags |= m_iOpCode;
        if (m_fRecurse) {
            iFlags |= RD_MASK;
        }
        return iFlags;
    }
    private boolean SetFlags(int iFlags) {
        m_fQueryResult = (iFlags & QR_MASK) == QR_MASK;
        m_fAuthAns = (iFlags & AA_MASK) == AA_MASK;
        m_fTruncated = (iFlags & TC_MASK) == TC_MASK;
        m_fRecurse = (iFlags & RD_MASK) == RD_MASK;
        m_fRecursionAvail = (iFlags & RA_MASK) == RA_MASK;
        m_iRCode = iFlags & RCODE_MASK;
        return m_iRCode == RCODE_SUCCESS;
    }
    public boolean SetQuery(int iType, int iClass, String strName) {
        m_iQryType = iType;
        m_iQryClass = iClass;
        m_cQuestions = 1;
        m_strQryName = new String(strName);
        return true;
    }
    private boolean WriteFQDN(DataOutputStream dos, String strName) {
        try {
            int iPos = 0;
            int iSep = 0;
            while ((iSep = strName.indexOf('.', iPos)) >= 0) {
                dos.writeByte((byte)(iSep - iPos));
                dos.writeBytes(strName.substring(iPos, iSep));
                iPos = iSep + 1;
            }
            if (iPos < strName.length()) {
                dos.writeByte((byte)(strName.length() - iPos));
                dos.writeBytes(strName.substring(iPos));
            }
            // terminator
            dos.writeByte(0);
            return true;
        } catch (IOException ioe) {
            return false;
        }
    }
    public static String ReadLabelList(DataInputStream dis, byte [] abData) {
        int cb;
         byte [] ab = new byte[MAX_LABEL];
        String str = new String();
        String strNext = new String();
        try {
            while ((cb = dis.readUnsignedByte()) > 0) {
                if ((cb & 0xc0) == 0xc0) {
                    int iReadOff = (int)(cb & ~0xc0) << 8;
                    cb = dis.readUnsignedByte();
                    iReadOff += (int)cb;
                    while (iReadOff < abData.length && ((cb = abData[iReadOff++]) > 0)) {
                        if ((cb & 0xc0) == 0xc0) {
                            return str;
                        }
                        strNext = new String(abData, 0, iReadOff, cb);
                        if (str.length() > 0) {
                            str += "." + strNext;
                        } else {
                            str = strNext;
                        }
                        iReadOff += cb;
                    }
                    return str;
                } else {
                    byte [] abNext = new byte[cb];
                    if (dis.read(abNext) < cb) {
                        return new String();
                    }
                    strNext = new String(abNext, 0, 0, cb);
                }
                if (str.length() > 0) {
                    str += "." + strNext;
                } else {
                    str = strNext;
                }
            }
        } catch (IOException ioe) {
            System.err.println("exception: " + ioe.getMessage());
        }
        return str;
    }
    public boolean WriteQuery(OutputStream os) {
        try {
            if (m_strQryName == null) {
                return false;
            }
            DataOutputStream dos = new DataOutputStream(os);
            // query sequence ID
            dos.writeShort(m_idQuery);
            // query flags
            dos.writeShort(BuildFlags());
            // question count
            dos.writeShort(1);
            // answer count
            dos.writeShort(0);
            // authority count
            dos.writeShort(0);
            // additional info count
            dos.writeShort(0);
            // write query name
            WriteFQDN(dos, m_strQryName);
            // query type
            dos.writeShort(m_iQryType);
            // query class
            dos.writeShort(m_iQryClass);
            return true;
        } catch (IOException ioe) {
            return false;
        }
    }
    private void SortRRs(DNSResourceRecord [] arr, boolean fDescending) {
        if (arr == null || arr.length < 2) {
            return;
        }
        boolean fSwapped;
        DNSResourceRecord rrSwap;
        do {
            fSwapped = false;
            for (int i = 0; i < arr.length - 1; i++) {
                boolean fSwap = false;
                if (arr[i+1].m_iType < arr[i].m_iType) {
                    fSwap = true;
                } else if (arr[i+1].m_iType == arr[i].m_iType) {
                    switch (arr[i].m_iType) {
                    case TYPE_MX: {
                        fSwap = arr[i+1].m_lData < arr[i].m_lData;
                    } break;
                    }
                }
                if (fSwap = fSwap ^ fDescending) {
                    rrSwap = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = rrSwap;
                    fSwapped = true;
                }
            }
        } while (fSwapped);
    }
    public boolean ReadQuery(byte [] abData, int cbData) {
        try {
            ByteArrayInputStream is = new ByteArrayInputStream(abData, 0, cbData);
            DataInputStream dis = new DataInputStream(is);
            // query sequence ID
            m_idQuery = dis.readShort();
            // query flags
            if (!SetFlags(dis.readShort())) {
                return false;
            }
            // question count
            m_cQuestions = dis.readShort();
            // answer count
            m_cAnswerRRs = dis.readShort();
            // authority count
            m_cAuthRRs = dis.readShort();
            // additional info count
            m_cInfoRRs = dis.readShort();
            // read query name
            m_strQryName = ReadLabelList(dis, abData);
            // query type
            m_iQryType = dis.readShort();
            // query class
            m_iQryClass = dis.readShort();
            m_arrAns = new DNSResourceRecord[m_cAnswerRRs];
            for (int i = 0; i < m_cAnswerRRs; i++) {
                m_arrAns[i] = new DNSResourceRecord();
                if (!m_arrAns[i].readRecord(dis, abData)) {
                    return false;
                }
            }
            SortRRs(m_arrAns, false);
            if (m_cAuthRRs > 0) {
                m_arrAuth = new DNSResourceRecord[m_cAuthRRs];
                for (int i = 0; i < m_cAuthRRs; i++) {
                    m_arrAuth[i] = new DNSResourceRecord();
                    if (!m_arrAuth[i].readRecord(dis, abData)) {
                        return false;
                    }
                }
                SortRRs(m_arrAuth, false);
            } else {
                m_arrAuth = null;
            }
            if (m_cInfoRRs > 0) {
                m_arrInfo = new DNSResourceRecord[m_cInfoRRs];
                for (int i = 0; i < m_cInfoRRs; i++) {
                    m_arrInfo[i] = new DNSResourceRecord();
                    if (!m_arrInfo[i].readRecord(dis, abData)) {
                        return false;
                    }
                }
                SortRRs(m_arrInfo, false);
            } else {
                m_arrInfo = null;
            }
        } catch (IOException ioe) {
            System.err.println("exception: " + ioe.getMessage());
            return false;
        }
        return true;
    }
}
hsarback/src/de/hsadmin/core/util/dns/DNSResourceRecord.java
New file
@@ -0,0 +1,190 @@
package de.hsadmin.core.util.dns;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
public class DNSResourceRecord {
    public String m_strDomainName;
    public int m_iType;
    public int m_iClass;
    public Date m_dtExpire = new Date();
    public int m_cbData;
    public byte [] m_abData = null;
    // these fields are interpreted differently depending on m_iType
    // (can you say union?)
    public long m_lData;
    public long m_lData1;
    public long m_lData2;
    public long m_lData3;
    public long m_lData4;
    public long m_lData5;
    public String m_strData;
    public String m_strData1;
    public boolean readRecord(DataInputStream dis, byte [] abData) {
        try {
            m_strDomainName = DNSQuery.ReadLabelList(dis, abData);
            m_iType = dis.readShort();
            m_iClass = dis.readShort();
            // time from DNS is in seconds, need milliseconds
            long lSecondsToLive = dis.readInt();
            Date dtNow = new Date();
            m_dtExpire.setTime(dtNow.getTime() + (lSecondsToLive * 1000L));
            m_cbData = dis.readShort();
            m_abData = null;
            switch (m_iType) {
            case DNSQuery.TYPE_A: {
                m_lData = dis.readInt();
            } break;
            case DNSQuery.TYPE_MX: {
                m_lData = dis.readShort();
                m_strData = DNSQuery.ReadLabelList(dis, abData);
            } break;
            case DNSQuery.TYPE_NS:
            case DNSQuery.TYPE_MD:
            case DNSQuery.TYPE_MF:
            case DNSQuery.TYPE_CNAME:
            case DNSQuery.TYPE_MB:
            case DNSQuery.TYPE_MG:
            case DNSQuery.TYPE_MR:
            case DNSQuery.TYPE_PTR: {
                m_strData = DNSQuery.ReadLabelList(dis, abData);
            } break;
            case DNSQuery.TYPE_SOA: {
                // !!!LATER!!! I had a real problem getting this code to work.
                // I think the spec I had was old RFC 883
                m_strData = DNSQuery.ReadLabelList(dis, abData);
                m_strData1 = DNSQuery.ReadLabelList(dis, abData);
                // SERIAL
                m_lData = dis.readUnsignedShort();
                // REFRESH
                m_lData1 = dis.readInt() & 0xffffffff;
                // RETRY
                m_lData2 = dis.readInt() & 0xffffffff;
                // EXPIRE
                m_lData3 = dis.readInt() & 0xffffffff;
                // MINIMUM
                m_lData4 = dis.readUnsignedShort();
                // UNKNOWN
                m_lData5 = dis.readInt() & 0xffffffff;
            } break;
            case DNSQuery.TYPE_MINFO:
            case DNSQuery.TYPE_HINFO: {
                m_strData = DNSQuery.ReadLabelList(dis, abData);
                m_strData1 = DNSQuery.ReadLabelList(dis, abData);
            } break;
            default: {
                m_abData = new byte[m_cbData];
                dis.read(m_abData);
            } break;
            }
        } catch (IOException ioe) {
            System.err.println("exception: " + ioe.getMessage());
        }
        return true;
    }
    public String getMXServer() {
        if (m_iType != DNSQuery.TYPE_MX) {
            return new String();
        }
        return new String(m_strData);
    }
    public int getMXPref() {
        if (m_iType != DNSQuery.TYPE_MX) {
            return -1;
        }
        return (int)m_lData;
    }
    public void dumpRecord(PrintStream ps) {
        ps.println("Domain: " + m_strDomainName);
        ps.println("Type: " + DNSQuery.GetTypeDesc(m_iType));
        ps.println("Class: " + DNSQuery.GetClassDesc(m_iClass));
        ps.println("Expires: " + m_dtExpire.toString());
        switch (m_iType) {
        case DNSQuery.TYPE_A: {
            ps.println("IP Address: " + Long.toHexString(m_lData));
        } break;
        case DNSQuery.TYPE_MX: {
            ps.println("MX Server: " + m_strData);
            ps.println("MX Pref: " + Long.toString(m_lData));
        } break;
        case DNSQuery.TYPE_NS:
        case DNSQuery.TYPE_MD:
        case DNSQuery.TYPE_MF:
        case DNSQuery.TYPE_CNAME:
        case DNSQuery.TYPE_MB:
        case DNSQuery.TYPE_MG:
        case DNSQuery.TYPE_MR:
        case DNSQuery.TYPE_PTR: {
            ps.println("Domain: " + m_strData);
        } break;
        case DNSQuery.TYPE_HINFO: {
            ps.println("CPU: " + m_strData);
            ps.println("OS: " + m_strData1);
        } break;
        case DNSQuery.TYPE_SOA: {
            ps.println("MNAME: " + m_strData);
            ps.println("RNAME: " + m_strData1);
            ps.println("SERIAL: " + Long.toString(m_lData));
            ps.println("REFRESH: " + Long.toString(m_lData1));
            ps.println("RETRY: " + Long.toString(m_lData2));
            ps.println("EXPIRE: " + Long.toString(m_lData3));
            ps.println("MINIMUM: " + Long.toString(m_lData4));
            ps.println("UNKNOWN: " + Long.toString(m_lData4));
        } break;
        default: {
            ps.println("Data: " + new String(m_abData));
            dumpBytes(ps, m_abData);
        }
        }
    }
    private void dumpBytes(PrintStream ps, byte [] ab) {
        int i;
        String strTemp;
        for (i = 0; i < ab.length; i++) {
            strTemp = Integer.toHexString(ab[i]);
            if (strTemp.length() < 2) {
                strTemp = "0" + strTemp;
            }
            ps.print(strTemp + " ");
            if (i > 0 && ((i % 8) == 0 || i == ab.length-1)) {
                ps.println();
            }
        }
    }
}
hsarback/src/de/hsadmin/core/util/dns/DNSService.java
New file
@@ -0,0 +1,80 @@
package de.hsadmin.core.util.dns;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public class DNSService {
    public static int DNS_SOCKET = 53;
    private static InetAddress m_iaDNS = null;
    private static Socket m_sockService = null;
    private static byte[] m_abReceive = new byte[512];
    private static int m_iQuerySerial = 0x1234;
    public static void SetDNSAddress(InetAddress iaDNS) {
        m_iaDNS = iaDNS;
        if (m_sockService == null) {
            try {
                m_sockService = new Socket(m_iaDNS, DNS_SOCKET, false);
            } catch (IOException ioe) {
                System.err.println("exception: " + ioe.getMessage());
            }
        }
    }
    public static boolean PerformDNSQuery(DNSQuery dns) {
        if (m_iaDNS == null || dns == null) {
            return false;
        }
        ByteArrayOutputStream bas = new ByteArrayOutputStream();
        dns.WriteQuery(bas);
        try {
            m_sockService.getOutputStream().write(bas.toByteArray());
        } catch (IOException ioe) {
            return false;
        }
        try {
            BufferedInputStream bis = new BufferedInputStream(m_sockService.getInputStream(), 512);
            int cRetry = 5;
            int cbAvail = 0;
            while (cRetry-- > 0 && ((cbAvail = bis.available()) <= 0)) {
                try {
                    Thread.currentThread().sleep(200);
                } catch (InterruptedException ie) {
                    System.err.println("exception: " + ie.getMessage());
                }
            }
            if (cbAvail > 0) {
                int cbRead = bis.read(m_abReceive, 0, cbAvail);
                m_iQuerySerial++;
                if (cbRead > 0) {
                    dns.ReadQuery(m_abReceive, cbRead);
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } catch (IOException ioe) {
            return false;
        }
        return true;
    }
}
hsarback/src/de/hsadmin/mods/cust/Customer.java
@@ -20,6 +20,7 @@
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;
import de.hsadmin.mods.pac.Pac;
import de.hsadmin.mods.user.UnixUser;
@@ -40,6 +41,9 @@
    @Column(name = "member_code", columnDefinition = "character varying(20)")
    private String name;
    @Transient
    private String password;
    @Column(name = "member_since", columnDefinition = "date", nullable = true)
    private Date memberSince;
@@ -269,4 +273,12 @@
    public static String restriction() {
        return "obj.name=:loginUserName";
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getPassword() {
        return password;
    }
}
hsarback/src/de/hsadmin/mods/cust/CustomerModuleImpl.java
@@ -1,7 +1,74 @@
package de.hsadmin.mods.cust;
import java.util.List;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.AbstractModuleImpl;
import de.hsadmin.core.model.GenericModuleImpl;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.util.TextUtil;
import de.hsadmin.mods.user.UnixUser;
public class CustomerModuleImpl extends AbstractModuleImpl {
    @Override
    public AbstractEntity add(AbstractEntity newEntity) throws HSAdminException {
        if (!getLoginUser().hasHostmasterRole()) {
            throw new HSAdminException("role hostmaster required to create new customer");
        }
        Customer newCustomer = (Customer) newEntity;
        assertNotNull("membercode", newCustomer.getName());
        assertNotNull("password", newCustomer.getPassword());
        Contact contact = newCustomer.getContacts().iterator().next();
        assertNotNull("contact_lastname", contact.getLastName());
        String custComment = contact.getLastName();
        if (contact.getFirstName() != null && contact.getFirstName().length() > 0) {
            custComment = contact.getFirstName() + " " + contact.getLastName();
        }
        GenericModuleImpl helperModule = new GenericModuleImpl(getTransaction());
        UnixUser custAccount = new UnixUser();
        custAccount.setComment(TextUtil.replaceUmlauts(custComment));
        custAccount.setName(newCustomer.getName());
        custAccount.setPassword(newCustomer.getPassword());
        custAccount.setShell("/usr/bin/passwd");
        custAccount.setQuotaSoftlimit(8);
        custAccount.setQuotaHardlimit(12);
        helperModule.add(custAccount);
        return super.add(newEntity);
    }
    @Override
    public List<AbstractEntity> search(
            Class<? extends AbstractEntity> entityClass, String condition,
            String orderBy) throws HSAdminException {
        return super.search(entityClass, condition, orderBy);
    }
    @Override
    public AbstractEntity update(AbstractEntity existingEntity)
            throws HSAdminException {
        if (!getLoginUser().hasHostmasterRole()) {
            throw new HSAdminException("role hostmaster required to update customers");
        }
        return super.update(existingEntity);
    }
    @Override
    public void delete(AbstractEntity existingEntity) throws HSAdminException {
        if (!getLoginUser().hasHostmasterRole()) {
            throw new HSAdminException("role hostmaster required to delete customers");
        }
        Customer cust = (Customer) existingEntity;
        GenericModuleImpl helper = new GenericModuleImpl(getTransaction());
        AbstractEntity custAccount = helper.findByString(UnixUser.class, cust.getName());
        helper.delete(custAccount);
        super.delete(existingEntity);
    }
    private void assertNotNull(String name, String value) throws HSAdminException {
        if (value == null || value.length() < 1) {
            throw new HSAdminException("field '" + name + "' is mandatory");
        }
    }
}
hsarback/src/de/hsadmin/mods/dom/Domain.java
New file
@@ -0,0 +1,232 @@
package de.hsadmin.mods.dom;
import static javax.persistence.FetchType.EAGER;
import static javax.persistence.GenerationType.SEQUENCE;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.mods.user.UnixUser;
@javax.persistence.Entity(name = "Domains")
@Table(name = "domain")
@SequenceGenerator(name = "DomainsSeqGen", sequenceName = "domain_domain_id_seq")
public class Domain extends AbstractEntity {
    private static final long serialVersionUID = -7496110900868795840L;
    @javax.persistence.Id
    @javax.persistence.Column(name = "domain_id", columnDefinition = "integer")
    @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "DomainsSeqGen")
    private long id;
    @javax.persistence.Column(name = "domain_name", columnDefinition = "character varying(256)", nullable = false)
    private String name;
    @JoinColumn(name = "domain_owner", columnDefinition = "integer", nullable = false)
    @ManyToOne(fetch = EAGER)
    private UnixUser user;
    @Column(name = "domain_status", columnDefinition = "character varying(12)", nullable = false)
    private String status;
    @Column(name = "domain_status_changed", columnDefinition = "date", nullable = false)
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date statusChanged;
    @Column(name = "domain_filed", columnDefinition = "date")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date filed;
    @Column(name = "domain_since", columnDefinition = "date")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date since;
    @Column(name = "domain_until", columnDefinition = "date")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date until;
    @Column(name = "domain_dns_master", columnDefinition = "character varying(64)")
    private String dnsMaster;
    public Domain() {
    }
    public Domain(String name, UnixUser user) {
        this.name = name;
        this.user = user;
        this.statusChanged = new java.util.Date();
        this.status = Status.FILED.toString();
    }
    public static String createQueryFromStringKey(String humanKey) {
        return "name = '" + humanKey + "'";
    }
    @Override
    public String createStringKey() {
        return getName();
    }
    @Override
    public long id() {
        return id;
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public UnixUser getUser() {
        return user;
    }
    public void setUser(UnixUser user) {
        this.user = user;
    }
    @javax.persistence.Transient
    public Status getStatus() {
        return Status.valueFor(status);
    }
    public void setStatus(Status status) {
        this.status = status.toString();
    }
    public Date getStatusChanged() {
        return statusChanged;
    }
    public void setStatusChanged(Date statusChanged) {
        this.statusChanged = statusChanged;
    }
    public Date getFiled() {
        return filed;
    }
    public void setFiled(Date filed) {
        this.filed = filed;
    }
    public Date getSince() {
        return since;
    }
    public void setSince(Date since) {
        this.since = since;
    }
    public Date getUntil() {
        return until;
    }
    public void setUntil(Date until) {
        this.until = until;
    }
    public String getDnsMaster() {
        return dnsMaster;
    }
    public void setDnsMaster(String dnsMaster) {
        this.dnsMaster = dnsMaster;
    }
    @Override
    public boolean isNew() {
        return id == 0;
    }
    public boolean isReadAllowedFor(UnixUser loginUser) {
        return loginUser.hasHostmasterRole() || // hostmaster
                user.getId() == loginUser.getId() || // dom-admin
                user.getPac().getName().equals(loginUser.getName()) || // pac-admin
                user.getPac().getCustomer().getName().equals(loginUser.getName()); // customer
    }
    public boolean isWriteAllowedFor(UnixUser loginUser) {
        return isReadAllowedFor(loginUser);
    }
    @Override
    public String getHiveName() {
        if (isNew())
            return null;
        else
            return getUser().getHiveName();
    }
    @Override
    public UnixUser owningUser(EntityManager em) {
        return user;
    }
    public static String restriction() {
        return
            // all doms of all pacs of customer
            "obj.user.pac.customer.name=:loginUserName OR " +
            // all doms of packet admin
            "obj.user.pac.name=:loginUserName OR " +
            // all domains of the user itself
            "obj.user=:loginUser";
    }
    public static enum Status {
        NONE(""),
        ERROR("error"),
        FILED("filed"),
        SELF("self"),
        REGISTERING("registering"),
        REGISTERED("registered"),
        TRANSFERRING("transferring"),
        NACK_RECEIVED("KK-NACK"),
        ACK_RECEIVED("KK-ACK"),
        MANAGED("connected"),
        LEAVE_SIGNALLED("KK-sig"),
        LEAVING("leaving"),
        GONE("gone"),
        CLOSE_REQUESTED("CLO-req"),
        CLOSING("closing"),
        CLOSED("closed");
        private Status(String aValue) {
            this.aValue = aValue;
        }
        public String toString() {
            return aValue;
        }
        private String aValue;
        public static Status valueFor(String str) {
            for (Status val : Status.values())
                if (val.toString().equals(str))
                    return val;
            return null;
        }
    }
}
hsarback/src/de/hsadmin/mods/dom/DomainModuleImpl.java
New file
@@ -0,0 +1,198 @@
package de.hsadmin.mods.dom;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import de.hsadmin.core.model.AbstractModuleImpl;
import de.hsadmin.core.model.AuthorisationException;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.util.DNSCheck;
import de.hsadmin.mods.dom.Domain.Status;
import de.hsadmin.mods.email.EMailAddress;
import de.hsadmin.mods.pac.Pac;
import de.hsadmin.mods.user.UnixUser;
public class DomainModuleImpl extends AbstractModuleImpl {
    private static final long serialVersionUID = -3991273357778898175L;
    @Override
    public AbstractEntity initialize(AbstractEntity newEntity) throws AuthorisationException {
        AbstractEntity newDom = super.initialize(newEntity);
        if (newDom instanceof Domain) {
            ((Domain) newDom).setUser(getLoginUser());
            return newDom;
        }
        return null;
    }
    @Override
    public AbstractEntity find(Class<? extends AbstractEntity> entityClass, Object key) throws HSAdminException {
        // do query
        AbstractEntity res = super.find(entityClass, key);
        // check access rights
        needsReadAccessOn(res, "find");
        // return clean result
        return res;
    }
    @Override
    public AbstractEntity findByString(Class<? extends AbstractEntity> entityClass, String key)
            throws HSAdminException {
        // do query
        AbstractEntity res = super.findByString(entityClass, key);
        // check access rights
        if (res != null)
            needsReadAccessOn(res, "findByString");
        // return clean result
        return res;
    }
    @Override
    public List<AbstractEntity> search(Class<? extends AbstractEntity> entityClass, String condition, String orderBy) throws HSAdminException {
        // do query
        if (orderBy == null || orderBy.length() == 0) {
            orderBy = "ORDER BY obj.name ASC";
        }
        List<AbstractEntity> res = super.search(entityClass, condition, orderBy);
        List<AbstractEntity> ret = new LinkedList<AbstractEntity>();
        // remove entities where login user has no access rights
        for (AbstractEntity entity : res) {
            try {
                needsReadAccessOn(entity, "search");
                ret.add(entity);
            } catch (AuthorisationException exc) {
            } // ignore
        }
        // return clean result
        return ret;
    }
    @Override
    public AbstractEntity add(AbstractEntity newEntity) throws HSAdminException {
        Domain dom = (Domain) newEntity;
        Date now = new Date();
        dom.setFiled(now);
        dom.setStatus(Status.SELF);
        dom.setStatusChanged(now);
        dom.setDnsMaster("dns1.hostsharing.net");
        if (dom.getName() == null || dom.getName().length() == 0) {
            throw new HSAdminException("domain name required");
        }
        UnixUser admin = dom.getUser();
        if (admin == null || admin.getName() == null || admin.getName().length() == 0) {
            throw new HSAdminException("domain admin required");
        }
        EntityManager em = getTransaction().getEntityManager();
        // search for domains superior to dom
        Query domainQuery = em.createQuery("SELECT d FROM Domains d WHERE d.name = :domainName");
        String superior = dom.getName();
        while (superior.contains(".")) {
            superior = superior.substring(superior.indexOf('.') + 1);
            domainQuery.setParameter("domainName", superior);
            if (domainQuery.getResultList().size() > 0) {
                DNSCheck dnsCheck = new DNSCheck(dom.getDnsMaster());
                if (dnsCheck.checkDomain(dom.getName())) {
                    break;
                } else {
                    throw new HSAdminException("domain " + dom.getName() + " is not delegated to " + dom.getDnsMaster());
                }
            }
        }
        Query adminQuery = em.createQuery("SELECT u FROM UnixUsers u WHERE u.name = :adminName");
        adminQuery.setParameter("adminName", admin.getName());
        dom.setUser((UnixUser) adminQuery.getSingleResult());
        needsWriteAccessOn(newEntity, "add");
//        Entity addedEntity = super.add(dom);
//        Domain addedDomain = (Domain) addedEntity;
        em.persist(dom);
        String[] emailAddresses = new String[] { "abuse", "postmaster", "webmaster" };
        for (String emailLocalpart : emailAddresses) {
            EMailAddress eMailAddress = new EMailAddress(emailLocalpart, "", dom, admin.getName());
            em.persist(eMailAddress);
        }
        return super.add(dom);
    }
    @Override
    public AbstractEntity update(AbstractEntity existingEntity) throws HSAdminException {
        Domain dom = (Domain) existingEntity;
        if (dom.getName() == null || dom.getName().length() == 0) {
            throw new HSAdminException("domain name required");
        }
        UnixUser admin = dom.getUser();
        if (admin == null || admin.getName() == null || admin.getName().length() == 0) {
            throw new HSAdminException("domain admin required");
        }
        if (admin.getPac() == null) {
            EntityManager em = getTransaction().getEntityManager();
            Query query = em.createQuery("SELECT u FROM UnixUsers u WHERE u.name = :adminName");
            query.setParameter("adminName", admin.getName());
            dom.setUser((UnixUser) query.getSingleResult());
        }
        needsWriteAccessOn(existingEntity, "update");
        throw new AuthorisationException(getLoginUser(), "update", existingEntity);
    }
    @Override
    public void delete(AbstractEntity existingEntity) throws HSAdminException {
        needsWriteAccessOn(existingEntity, "delete");
        Domain dom = (Domain) existingEntity;
        EntityManager em = getTransaction().getEntityManager();
        Query query = em.createQuery("SELECT adr FROM " + EMailAddress.class.getAnnotation(javax.persistence.Entity.class).name()
                + " adr WHERE adr.domain.name='" + dom.getName() + "'");
        List<?> resultList = query.getResultList();
        for (Object obj : resultList) {
            EMailAddress eMailAddress = (EMailAddress) obj;
            em.remove(eMailAddress);
        }
        super.delete(existingEntity);
    }
    private void needsReadAccessOn(AbstractEntity ent, String method) throws AuthorisationException {
        if (ent instanceof Domain) {
            Domain dom = (Domain) ent;
            String aLoginUserName = getLoginUser().getName();
            UnixUser domUser = dom.getUser();
            Pac domPac = domUser.getPac();
            boolean isDomAdmin = aLoginUserName.equals(domUser.getName());
            boolean isPacAdmin = aLoginUserName.equals(domPac.getName());
            boolean isCustomer = aLoginUserName.equals(domPac.getCustomer().getName());
            boolean isHostmaster = getLoginUser().hasHostmasterRole();
            if (!isDomAdmin && !isPacAdmin && !isCustomer && !isHostmaster) {
                throw new AuthorisationException(getLoginUser(), method, dom);
            }
        } else {
            throw new AuthorisationException(getLoginUser(), method, ent);
        }
    }
    private void needsWriteAccessOn(AbstractEntity ent, String method) throws AuthorisationException {
        if (ent instanceof Domain) {
            Domain dom = (Domain) ent;
            String aLoginUserName = getLoginUser().getName();
            UnixUser domUser = dom.getUser();
            Pac domPac = domUser.getPac();
            boolean isPacAdmin = aLoginUserName.equals(domPac.getName());
            boolean isCustomer = aLoginUserName.equals(domPac.getCustomer().getName());
            if (!isPacAdmin && !isCustomer)
                throw new AuthorisationException(getLoginUser(), method, dom);
        } else {
            throw new AuthorisationException(getLoginUser(), method, ent);
        }
    }
}
hsarback/src/de/hsadmin/mods/dom/DomainProcessorFactory.java
New file
@@ -0,0 +1,207 @@
package de.hsadmin.mods.dom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.qserv.CompoundProcessor;
import de.hsadmin.core.qserv.CopyFileProcessor;
import de.hsadmin.core.qserv.CreateFileProcessor;
import de.hsadmin.core.qserv.EntityProcessorFactory;
import de.hsadmin.core.qserv.Processor;
import de.hsadmin.core.qserv.ProcessorException;
import de.hsadmin.core.qserv.ShellProcessor;
import de.hsadmin.core.qserv.TemplateProcessor;
import de.hsadmin.core.qserv.WaitingTasksProcessor;
import de.hsadmin.mods.email.EMailAddress;
import de.hsadmin.mods.email.EMailAddressProcessorFactory;
import de.hsadmin.mods.pac.Pac;
import de.hsadmin.mods.user.UnixUser;
public class DomainProcessorFactory implements EntityProcessorFactory {
    /**
     * @return a Processor which creates a domain
     * @throws ProcessorException
     */
    public <T extends AbstractEntity> Processor createCreateProcessor(EntityManager em, T entity) throws ProcessorException {
        String hiveName = entity.getHiveName();
        Domain dom = (Domain) entity;
        UnixUser domUser = dom.getUser();
        Pac pac = domUser.getPac();
        String domName = dom.getName();
        String zonefileTargetPath = "/etc/bind/pri." + domName;
        Map<String, String> templateVars = new HashMap<String, String>();
        templateVars.put("SIO", Long.toString(System.currentTimeMillis()/1000L));
        templateVars.put("DOM_HOSTNAME", domName);
        templateVars.put("PAC_HOSTNAME", pac.getName() + ".hostsharing.net");
        templateVars.put("DOM_IPNUMBER", pac.getCurINetAddr().getInetAddr());
        Processor zonefileTemplateProcessor =
            new TemplateProcessor("/de/hsadmin/mods/dom/zonefile.jtpl", templateVars, zonefileTargetPath, false);
        Processor zonefileACLProcessor =
            new ShellProcessor("chown root:bind " + zonefileTargetPath + " && chmod 644 " + zonefileTargetPath);
        Processor prizonesFileProcessor =
            new ShellProcessor("echo 'zone \"" + domName + "\" { type master; file \"pri." + domName + "\"; };' >>/etc/bind/named.pri-zones" +
                " && sort /etc/bind/named.pri-zones | uniq >/etc/bind/named.pri-zones.tmp" +
                " && mv /etc/bind/named.pri-zones.tmp /etc/bind/named.pri-zones");
        // zone "zweisprech.de" { type slave; file "sec.zweisprech.de"; masters { 83.223.95.31; }; };
        String hiveInetAddr = pac.getHive().getInetAddr().getInetAddr();
        Processor smtpTransportsProcessor =
            new ShellProcessor("echo " + domName + " >> /etc/postfix/relaydomains" +
                    " && invoke-rc.d postfix reload");
        Processor dnsReloadProcessor =
            new ShellProcessor("invoke-rc.d bind9 reload");
        EMailAddressProcessorFactory eMailAddressProcessorFactory = new EMailAddressProcessorFactory();
        Processor dnsSetupProcessor =
            new CompoundProcessor(zonefileTemplateProcessor, zonefileACLProcessor, prizonesFileProcessor, dnsReloadProcessor);
        WaitingTasksProcessor mainProcessor = new WaitingTasksProcessor(dnsSetupProcessor);
        CompoundProcessor emailAdrProcessor = new CompoundProcessor();
        Query query = em.createQuery("SELECT adr FROM " + EMailAddress.class.getAnnotation(javax.persistence.Entity.class).name()
            + " adr WHERE adr.domain.name='" + domName + "'");
        List<?> resultList = query.getResultList();
        for (Object obj : resultList) {
            EMailAddress eMailAddress = (EMailAddress) obj;
            emailAdrProcessor.appendProcessor(eMailAddressProcessorFactory.createCreateProcessor(em, eMailAddress));
        }
        mainProcessor.appendProcessor(hiveName, emailAdrProcessor, "Setup EMail");
        Processor seczonesFileProcessor = null;
        String[] dnsServerQueues = { "dns1", "dns2", "dns3" };
        for (String queueName : dnsServerQueues) {
            seczonesFileProcessor =
                new ShellProcessor("echo 'zone \"" + domName + "\" { type slave; file \"sec." + domName + "\"; masters { " + hiveInetAddr + "; }; };' >>/etc/bind/named-hsh.conf" +
                    " && sort /etc/bind/named-hsh.conf | uniq >/etc/bind/named-hsh.conf.tmp" +
                    " && mv /etc/bind/named-hsh.conf.tmp /etc/bind/named-hsh.conf" +
                    " && invoke-rc.d bind9 reload");
            mainProcessor.appendProcessor(queueName, seczonesFileProcessor, queueName + ".hostsharing.net");
        }
        Processor relayDomainsProcessor = null;
        String[] mailServerQueues = { "mail1", "mail2", "mail3" };
        for (String queueName : mailServerQueues) {
            relayDomainsProcessor = new ShellProcessor("postmap -r -i /etc/postfix/relaydomains", domName + " anything");
            mainProcessor.appendProcessor(queueName, relayDomainsProcessor, queueName + ".hostsharing.net");
        }
        mainProcessor.appendProcessor("backupmx", smtpTransportsProcessor, "backupmx.hostsharing.net");
        String domsDir = domUser.getHomedir() + "/doms";
        String domainDir = domsDir + "/" + dom.getName();
        String[] subDirs = new String[] { "htdocs", "htdocs-ssl", "subs", "subs/www", "subs-ssl", "subs-ssl/www", "cgi", "fastcgi", "cgi-ssl", "fastcgi-ssl", "etc", "var" };
        String pacName = pac.getName();
        String userName = domUser.getName();
        Processor mkDomainDirProzessor =
            new ShellProcessor(
                    "mkdir --mode=1550 --parents " + domsDir + " && " +
                    "chown httpd:" + pacName + " " + domsDir + " && " +
                    "mkdir --mode=750 " + domainDir + " && " +
                    "chown " + userName + ":httpd " + domainDir
                    );
        CompoundProcessor domDirsProcessor = new CompoundProcessor(mkDomainDirProzessor);
        for (String subDir : subDirs) {
            domDirsProcessor.appendProcessor(new ShellProcessor(
                    "mkdir --mode=755 " + domainDir + "/" + subDir + " && " +
                    "chown " + userName + ":" + pacName +  " " + domainDir + "/" + subDir
                ));
        }
        templateVars = new HashMap<String, String>();
        templateVars.put("DOMAIN", domName);
        templateVars.put("USER_NAME", domUser.getComment());
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/htaccess.jtpl", templateVars, domainDir + "/htdocs/.htaccess", userName, pacName, "644")
            );
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/htaccess.jtpl", templateVars, domainDir + "/htdocs-ssl/.htaccess", userName, pacName, "644")
            );
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/index.html.jtpl", templateVars, domainDir + "/subs/www/index.html", userName, pacName, "644")
            );
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/index.html.jtpl", templateVars, domainDir + "/subs-ssl/www/index.html", userName, pacName, "644")
            );
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/test.cgi.jtpl", templateVars, domainDir + "/cgi/test.cgi", userName, pacName, "755")
            );
        domDirsProcessor.appendProcessor(
                new CreateFileProcessor("/de/hsadmin/mods/dom/test.cgi.jtpl", templateVars, domainDir + "/cgi-ssl/test.cgi", userName, pacName, "755")
            );
        domDirsProcessor.appendProcessor(
                new CopyFileProcessor("/usr/local/src/phpstub/phpstub", domainDir + "/fastcgi/phpstub", userName, pacName, "755")
            );
        domDirsProcessor.appendProcessor(
                new CopyFileProcessor("/usr/local/src/phpstub/phpstub", domainDir + "/fastcgi-ssl/phpstub", userName, pacName, "755")
        );
        domDirsProcessor.appendProcessor(
                new ShellProcessor("ln -s " + domainDir + " /home/doms/ && " +
                        "chown --no-dereference " + userName + ":" + pacName + " /home/doms/" + domName
                         + " && " + "chown " + userName + ":httpd /home/doms/" + domName + "/")
            );
        mainProcessor.appendProcessor(hiveName, domDirsProcessor, "Setup doms/" + domName + "-Directory");
        Processor domSetupProcessor =
            new ShellProcessor("mk-httpd-conf " + pacName + " && invoke-rc.d apache2 reload >/dev/null 2>&1");
        mainProcessor.appendProcessor(hiveName, domSetupProcessor, "Setup Apache VHost");
        return mainProcessor;
    }
    /**
     * @return a Processor which updates a domain
     */
    public <T extends AbstractEntity> Processor createUpdateProcessor(EntityManager em, T entity) {
        return null;
    }
    /**
     * @return a Processor which deletes a domain
     */
    public <T extends AbstractEntity> Processor createDeleteProcessor(EntityManager em, T entity) {
        Domain dom = (Domain) entity;
        String domName = dom.getName();
        ShellProcessor emailAddressRemoveProcessor = new ShellProcessor("for KEY in $(postmap -s /etc/postfix/virtual|grep " + domName + "|cut -f1); do postmap -d $KEY /etc/postfix/virtual; done");
        ShellProcessor dnsRemoveRemoveProcessor =
            new ShellProcessor("grep -v pri." + domName + " /etc/bind/named.pri-zones > /etc/bind/named.pri-zones.tmp" +
                " && mv /etc/bind/named.pri-zones.tmp /etc/bind/named.pri-zones" +
                " && rm /etc/bind/pri." + domName +
                " && invoke-rc.d bind9 reload");
        WaitingTasksProcessor mainProcessor = new WaitingTasksProcessor(new CompoundProcessor(emailAddressRemoveProcessor, dnsRemoveRemoveProcessor));
        Processor seczonesFileProcessor = null;
        String[] dnsQueues = { "dns1", "dns2", "dns3" };
        for (String queueName : dnsQueues) {
            seczonesFileProcessor =
                new ShellProcessor("grep -v sec." + domName + " /etc/bind/named-hsh.conf >/etc/bind/named-hsh.conf.tmp" +
                        " && mv /etc/bind/named-hsh.conf.tmp /etc/bind/named-hsh.conf" +
                        " && rm /var/cache/bind/sec." + domName +
                        " && invoke-rc.d bind9 reload");
            mainProcessor.appendProcessor(queueName, seczonesFileProcessor, queueName + ".hostsharing.net");
        }
        Processor mailQueueProcessor = null;
        String[] mailServerQueues = { "mail1", "mail2", "mail3" };
        for (String queueName : mailServerQueues) {
            mailQueueProcessor = new ShellProcessor("postmap -d '" + domName + "' /etc/postfix/relaydomains");
            mainProcessor.appendProcessor(queueName, mailQueueProcessor, queueName + ".hostsharing.net");
        }
        Processor vhostDelProcessor =
            new ShellProcessor("rm /home/doms/" + domName +
                " && rm -rf " + dom.getUser().getHomedir() + "/doms/" + domName +
                " && rm /etc/apache2/sites-generated/" + domName +
                " && rm /etc/apache2/sites-*/`ls -1 /etc/apache2/sites-enabled/ | egrep \"^[01]+-" + domName + "$\"`" +
                " && invoke-rc.d apache2 reload >/dev/null 2>&1");
        mainProcessor.appendProcessor(dom.getHiveName(), vhostDelProcessor, "remove apache vhost");
        Processor smtpRelayDelProcessor =
            new ShellProcessor("grep -v " + domName + " /etc/postfix/relaydomains > /etc/postfix/relaydomains.tmp" +
                " && mv /etc/postfix/relaydomains.tmp /etc/postfix/relaydomains" +
                " && invoke-rc.d postfix reload");
        mainProcessor.appendProcessor("backupmx", smtpRelayDelProcessor, "backupmx.hostsharing.net");
        return mainProcessor;
    }
}
hsarback/src/de/hsadmin/mods/dom/htaccess.jtpl
New file
@@ -0,0 +1,2 @@
<!-- BEGIN: main -->Redirect permanent / http://www.{DOMAIN}/
<!-- END: main -->
hsarback/src/de/hsadmin/mods/dom/index.html.jtpl
New file
@@ -0,0 +1,22 @@
<!-- BEGIN: main --><html>
<head>
<title>Willkommen bei {DOMAIN}</title>
</head>
<body bgcolor="orange">
<h1>Willkommen bei {DOMAIN}</h1>
<p>Diese neue Website wurde gerade bei der
<a href="http://www.hostsharing.net">Hostsharing eG</a>
f&uuml;r {USER_NAME} eingerichtet. </p>
<p>Der Inhaber der Domain ist bereits per Email unter
<a href="mailto:webmaster(at){DOMAIN}">webmaster(at){DOMAIN}</a>
zu erreichen. </p>
</body>
</html>
<!-- END: main -->
hsarback/src/de/hsadmin/mods/dom/test.cgi.jtpl
New file
@@ -0,0 +1,5 @@
<!-- BEGIN: main -->#!/bin/sh
echo Content-type: text/plain
echo
echo Hello, world
<!-- END: main -->
hsarback/src/de/hsadmin/mods/dom/zonefile.jtpl
New file
@@ -0,0 +1,29 @@
<!-- BEGIN: main -->; default - generated by hsadmin
$TTL 4H
{DOM_HOSTNAME}. IN SOA dns1.hostsharing.net. hostmaster.hostsharing.net. (
        {SIO}     ; serial secs since Jan 1 1970
        6H        ; refresh (>=10000)
        1H        ; retry (>=1800)
        1W        ; expire
        1H        ; minimum
        )
; please read http://www.hostsharing.net/tech/descriptions/domains-zonefile.htm
        IN    NS    dns1.hostsharing.net.
        IN    NS    dns2.hostsharing.net.
        IN    NS    dns3.hostsharing.net.
        IN    MX    30 {PAC_HOSTNAME}.
        IN    MX    80 backupmx.hostsharing.net.
        IN    A    {DOM_IPNUMBER}
www        IN    A    {DOM_IPNUMBER}
ftp        IN    A    {DOM_IPNUMBER}
pop3        IN    A    {DOM_IPNUMBER}
mail        IN    A    {DOM_IPNUMBER}
mysql        IN    A    {DOM_IPNUMBER}
pgsql        IN    A    {DOM_IPNUMBER}
*.{DOM_HOSTNAME}.    IN    A    {DOM_IPNUMBER}
<!-- END: main -->
hsarback/src/de/hsadmin/mods/email/EMailAddress.java
New file
@@ -0,0 +1,208 @@
package de.hsadmin.mods.email;
import static javax.persistence.FetchType.EAGER;
import static javax.persistence.GenerationType.SEQUENCE;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.EntityInfo;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.model.SearchFilter;
import de.hsadmin.mods.dom.Domain;
import de.hsadmin.mods.user.UnixUser;
/**
 * Entity class for email addresses.
 */
@Entity(name = "EMailAddresses")
@Table(name = "emailaddr")
@SequenceGenerator(name = "EMailAddressesSeqGen", sequenceName = "emailaddr_emailaddr_id_seq")
@EntityInfo(name = "E-Mail-Adresse")
@SearchFilter("domain.user = :loginUser OR "
        + "domain.user.pac = :loginUserPac OR "
        + "domain.user.pac.customer.name = :loginUserName")
public class EMailAddress extends AbstractEntity implements Serializable {
    private static final long serialVersionUID = -2265500181746604429L;
    @Id
    @GeneratedValue(strategy = SEQUENCE, generator = "EMailAddressesSeqGen")
    @Column(name = "emailaddr_id", columnDefinition = "integer")
    private long id;
    @Column(name = "localpart", updatable = false, nullable= false)
    private String localpart = "";
    @Column(name = "subdomain")
    private String subdomain;
    @ManyToOne(fetch = EAGER)
    @JoinColumn(name = "domain_id", columnDefinition = "integer", updatable = false)
    private Domain domain;
    @Column(name = "target", nullable= false)
    private String target;
    public EMailAddress() {
    }
    public EMailAddress(String localPart, String subdomain, Domain domain,
            String target) {
        this.localpart = localPart;
        this.subdomain = subdomain;
        this.domain = domain;
        this.target = target;
    }
    public static String createQueryFromStringKey(String humanKey) throws HSAdminException {
        String[] parts = humanKey.split("@", 2);
        if (parts.length != 2) {
            throw new HSAdminException("error in oid: " + humanKey);
        }
        String[] doms = parts[1].split("\\.");
        StringBuilder query = new StringBuilder("localpart='" + parts[0]
                + "' AND ( ( subdomain IS NULL AND domain.name='" + parts[1]
                + "' )");
        for (int subdomLevel = 1; subdomLevel < doms.length - 1; ++subdomLevel) {
            String subdom = "";
            for (int n = 0; n < subdomLevel; ++n)
                subdom += "." + doms[n];
            String domain = "";
            for (int n = subdomLevel; n < doms.length; ++n)
                domain += "." + doms[n];
            query.append(" OR ( subdomain='" + subdom.substring(1)
                    + "' AND domain.name='" + domain.substring(1) + "' )");
        }
        query.append(" )");
        String queryString = query.toString();
        return queryString;
    }
    @Override
    public String createStringKey() {
        String key = getDomain() != null ? (getLocalPart() + "@" + getFullDomain())
                : "?@?";
        return key;
    }
    @Override
    public long id() {
        return id;
    }
    public long getId() {
        return id;
    }
    protected void setId(long id) {
        this.id = id;
    }
    public String getLocalPart() {
        return localpart == null ? "" : localpart;
    }
    public void setLocalPart(String localPart) {
        this.localpart = trimToEmpty(localPart);
    }
    public String getSubdomain() {
        return subdomain == null || subdomain.length() == 0 ? null : subdomain;
    }
    public void setSubdomain(String subdomain) {
        this.subdomain = trimToNull(subdomain);
    }
    public Domain getDomain() {
        return domain;
    }
    public void setDomain(Domain domain) {
        this.domain = domain;
    }
    public String getTarget() {
        return target;
    }
    public void setTarget(String target) {
        this.target = trim(target);
    }
    @Transient
    public String getEMailAddress() {
        return createStringKey();
    }
    /**
     * returns the full domain (subdomain + domain)
     */
    @Transient
    public String getFullDomain() {
        return (getSubdomain() == null ? "" : (getSubdomain() + "."))
                + getDomain().getName();
    }
    public String toString() {
        if (localpart != null && target != null)
            return super.toString() + "{ id=" + id + "; address=" + localpart
                    + subdomain + "; target=" + target + " }";
        else
            return super.toString();
    }
    @Override
    public boolean isNew() {
        return id == 0;
    }
    @Override
    public String getHiveName() {
        if (isNew())
            return null;
        else
            return getDomain().getUser().getHiveName();
    }
    @Override
    public UnixUser owningUser(EntityManager em) {
        return domain.owningUser(em);
    }
    @Override
    public boolean isReadAllowedFor(UnixUser loginUser) {
        return getDomain().isReadAllowedFor(loginUser);
    }
    @Override
    public boolean isWriteAllowedFor(UnixUser loginUser) {
        return getDomain().isWriteAllowedFor(loginUser);
    }
    /**
     * query restriction for this Entity
     */
    public static String restriction() {
        return "( " +
            // domain-owner?
            "obj.domain.user = :loginUser OR " +
            // pac-admin? (TODO: Hostsharing name convention)
            "obj.domain.user.pac.name = :loginUserName OR " +
            // customer
            "obj.domain.user.pac.customer.name = :loginUserName )";
    }
}
hsarback/src/de/hsadmin/mods/email/EMailAddressModuleImpl.java
New file
@@ -0,0 +1,75 @@
package de.hsadmin.mods.email;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import de.hsadmin.core.model.AbstractModuleImpl;
import de.hsadmin.core.model.AuthorisationException;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.mods.dom.Domain;
public class EMailAddressModuleImpl extends AbstractModuleImpl {
    @Override
    public List<AbstractEntity> search(Class<? extends AbstractEntity> entityClass,
            String condition, String orderBy) throws HSAdminException {
        if (orderBy == null || orderBy.length() == 0) {
            orderBy = "ORDER BY obj.domain.name ASC, obj.subdomain ASC, obj.localpart ASC";
        }
        return super.search(entityClass, condition, orderBy);
    }
    @Override
    public AbstractEntity add(AbstractEntity newEntity) throws HSAdminException {
        EntityManager em = getTransaction().getEntityManager();
        EMailAddress adr = (EMailAddress) newEntity;
        if (adr.getTarget() == null || adr.getTarget().length() == 0) {
            throw new HSAdminException("target required");
        }
        if (adr.getLocalPart() == null) {
            adr.setLocalPart("");
        }
        if (adr.getDomain() == null
                || adr.getDomain().getName() == null
                || adr.getDomain().getName().length() == 0) {
            throw new HSAdminException("domain required");
        }
        Query qDomain = em.createQuery("SELECT d FROM Domains d WHERE d.name = :domName");
        qDomain.setParameter("domName", adr.getDomain().getName());
        Domain dom = (Domain) qDomain.getSingleResult();
        adr.setDomain(dom);
        return super.add(newEntity);
    }
    @Override
    public AbstractEntity update(AbstractEntity existingEntity) throws HSAdminException {
        EMailAddress detachedAddr = (EMailAddress) existingEntity;
        EntityManager em = getTransaction().getEntityManager();
        EMailAddress attachedAddr = em.find(EMailAddress.class, detachedAddr.getId());
        Domain domain = detachedAddr.getDomain();
        if (domain != null && domain.getId() != attachedAddr.getDomain().getId()) {
            detachedAddr.setDomain(attachedAddr.getDomain());
            throw new AuthorisationException(getLoginUser(), "update", detachedAddr, "domain");
        }
        String subdomain = detachedAddr.getSubdomain();
        if (subdomain != null && !subdomain.equals(attachedAddr.getSubdomain())) {
            detachedAddr.setSubdomain(attachedAddr.getSubdomain());
            throw new AuthorisationException(getLoginUser(), "update", detachedAddr, "subdomain");
        }
        String localPart = detachedAddr.getLocalPart();
        if (localPart != null && !localPart.equals(attachedAddr.getLocalPart())) {
            detachedAddr.setLocalPart(attachedAddr.getLocalPart());
            throw new AuthorisationException(getLoginUser(), "update", detachedAddr, "localpart");
        }
        String target = detachedAddr.getTarget();
        if (target == null) {
            throw new HSAdminException("target required");
        }
        attachedAddr.setTarget(target);
        return super.update(attachedAddr);
    }
}
hsarback/src/de/hsadmin/mods/email/EMailAddressProcessorFactory.java
New file
@@ -0,0 +1,70 @@
package de.hsadmin.mods.email;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.qserv.CompoundProcessor;
import de.hsadmin.core.qserv.EntityProcessorFactory;
import de.hsadmin.core.qserv.Processor;
import de.hsadmin.core.qserv.ShellProcessor;
/**
 * System level implementation for EMailAdress module.
 */
public class EMailAddressProcessorFactory implements EntityProcessorFactory {
    /**
     * @return a Processor which creates an email address
     */
    public <T extends AbstractEntity> Processor createCreateProcessor(EntityManager em, T entity) {
        // TODO: combine both keys in a single call (optimization)
        EMailAddress email = (EMailAddress) entity;
        CompoundProcessor cp = new CompoundProcessor();
        cp.appendProcessor(new ShellProcessor( "postmap -r -i /etc/postfix/virtual",
                email.getFullDomain() + " -" ) );
        cp.appendProcessor(new ShellProcessor( "postmap -r -i /etc/postfix/virtual",
                email.getEMailAddress() + " " + email.getTarget() ) );
        return cp;
    }
    /**
     * @return a Processor which updates an email address
     */
    public <T extends AbstractEntity> Processor createUpdateProcessor(EntityManager em, T entity) {
        // TODO: if update is specified by primary-key or DB query instead of OID,
        //     a  postmap -d might be neccessary
        return createCreateProcessor(em, entity);
    }
    /**
     * @return a Processor which deletes an email address
     */
    public <T extends AbstractEntity> Processor createDeleteProcessor(EntityManager em, T entity) {
        // TODO: combine both keys in a single call (optimization)
        // remove the entry itself
        CompoundProcessor cp = new CompoundProcessor();
        EMailAddress email = (EMailAddress) entity;
        cp.appendProcessor(
                new ShellProcessor( "postmap -d '" + email.getEMailAddress() + "' /etc/postfix/virtual" ) );
        // any other email addresses for this domain?
        Query query;
        if ( email.getSubdomain() != null ) {
            query = em.createQuery("SELECT e FROM EMailAddresses e WHERE e.subdomain=:subdomain AND e.domain=:domain");
            query.setParameter("subdomain", email.getSubdomain());
        }
        else {
            query = em.createQuery("SELECT e FROM EMailAddresses e WHERE e.subdomain IS NULL AND e.domain=:domain");
        }
        query.setParameter("domain", email.getDomain());
        List<?> result = query.getResultList();
        if ( result == null || result.size() == 0 ) {
            // remove the domain from virtual.db
            cp.appendProcessor(
                    new ShellProcessor( "postmap -d '" + email.getFullDomain() + "' /etc/postfix/virtual" ) );
        }
        return cp;
    }
}
hsarback/src/de/hsadmin/mods/email/EMailAlias.java
New file
@@ -0,0 +1,200 @@
package de.hsadmin.mods.email;
import static javax.persistence.GenerationType.SEQUENCE;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import de.hsadmin.core.model.AbstractModuleImpl;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.EntityInfo;
import de.hsadmin.core.model.SearchFilter;
import de.hsadmin.mods.pac.Pac;
import de.hsadmin.mods.user.UnixUser;
/**
 * Entity class for email aliases.
 *
 */
@javax.persistence.Entity(name = "EMailAliases")
@Table(name = "emailalias")
@SequenceGenerator(name = "EMailAliasesSeqGen", sequenceName = "emailalias_emailalias_id_seq")
@EntityInfo(name = "E-Mail-Alias")
@SearchFilter("pac = :loginUserPac OR "
        + "pac.customer.name = :loginUserName")
public class EMailAlias extends AbstractEntity implements Serializable {
    private static final long serialVersionUID = -4711415079723587161L;
    @Id
    @GeneratedValue(strategy = SEQUENCE, generator = "EMailAliasesSeqGen")
    @Column(name = "emailalias_id", columnDefinition = "integer", insertable=false, updatable=false)
    private long id;
    @ManyToOne()
    @JoinColumn(name = "pac_id", columnDefinition = "integer")
    private Pac pac;
    @Column(updatable=false)
    private String name;
    @Column
    private String target;
    public EMailAlias() {
    }
    public EMailAlias(Pac pac, String name, String target) {
        this.pac = pac;
        this.name = name;
        this.target = target;
    }
    @Override
    public void initialize(EntityManager em, UnixUser loginUser) {
        pac = loginUser.getPac();
        name = pac.getName() + "-";
        target = "";
    }
    @Override
    public void complete(EntityManager em, UnixUser loginUser) {
        if (pac == null && name != null && name.length() > 0) {
            // TODO: it's ugly having this code here, needs refactoring
            String pacName = name.substring(0, 5);
            try {
                // get the entities name (query part from FROM to WHERE)
                javax.persistence.Entity entityAnnot = Pac.class
                        .getAnnotation(javax.persistence.Entity.class);
                String queryString = "FROM " + entityAnnot.name() + " WHERE "
                        + Pac.createQueryFromStringKey(pacName);
                // set parameters
                Query query = em.createQuery(queryString);
                AbstractModuleImpl.setQueryParameter(query, queryString,
                        "loginUser", loginUser);
                AbstractModuleImpl.setQueryParameter(query, queryString,
                        "loginUserName", loginUser.getName());
                AbstractModuleImpl.setQueryParameter(query, queryString,
                        "loginUserPac", loginUser.getPac());
                pac = (Pac) query.getSingleResult();
            } catch (NoResultException exc) {
                throw new SecurityException("packet '" + pacName
                        + "' not found or access denied");
            }
        }
    }
    public static String createQueryFromStringKey(String humanKey) {
        return "name='" + humanKey + "'";
    }
    @Override
    public String createStringKey() {
        return name;
    }
    @Override
    public long id() {
        return id;
    }
    public long getId() {
        return id;
    }
    protected void setId(long id) {
        this.id = id;
    }
    public Pac getPac() {
        return pac;
    }
    public void setPac(Pac pac) {
        this.pac = pac;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getTarget() {
        return target;
    }
    public void setTarget(String target) {
        this.target = target;
    }
    @Override
    public boolean isNew() {
        return id == 0;
    }
    @Override
    public String getHiveName() {
        if (isNew())
            return null;
        else
            return getPac().getHiveName();
    }
    @Override
    public UnixUser owningUser(EntityManager em) {
        return pac.owningUser(em);
    }
    public AbstractEntity merge(EntityManager em, UnixUser loginUser) {
        EMailAlias attachedEntity = em.find(getClass(), id());
        Pac attachedPacket = attachedEntity.getPac();
        if (pac.getId() != attachedPacket.getId())
            throw new SecurityException(
                    "changing the Packet of an EMailAlias is not allowed");
        if (!name.equals(attachedPacket.getName())
                && !name.startsWith(attachedPacket.getName() + "-"))
            throw new SecurityException(
                    "changing the Packet of an EMailAlias is not allowed");
        attachedEntity.setName(name);
        attachedEntity.setTarget(target);
        return attachedEntity;
    }
    @Override
    public boolean isReadAllowedFor(UnixUser loginUser) {
        return loginUser.hasPacAdminRoleFor(getPac());
    }
    @Override
    public boolean isWriteAllowedFor(UnixUser loginUser) {
        String pacName = pac.getName();
        if (!name.equals(pacName) && !name.startsWith(pacName + "-"))
            return false;
        return loginUser.hasPacAdminRoleFor(getPac());
    }
    public static String restriction() {
        return
            // all aliases of all pacs of customer
            "pac.customer.name=:loginUserName OR " +
            // all aliases of packet admin
            "pac.name=:loginUserName";
    }
}
hsarback/src/de/hsadmin/mods/email/EMailAliasModuleImpl.java
New file
@@ -0,0 +1,20 @@
package de.hsadmin.mods.email;
import java.util.List;
import de.hsadmin.core.model.AbstractModuleImpl;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.HSAdminException;
public class EMailAliasModuleImpl extends AbstractModuleImpl {
    @Override
    public List<AbstractEntity> search(Class<? extends AbstractEntity> entityClass,
            String condition, String orderBy) throws HSAdminException {
        if (orderBy == null || orderBy.length() == 0) {
            orderBy = "ORDER BY name ASC";
        }
        return super.search(entityClass, condition, orderBy);
    }
}
hsarback/src/de/hsadmin/mods/email/EMailAliasProcessorFactory.java
New file
@@ -0,0 +1,46 @@
package de.hsadmin.mods.email;
import javax.persistence.EntityManager;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.qserv.EntityProcessorFactory;
import de.hsadmin.core.qserv.Processor;
import de.hsadmin.core.qserv.ShellProcessor;
/**
 * System level implementation for EMailAlias module.
 */
public class EMailAliasProcessorFactory implements EntityProcessorFactory {
    // TODO: execution of any of these processors needs to be synchronized
    /**
     * @return a Processor which creates an email alias
     */
    public <T extends AbstractEntity> Processor createCreateProcessor(EntityManager em,
            T entity) {
        EMailAlias alias = (EMailAlias) entity;
        return new ShellProcessor("postalias -r -i /etc/postfix/aliases",
            alias.getName() + ": " + alias.getTarget());
    }
    /**
     * @return a Processor which updates an email alias
     */
    public <T extends AbstractEntity> Processor createUpdateProcessor(EntityManager em,
            T entity) {
        EMailAlias alias = (EMailAlias) entity;
        return new ShellProcessor("postalias -r -i /etc/postfix/aliases",
            alias.getName() + ": " + alias.getTarget());
    }
    /**
     * @return a Processor which deletes an email alias
     */
    public <T extends AbstractEntity> Processor createDeleteProcessor(EntityManager em,
            T entity) {
        EMailAlias alias = (EMailAlias) entity;
        return new ShellProcessor(
            "postalias -d '" + alias.getName() + "' /etc/postfix/aliases");
    }
}
hsarback/src/de/hsadmin/mods/user/UnixUserModuleImpl.java
@@ -100,9 +100,13 @@
        if (passWord.indexOf(':') >= 0) {
            throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId");
        }
        if (newUnixUser.getPac() == null || newUnixUser.getPac().getNew()) {
        }
        Query qPac = em.createQuery("SELECT obj FROM Pacs obj WHERE obj.name = :pacName");
        qPac.setParameter("pacName", userName.substring(0, 5));
        Pac pac = (Pac) qPac.getSingleResult();
        Object singleResult = qPac.getSingleResult();
        Pac pac = (Pac) singleResult;
        newUnixUser.setName(userName);
        newUnixUser.setHomedir("/home/pacs/" + userName.substring(0, 5) + "/users/" + userName.substring(6));
@@ -136,10 +140,6 @@
        // add new entity
        return super.add(newEntity);
    }
    private EntityManager getEntityManager() {
        return getTransaction().getEntityManager();
    }
    @Override
@@ -218,6 +218,10 @@
        super.delete(attachedUnixUser);
    }
    private EntityManager getEntityManager() {
        return getTransaction().getEntityManager();
    }
    // throws an AuthorisationException if the login user has no write acess
    // on the pac of the given UnixUser
    private boolean hasFullAccessOnPacOf(UnixUser user) {
hsarback/src/de/hsadmin/remote/AbstractRemote.java
@@ -150,7 +150,7 @@
                            "better safe than sorry: no where parameter found");
                }
                List<AbstractEntity> list = module.search(getEntityClass(),
                        queryCondition, "ORDER BY obj.name ASC");
                        queryCondition, getOrderBy());
                transaction.beginTransaction();
                for (AbstractEntity update : list) {
                    if (update.isWriteAllowedFor(unixUser)) {
@@ -178,6 +178,10 @@
        }
    }
    public String getOrderBy() {
        return "ORDER BY obj.name ASC";
    }
    protected boolean assertNotNull(String string) {
        return string != null && string.length() > 0;
    }
hsarback/src/de/hsadmin/remote/CustomerRemote.java
@@ -94,6 +94,10 @@
        if (assertNotNull(memberCode)) {
            cust.setName(memberCode);
        }
        String password = setParams.get("password");
        if (assertNotNull(password)) {
            cust.setPassword(password);
        }
        String memberNo = setParams.get("memberno");
        if (assertNotNull(memberNo)) {
            cust.setMemberNo(Integer.parseInt(memberNo));
hsarback/src/de/hsadmin/remote/DomainRemote.java
New file
@@ -0,0 +1,63 @@
package de.hsadmin.remote;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.mods.dom.Domain;
import de.hsadmin.mods.user.UnixUser;
public class DomainRemote extends AbstractRemote {
    private static final DateFormat df = SimpleDateFormat.getDateInstance(DateFormat.SHORT);
    @Override
    protected Class<? extends AbstractEntity> getEntityClass() {
        return Domain.class;
    }
    @Override
    protected void entity2map(AbstractEntity entity, Map<String, String> resultMap) {
        Domain dom = (Domain) entity;
        String id = Long.toString(dom.getId());
        resultMap.put("id", id);
        String name = dom.getName();
        resultMap.put("name", name);
        String user = dom.getUser().getName();
        resultMap.put("user", user);
        String hive = dom.getHiveName();
        resultMap.put("hive", hive);
        String pac = dom.getUser().getPac().getName();
        resultMap.put("pac", pac);
        Date sDate = dom.getSince();
        if (assertNotNull(sDate)) {
            String since = df.format(sDate);
            resultMap.put("since", since);
        }
    }
    @Override
    protected void map2entity(Map<String, String> setParams, AbstractEntity entity) {
        Domain dom = (Domain) entity;
        String name = setParams.get("name");
        String user = setParams.get("user");
        if (assertNotNull(name)) {
            dom.setName(name);
        }
        if (assertNotNull(user)) {
            UnixUser u = new UnixUser();
            u.setName(user);
            dom.setUser(u);
        }
    }
    @Override
    protected void regularizeKeys(Map<String, String> whereParams) {
        replaceKey(whereParams, "user", "user.name");
        replaceKey(whereParams, "pac", "user.pac.name");
        replaceKey(whereParams, "hive", "hiveName");
    }
}
hsarback/src/de/hsadmin/remote/EMailAddressRemote.java
New file
@@ -0,0 +1,74 @@
package de.hsadmin.remote;
import java.util.Map;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.mods.dom.Domain;
import de.hsadmin.mods.email.EMailAddress;
public class EMailAddressRemote extends AbstractRemote {
    @Override
    protected void entity2map(AbstractEntity entity, Map<String, String> map) {
        EMailAddress adr = (EMailAddress) entity;
        long id = adr.getId();
        String domain = adr.getDomain().getName();
        String admin = adr.getDomain().getUser().getName();
        String pac = adr.getDomain().getUser().getPac().getName();
        String target = adr.getTarget();
        String localpart = adr.getLocalPart();
        String subdomain = adr.getSubdomain();
        String emailaddress = adr.getEMailAddress();
        String fulldomain = adr.getFullDomain();
        map.put("id", Long.toString(id));
        map.put("domain", domain);
        map.put("admin", admin);
        map.put("pac", pac);
        map.put("target", target);
        map.put("localpart", localpart);
        map.put("subdomain", subdomain);
        map.put("emailaddress", emailaddress);
        map.put("fulldomain", fulldomain);
    }
    @Override
    protected void map2entity(Map<String, String> map, AbstractEntity entity) {
        EMailAddress adr = (EMailAddress) entity;
        String localPart = map.get("localpart");
        if (assertNotNull(localPart)) {
            adr.setLocalPart(localPart);
        }
        String subdomain = map.get("subdomain");
        if (assertNotNull(subdomain)) {
            adr.setSubdomain(subdomain);
        }
        String target = map.get("target");
        if (assertNotNull(target)) {
            adr.setTarget(target);
        }
        String domain = map.get("domain");
        if (assertNotNull(domain)) {
            Domain dom = new Domain();
            dom.setName(domain);
            adr.setDomain(dom);
        }
    }
    @Override
    protected Class<? extends AbstractEntity> getEntityClass() {
        return EMailAddress.class;
    }
    @Override
    protected void regularizeKeys(Map<String, String> whereParams) {
        replaceKey(whereParams, "domain", "domain.name");
        replaceKey(whereParams, "pac", "domain.user.pac.name");
        replaceKey(whereParams, "admin", "domain.user.name");
    }
    @Override
    public String getOrderBy() {
        return "ORDER BY obj.domain.name ASC, obj.subdomain ASC, obj.localpart ASC";
    }
}
hsarback/src/de/hsadmin/remote/EMailAliasRemote.java
New file
@@ -0,0 +1,46 @@
package de.hsadmin.remote;
import java.util.Map;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.mods.email.EMailAlias;
public class EMailAliasRemote extends AbstractRemote {
    @Override
    protected void entity2map(AbstractEntity entity, Map<String, String> map) {
        EMailAlias alias = (EMailAlias) entity;
        String id = Long.toString(alias.getId());
        String name = alias.getName();
        String pac = alias.getPac().getName();
        String target = alias.getTarget();
        map.put("id", id);
        map.put("name", name);
        map.put("pac", pac);
        map.put("target", target);
    }
    @Override
    protected Class<? extends AbstractEntity> getEntityClass() {
        return EMailAlias.class;
    }
    @Override
    protected void map2entity(Map<String, String> map, AbstractEntity entity) {
        EMailAlias alias = (EMailAlias) entity;
        String name = map.get("name");
        String target = map.get("target");
        if (assertNotNull(name)) {
            alias.setName(name);
        }
        if (assertNotNull(target)) {
            alias.setTarget(target);
        }
    }
    @Override
    protected void regularizeKeys(Map<String, String> whereParams) {
        replaceKey(whereParams, "pac", "pac.name");
    }
}
hsarback/src/org/apache/xmlrpc/webserver/XmlRpcServlet.properties
@@ -1,3 +1,6 @@
member=de.hsadmin.remote.CustomerRemote
user=de.hsadmin.remote.UnixUserRemote
domain=de.hsadmin.remote.DomainRemote
emailalias=de.hsadmin.remote.EMailAliasRemote
emailaddress=de.hsadmin.remote.EMailAddressRemote
q=de.hsadmin.remote.QueueTaskRemote
hsarback/test/de/hsadmin/remote/CustomerTest.java
@@ -44,12 +44,13 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        setParams.put("membercode", "hsh00-aaa");
        setParams.put("password", "geheimnIs");
        setParams.put("memberno", "20001");
        setParams.put("membersince", "01.10.2010");
        setParams.put("contact_salut", "Herr");
        setParams.put("contact_title", "Dr.");
        setParams.put("contact_firstname", "Rainer");
        setParams.put("contact_lastname", "Mustermann");
        setParams.put("contact_firstname", "Ömer Günther");
        setParams.put("contact_lastname", "Janßen-Müller");
        setParams.put("contact_salut", "Herr");
        setParams.put("contact_street", "Hauptstr. 1");
        setParams.put("contact_zipcode", "99998");
@@ -58,7 +59,7 @@
        setParams.put("contact_phone_private", "+49 9999 123456");
        setParams.put("contact_email", "rainer.mustermann@example.org");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams };
        try {
            client.execute(MODULE + ".add", params);
@@ -77,7 +78,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -94,7 +95,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -111,7 +112,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -128,7 +129,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -140,25 +141,57 @@
    }
    @Test
    public void testDeleteAsMember() {
    public void testUpdateAsMember() {
        String user = "hsh00-aaa";
        int membersCount = -9999;
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Map<String, String> setParams = new HashMap<String, String>();
        whereParams.put("membercode", "hsh00-aaa");
        setParams.put("contact_firstname", "Hugo");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            membersCount = getMembersCount(user);
            client.execute(MODULE + ".update", params);
            fail("exception erwartet");
        } catch (XmlRpcException e) {
            // Exception erwartet
        }
    }
    @Test
    public void testUpdateAsHostmaster() {
        String user = "pe";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Map<String, String> setParams = new HashMap<String, String>();
        whereParams.put("membercode", "hsh00-aaa");
        setParams.put("contact_title", "Prof.");
        setParams.put("membersince", "Hugo");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            client.execute(MODULE + ".update", params);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
    @Test
    public void testDeleteAsMember() {
        String user = "hsh00-aaa";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("membercode", "hsh00-aaa");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            client.execute(MODULE + ".delete", params);
            assertEquals(membersCount - 1, getMembersCount(user));
            fail("exception erwartet");
        } catch (XmlRpcException e) {
            fail(e.getMessage());
            // Exception erwartet
        }
    }
@@ -175,7 +208,7 @@
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("membercode", "hsh00-aaa");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            client.execute(MODULE + ".delete", params);
@@ -189,7 +222,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        Object execute = client.execute(MODULE + ".search", params);
        Object[] result = (Object[]) execute;
hsarback/test/de/hsadmin/remote/DomainTest.java
New file
@@ -0,0 +1,140 @@
package de.hsadmin.remote;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Map;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class DomainTest {
    private static final String MODULE = "domain";
    private XmlRpcClient client;
    private RemoteCASHelper cas;
    @Before
    public void setUp() throws Exception {
        client = RemoteTestHelper.getClient();
        cas = new RemoteCASHelper();
    }
    @After
    public void tearDown() throws Exception {
        client = null;
        cas = null;
    }
    @Test
    public void testSearchAllAsPacAdmin() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            assertEquals(36, result.length);
            for (Object o : result) {
                if (o instanceof Map<?, ?>) {
                    Map<?, ?> row = (Map<?, ?>) o;
                    assertEquals("peh00", row.get("user"));
                } else {
                    fail("map expected");
                }
            }
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
    @Test
    public void testUpdate() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        Map<String, String> whereParams = new HashMap<String, String>();
        setParams.put("user", "peh00-phor");
        whereParams.put("name", "i2null.de");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            Object execute = client.execute(MODULE + ".update", params);
            assertNotNull(execute);
            fail("exception expected");
        } catch (XmlRpcException e) {
//            System.out.println(e.getMessage());
        }
    }
    @Test
    public void testCreate() {
        int count = getDomsCount();
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        setParams.put("name", "f6n.de");
        setParams.put("user", "peh00");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams };
        try {
            Object execute = client.execute(MODULE + ".add", params);
            assertTrue(execute instanceof Map<?, ?>);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count + 1, getDomsCount());
    }
    @Test
    public void testDelete() {
        int count = getDomsCount();
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("name", "f6n.de");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".delete", params);
            assertNull(execute);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count - 1, getDomsCount());
    }
    private int getDomsCount() {
        int count = 0;
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            count = result.length;
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        return count;
    }
}
hsarback/test/de/hsadmin/remote/EMailAddressTest.java
New file
@@ -0,0 +1,246 @@
package de.hsadmin.remote;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Map;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class EMailAddressTest {
    private static final String MODULE = "emailaddress";
    private XmlRpcClient client;
    private RemoteCASHelper cas;
    @Before
    public void setUp() throws Exception {
        client = RemoteTestHelper.getClient();
        cas = new RemoteCASHelper();
    }
    @After
    public void tearDown() throws Exception {
        client = null;
        cas = null;
    }
    @Test
    public void testSearchAllAsPacAdmin() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            assertEquals(262, result.length);
            for (Object o : result) {
                if (o instanceof Map<?, ?>) {
                    Map<?, ?> row = (Map<?, ?>) o;
                    assertEquals("peh00", row.get("pac"));
                } else {
                    fail("map expected");
                }
            }
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
    @Test
    public void testSearchAsPacAdmin() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("domain", "herzensklaenge.de");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            assertEquals(2, result.length);
            for (Object o : result) {
                if (o instanceof Map<?, ?>) {
                    Map<?, ?> row = (Map<?, ?>) o;
                    assertEquals("peh00", row.get("pac"));
                } else {
                    fail("map expected");
                }
            }
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
    @Test
    public void testUpdate() {
        int count = getTargetCount();
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        Map<String, String> whereParams = new HashMap<String, String>();
        setParams.put("target", "peh00-phor");
        whereParams.put("domain", "jalin.de");
        whereParams.put("localpart", "fax");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            Object execute = client.execute(MODULE + ".update", params);
            Object[] result = (Object[]) execute;
            assertEquals(1, result.length);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count + 1, getTargetCount());
        setParams = new HashMap<String, String>();
        whereParams = new HashMap<String, String>();
        setParams.put("target", "peh00-hotline");
        whereParams.put("domain", "jalin.de");
        whereParams.put("localpart", "fax");
        params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            Object execute = client.execute(MODULE + ".update", params);
            Object[] result = (Object[]) execute;
            assertEquals(1, result.length);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count, getTargetCount());
    }
    @Test
    public void testSearchAsDomAdmin() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("domain", "herzensklaenge.de");
        whereParams.put("admin", "peh00");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            assertEquals(2, result.length);
            for (Object o : result) {
                if (o instanceof Map<?, ?>) {
                    Map<?, ?> row = (Map<?, ?>) o;
                    assertEquals("peh00", row.get("admin"));
                } else {
                    fail("map expected");
                }
            }
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
    @Test
    public void testCreateAsPacAdminFails() {
        int count = getObjectCount();
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        setParams.put("localpart", "f6n");
        setParams.put("domain", "f6n.de");
        setParams.put("target", "peh00-phor");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams };
        try {
            Object execute = client.execute(MODULE + ".add", params);
            assertTrue(execute instanceof Map<?, ?>);
            fail("exception expected");
        } catch (XmlRpcException e) {
        }
        assertEquals(count, getObjectCount());
    }
    @Test
    public void testCreateAndDelete() {
        int count = getObjectCount();
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> setParams = new HashMap<String, String>();
        setParams.put("localpart", "f6n");
        setParams.put("domain", "jalin.de");
        setParams.put("target", "peh00-phor");
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams };
        try {
            Object execute = client.execute(MODULE + ".add", params);
            assertTrue(execute instanceof Map<?, ?>);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count + 1, getObjectCount());
        count = getObjectCount();
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("localpart", "f6n");
        whereParams.put("domain", "jalin.de");
        params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".delete", params);
            assertNull(execute);
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        assertEquals(count - 1, getObjectCount());
    }
    private int getObjectCount() {
        int count = -1;
        try {
            String user = "peh00";
            String grantingTicketURL = cas.getGrantingTicketURL(user);
            Map<String, String> whereParams = new HashMap<String, String>();
            Object[] params = new Object[] { user,
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            count = result.length;
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        return count;
    }
    private int getTargetCount() {
        int count = -1;
        try {
            String user = "peh00";
            String grantingTicketURL = cas.getGrantingTicketURL(user);
            Map<String, String> whereParams = new HashMap<String, String>();
            whereParams.put("target", "peh00-phor");
            Object[] params = new Object[] { user,
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            count = result.length;
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
        return count;
    }
}
hsarback/test/de/hsadmin/remote/EMailAliasTest.java
New file
@@ -0,0 +1,61 @@
package de.hsadmin.remote;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Map;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class EMailAliasTest {
    private static final String MODULE = "emailalias";
    private XmlRpcClient client;
    private RemoteCASHelper cas;
    @Before
    public void setUp() throws Exception {
        client = RemoteTestHelper.getClient();
        cas = new RemoteCASHelper();
    }
    @After
    public void tearDown() throws Exception {
        client = null;
        cas = null;
    }
    @Test
    public void testSearchAllAsPacAdmin() {
        String user = "peh00";
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user,
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
            assertEquals(262, result.length);
            for (Object o : result) {
                if (o instanceof Map<?, ?>) {
                    Map<?, ?> row = (Map<?, ?>) o;
                    assertEquals("peh00", row.get("pac"));
                } else {
                    fail("map expected");
                }
            }
        } catch (XmlRpcException e) {
            fail(e.getMessage());
        }
    }
}
hsarback/test/de/hsadmin/remote/QueueTaskTest.java
@@ -39,7 +39,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -67,7 +67,7 @@
        setParams.put("details", "Test");
        whereParams.put("user", "peh00");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams, whereParams };
        try {
            Object execute = client.execute(MODULE + ".update", params);
@@ -93,7 +93,7 @@
        setParams.put("details", "Blupp");
        setParams.put("exception", "f6n");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                setParams };
        try {
            Object execute = client.execute(MODULE + ".add", params);
@@ -111,7 +111,7 @@
        Map<String, String> whereParams = new HashMap<String, String>();
        whereParams.put("user", "peh00");
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".delete", params);
hsarback/test/de/hsadmin/remote/RemoteCASHelper.java
@@ -2,40 +2,33 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Properties;
import javax.net.ssl.HttpsURLConnection;
import de.hsadmin.core.util.Config;
public class RemoteCASHelper {
    private static String LOGIN_URL = "https://login.hostsharing.net:443/cas/v1/tickets";
    private String loginURL;
    private Properties config;
    
    public RemoteCASHelper() {
        initConfig();
    }
    private void initConfig() {
        config = new Properties();
        try {
            config.load(new FileInputStream( new File(System.getProperty("user.home"), ".hsadmin.conf")));
        } catch (IOException e) {
        }
        loginURL = config.getProperty("loginURL", LOGIN_URL);
        loginURL = Config.getInstance().getProperty("loginURL", LOGIN_URL);
    }
    
    public String getGrantingTicketURL(String user) {
        String pw = config.getProperty(user + ".passWord", "-");
        String pw = Config.getInstance().getProperty(user + ".passWord", "-");
        try {
            String encodedParams = URLEncoder.encode("username", "UTF-8")
                + "=" + URLEncoder.encode(user, "UTF-8")
hsarback/test/de/hsadmin/remote/RemoteTest.java
@@ -10,8 +10,9 @@
//    MySqlDatabaseTest.class,
//    MySqlUserTest.class,
//    PgSqlUserTest.class,
//    EMailAddressTest.class,
//    DomainTest.class,
    EMailAliasTest.class,
    EMailAddressTest.class,
    DomainTest.class,
//    HostmasterTest.class,
    QueueTaskTest.class
})
hsarback/test/de/hsadmin/remote/RemoteTestHelper.java
@@ -5,16 +5,19 @@
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import de.hsadmin.core.util.Config;
public class RemoteTestHelper {
    
    public static String BACKEND_URL = "https://agnes.ostwall195.de:9443/hsar/backend";
    private static final String XMLRPC_URL = "https://admin.hostsharing.net:443/hsar/xmlrpc/hsadmin";
    private static final String BACKEND_URL = "https://admin.hostsharing.net:443/hsar/backend";
    
    private static XmlRpcClient client;
    public static XmlRpcClient getClient() throws Exception {
        if (client == null) {
            XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
            config.setServerURL(new URL("https://agnes.ostwall195.de:9443/hsar/xmlrpc/hsadmin"));
            config.setServerURL(new URL(getXmlrpcURL()));
            config.setEnabledForExtensions(true);
            client = new XmlRpcClient();
            client.setConfig(config);
@@ -22,4 +25,11 @@
        return client;
    }
    public static String getBackendURL() {
        return Config.getInstance().getProperty("backendURL", BACKEND_URL);
    }
    public static String getXmlrpcURL() {
        return Config.getInstance().getProperty("xmlrpcURL", XMLRPC_URL);
    }
}
hsarback/test/de/hsadmin/remote/UnixUserTest.java
@@ -38,7 +38,7 @@
        String grantingTicketURL = cas.getGrantingTicketURL(user);
        Map<String, String> whereParams = new HashMap<String, String>();
        Object[] params = new Object[] { user, 
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                whereParams };
        try {
            Object execute = client.execute(MODULE + ".search", params);
@@ -69,7 +69,7 @@
            setParams.put("quota", "128");
            setParams.put("quotalimit", "192");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    setParams };
            Object execute = client.execute(MODULE + ".add", params);
            if (execute instanceof Map<?, ?>) {
@@ -96,7 +96,7 @@
            setParams.put("name", "peh01-testfail");
            setParams.put("password", "test123");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    setParams };
            Object execute = client.execute(MODULE + ".add", params);
            assertNull(execute);
@@ -117,7 +117,7 @@
            setParams.put("name", "peh00-langer-name");
            setParams.put("password", "test123");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    setParams };
            Object execute = client.execute(MODULE + ".add", params);
            assertNull(execute);
@@ -139,7 +139,7 @@
            whereParams.put("name", "peh00-test2");
            setParams.put("password", "test1234");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    setParams, whereParams };
            Object execute = client.execute(MODULE + ".update", params);
            if (execute instanceof Object[]) {
@@ -171,7 +171,7 @@
            Map<String, String> whereParams = new HashMap<String, String>();
            whereParams.put("name", "peh00-langer-name");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".delete", params);
            assertNull(execute);
@@ -190,7 +190,7 @@
            Map<String, String> whereParams = new HashMap<String, String>();
            whereParams.put("name", "peh00-test2");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".delete", params);
            assertNull(execute);
@@ -209,7 +209,7 @@
            Map<String, String> whereParams = new HashMap<String, String>();
            whereParams.put("name", "peh00-test2");
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".delete", params);
            assertNull(execute);
@@ -226,7 +226,7 @@
            String grantingTicketURL = cas.getGrantingTicketURL(user);
            Map<String, String> whereParams = new HashMap<String, String>();
            Object[] params = new Object[] { user, 
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.BACKEND_URL),
                    cas.getServiceTicket(grantingTicketURL, RemoteTestHelper.getBackendURL()),
                    whereParams };
            Object execute = client.execute(MODULE + ".search", params);
            Object[] result = (Object[]) execute;
hsarback/webapp/WEB-INF/web.xml
@@ -27,7 +27,7 @@
          </init-param>
          <init-param>
              <param-name>Components</param-name>
              <param-value>user,member,contact,bankaccount,pac,paccomponent,hive,ipaddr,basepac,basecomponent,component,q</param-value>
              <param-value>user,domain,member,contact,bankaccount,emailaddress,emailalias,pac,paccomponent,hive,ipaddr,basepac,basecomponent,component,q</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_user</param-name>
@@ -36,6 +36,14 @@
          <init-param>
              <param-name>ComponentDescription_user</param-name>
              <param-value>Unix User</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_domain</param-name>
            <param-value>de.hsadmin.mods.dom.Domain</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentDescription_domain</param-name>
              <param-value>Domains</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_member</param-name>
@@ -62,6 +70,22 @@
              <param-value>Bankverbindungen</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_emailaddress</param-name>
            <param-value>de.hsadmin.mods.email.EMailAddress</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentDescription_emailaddress</param-name>
              <param-value>e-Mail Adressen</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_emailalias</param-name>
            <param-value>de.hsadmin.mods.email.EMailAlias</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentDescription_emailalias</param-name>
              <param-value>e-Mail Aliases</param-value>
          </init-param>
          <init-param>
              <param-name>ComponentClass_pac</param-name>
            <param-value>de.hsadmin.mods.pac.Pac</param-value>
          </init-param>