org.ejbca.externalra.gui.EnrollInterfaceBean.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.externalra.gui.EnrollInterfaceBean.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA: The OpenSource Certificate Authority                          *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.externalra.gui;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.netscape.NetscapeCertRequest;
import org.ejbca.config.InternalConfiguration;
import org.ejbca.core.model.SecConst;
import org.ejbca.extra.db.CertificateRequestRequest;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.ejbca.util.FileTools;

import com.icesoft.faces.component.inputfile.InputFile;
import com.icesoft.faces.context.ByteArrayResource;
import com.icesoft.faces.context.Resource;
import com.icesoft.faces.context.effects.JavascriptContext;
import com.novosec.pkix.asn1.crmf.CertReqMessages;

/**
 * This is the backing bean for the enrollment part of the External RA GUI.
 * 
 * @version $Id: EnrollInterfaceBean.java 9330 2010-06-30 18:16:53Z anatom $
 */
public class EnrollInterfaceBean {

    private static final String PEM_CSR_BEGIN = "-----BEGIN CERTIFICATE REQUEST-----";
    private static final String PEM_CSR_END = "-----END CERTIFICATE REQUEST-----";
    private static final String PEM_CSR_BEGIN_VISTA = "-----BEGIN NEW CERTIFICATE REQUEST-----";
    private static final String PEM_CSR_END_VISTA = "-----END NEW CERTIFICATE REQUEST-----";
    private static final String PEM_PKCS7_BEGIN = "-----BEGIN PKCS7-----";
    private static final String PEM_PKCS7_END = "-----END PKCS7-----";

    private static final Logger log = Logger.getLogger(EnrollInterfaceBean.class);

    private IRequestDispatcher requestDispatcher = null;

    // General variables
    private String userAgentString = null;
    private String username = null;
    private String password = null;
    private boolean showPassword = false;
    private String filename = null;
    private Resource resource = null;
    private String mimeType = null;

    // Variables used to get a certificate from a CSR
    private String certificateRequest = PEM_CSR_BEGIN + "\n...base 64 encoded request...\n" + PEM_CSR_END;
    private String requestedResponseType = "der";

    private String certificateRequestType = null;
    private String certificateResponse = "";
    private String certificateResponseType = null;

    private String cspSelectValue = null;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean getShowPassword() {
        return showPassword;
    }

    public void setShowPassword(boolean showPassword) {
        this.showPassword = showPassword;
    }

    public boolean isDownloadAvailable() {
        return resource != null;
    }

    public String getFilename() {
        return filename;
    }

    public Resource getResource() {
        return resource;
    }

    public String getMimeType() {
        return mimeType;
    }

    public String getCertificateRequest() {
        return certificateRequest;
    }

    public void setCertificateRequest(String certificateRequest) {
        this.certificateRequest = certificateRequest;
    }

    public String getRequestedResponseType() {
        return requestedResponseType;
    }

    public void setRequestedResponseType(String requestedResponseType) {
        this.requestedResponseType = requestedResponseType;
    }

    public String getCertificateRequestType() {
        return certificateRequestType;
    }

    public void setCertificateRequestType(String certificateRequestType) {
        this.certificateRequestType = certificateRequestType;
    }

    public String getCertificateResponseType() {
        return certificateResponseType;
    }

    public String getCertificateResponse() {
        return certificateResponse;
    }

    public void setCertificateResponse(String certificateResponse) {
        this.certificateResponse = certificateResponse;
    }

    public String getCspSelectValue() {
        return cspSelectValue;
    }

    public void setCspSelectValue(String cspSelectValue) {
        this.cspSelectValue = cspSelectValue;
    }

    public String getKeySpec() {
        return ExternalRaGuiConfiguration.getKeySpec();
    }

    public String getExportable() {
        return ExternalRaGuiConfiguration.getExportable() ? "1" : "0";
    }

    public String getVersionString() {
        return InternalConfiguration.getAppVersionNumber() + " (" + InternalConfiguration.getSvnRevision() + ")";
    }

    public String getHelpUrl() {
        return ExternalRaGuiConfiguration.getHelpUrl();
    }

    /** @return The host's name or "unknown" if it could not be determined. */
    public String getHostName() {
        String hostname = "unknown";
        try {
            hostname = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            // Ignored
        }
        return hostname;
    }

    /** @return true if the user-agent String contains MSIE **/
    public boolean isInternetExplorer() {
        return getUserAgenString().indexOf("MSIE") != -1;
    }

    /**
     * XEnroll:
     *  "Windows NT 5.1" = Win XP x86
     *  "Windows NT 5.2" = Win XP x64, Server 2003
     * CertEnroll:
     *  "Windows NT 6.0" = Win Vista, Server 2008
     *  "Windows NT 6.1" = Windows 7, Server 2008 R2
     */
    public boolean isWindowsNT5() {
        return getUserAgenString().indexOf("Windows NT 5.") != -1;
    }

    /**
     * Prevent the web framework from rendering an empty table if there are no global messages
     * by using this method. Empty tables results in error messages in the Safari log.
     */
    public boolean isMessagesPending() {
        return FacesContext.getCurrentInstance().getMessages(null).hasNext();
    }

    /**
     * @return the current user-agent string that identifies the clients browser and operating system. 
     */
    private String getUserAgenString() {
        if (userAgentString == null) {
            userAgentString = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap()
                    .get("user-agent");
            log.debug("User agent: " + userAgentString);
        }
        return userAgentString;
    }

    /**
     * @return an implementation for communication with the EJBCA instance.
     */
    private IRequestDispatcher getRequestDispatcher() {
        if (requestDispatcher == null) {
            // Change this to read the class name from the externalra-gui configuration if we add additional implementations.
            String className = ExternalRARequestDispatcher.class.getName();
            try {
                requestDispatcher = (IRequestDispatcher) Class.forName(className).newInstance();
            } catch (ClassNotFoundException e) {
                log.error("Could not find request implementaion :" + className);
            } catch (InstantiationException e) {
                log.error("Could not instantiate request implementaion :" + className);
            } catch (IllegalAccessException e) {
                log.error("Could not access request implementaion :" + className);
            }
        }
        return requestDispatcher;
    }

    /**
     * Used for reading a Certificate Signing Request file upload into the certificate request String.
     * @param actionEvent is the parameter from the web framework containing the file.
     */
    public void uploadActionListener(ActionEvent actionEvent) {
        InputFile inputFile = (InputFile) actionEvent.getSource();
        FacesContext context = FacesContext.getCurrentInstance();
        if (inputFile.getFileInfo().isSaved()) {
            // Validate that it is a CSR..
            File f = inputFile.getFileInfo().getFile();
            // Assume this is a small file.. it should be..
            long len = f.length();
            if (len < 16 * 1024L) {
                byte[] buf = new byte[(int) len];
                try {
                    FileInputStream in = new FileInputStream(f);
                    in.read(buf);
                    in.close();
                } catch (IOException e) {
                    context.addMessage(null /*actionEvent.getComponent().getClientId(context)*/, new FacesMessage(
                            FacesMessage.SEVERITY_ERROR, getMessage("enroll.csrcert.uploadfailed"), null));
                    log.debug("Rejected uploaded file due to IOException.");
                    return;
                }
                try {
                    // See if it was a PEM
                    buf = FileTools.getBytesFromPEM(buf, PEM_CSR_BEGIN, PEM_CSR_END);
                } catch (IOException e) {
                    log.debug("Uploaded file was not a PEM.. tryin to parse it as a DER encoded request.");
                }
                // See if it as a PKCS10
                try {
                    new PKCS10CertificationRequest(buf);
                } catch (Exception e) {
                    context.addMessage(null /*actionEvent.getComponent().getClientId(context)*/, new FacesMessage(
                            FacesMessage.SEVERITY_ERROR, getMessage("enroll.csrcert.uploadfailednotpkcs10"), null));
                    log.debug("Rejected uploaded file since it's not a valid PKCS#10 request.");
                    return;
                }
                // Convert it back to a PEM
                String pem = PEM_CSR_BEGIN + "\n" + new String(Base64.encode(buf)) + "\n" + PEM_CSR_END;
                certificateRequest = pem;
                context.addMessage(null /*actionEvent.getComponent().getClientId(context)*/,
                        new FacesMessage(FacesMessage.SEVERITY_INFO, getMessage("enroll.csrcert.uploadok"), null));
            } else {
                context.addMessage(null /*actionEvent.getComponent().getClientId(context)*/, new FacesMessage(
                        FacesMessage.SEVERITY_ERROR, getMessage("enroll.csrcert.uploadfailedtoolarge"), null));
            }
        } else {
            log.debug("File upload failed: " + inputFile.getFileInfo().getException().getMessage());
            context.addMessage(null /*actionEvent.getComponent().getClientId(context)*/,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.csrcert.uploadfailed"), null));
        }
    }

    /**
     * Action that requests a KeyStore from EJBCA using the given credentials.
     */
    public void createKeystore() {
        log.info("Recieved a KeyStore request for username '" + username + "' from " + getRemoteAddress());
        FacesContext context = FacesContext.getCurrentInstance();
        if (username == null || username.length() == 0 || password == null || password.length() == 0) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.incompletefields"), null));
            return;
        }
        // Request the KeyStore from the CA
        ResponseData keyStoreResponse = getRequestDispatcher().getKeyStoreResponse(username, password);
        // Check if got a valid result
        if (keyStoreResponse == null) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.noresponse"), null));
            log.error("KeyStore request for '" + username + "' failed. No response from CA.");
            return;
        } else if (keyStoreResponse.getErrorMessage() != null) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    getMessage("enroll.keystore.couldnotcreate"), null));
            log.info("KeyStore request for '" + username + "' failed. " + keyStoreResponse.getErrorMessage());
            return;
        }
        // Handle response
        resource = new ByteArrayResource(keyStoreResponse.getResponseData());
        switch (keyStoreResponse.getResponseType()) {
        case SecConst.TOKEN_SOFT_JKS:
            filename = username + ".jks";
            break;
        case SecConst.TOKEN_SOFT_P12:
            filename = username + ".p12";
            break;
        case SecConst.TOKEN_SOFT_PEM:
            filename = username + ".pem";
            break;
        default:
            filename = username + ".unknown";
            break;
        }
        mimeType = "application/octet-stream";
        log.info("KeyStore request with response-type " + keyStoreResponse.getResponseType() + " for '" + username
                + "' was successful.");
    }

    /**
     * Action that requests a certificate from EJBCA using the given credentials and the Certificate Signing Request.
     */
    public void createCertFromCSR() {
        log.info("Recieved a certificate signing request for username '" + username + "' from "
                + getRemoteAddress());
        if (log.isDebugEnabled()) {
            log.debug("certificateRequest: " + certificateRequest);
        }
        FacesContext context = FacesContext.getCurrentInstance();
        if (username == null || username.length() == 0 || password == null || password.length() == 0
                || certificateRequest == null || certificateRequest.length() == 0) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.incompletefields"), null));
            return;
        }
        // Verify that we got a valid Certificate Signing Request
        try {
            // Clean it up if windows has messed it up..
            byte[] buf = (PEM_CSR_BEGIN + certificateRequest.replaceFirst(PEM_CSR_BEGIN, "")
                    .replaceFirst(PEM_CSR_END, "").replaceAll(" ", "").replaceAll("\r", "") + PEM_CSR_END)
                            .getBytes();
            // See if it is a PEM
            buf = FileTools.getBytesFromPEM(buf, PEM_CSR_BEGIN, PEM_CSR_END);
            certificateRequest = PEM_CSR_BEGIN + "\n" + new String(Base64.encode(buf)) + "\n" + PEM_CSR_END;
            if (log.isDebugEnabled()) {
                log.debug("cleaned req: " + certificateRequest);
            }
            new PKCS10CertificationRequest(buf);
        } catch (Exception e) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.invalidreqdata"), null));
            return;
        }
        // Determine what kind of response the user has requested
        int responseType = CertificateRequestRequest.RESPONSE_TYPE_ENCODED;
        if ("pkcs7".equals(requestedResponseType)) {
            responseType = CertificateRequestRequest.RESPONSE_TYPE_PKCS7;
        }
        // Request the certificate from the CA
        ResponseData csrResponse = getRequestDispatcher().getCertificateSigningRequestResponse(username, password,
                certificateRequest, responseType);
        // Check if got a valid result
        if (csrResponse == null) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.noresponse"), null));
            log.error("Certificate request for '" + username + "' failed. No response from CA.");
            return;
        } else if (csrResponse.getErrorMessage() != null) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    getMessage("enroll.csrcert.couldnotcreate"), null));
            log.info("Certificate request for '" + username + "' failed. " + csrResponse.getErrorMessage());
            return;
        }
        // Handle response
        switch (csrResponse.getResponseType()) {
        case CertificateRequestRequest.RESPONSE_TYPE_ENCODED:
            if ("pem".equals(requestedResponseType)) {
                Certificate[] certs = new Certificate[1];
                try {
                    certs[0] = CertTools.getCertfromByteArray(csrResponse.getResponseData());
                    resource = new ByteArrayResource(
                            CertTools.getPEMFromCerts(CertTools.getCertCollectionFromArray(certs, "BC")));
                    filename = username + ".pem";
                    mimeType = "application/x-pem-file";
                } catch (Exception e) {
                    log.error("", e);
                    context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            getMessage("enroll.invalidresponse"), null));
                }
            } else {
                resource = new ByteArrayResource(csrResponse.getResponseData());
                filename = username + ".der";
                mimeType = "application/pkix-cert";
            }
            break;
        case CertificateRequestRequest.RESPONSE_TYPE_PKCS7:
            resource = new ByteArrayResource(csrResponse.getResponseData());
            filename = username + ".p7b";
            mimeType = "application/x-pkcs7-certificates";
            break;
        default:
            filename = username + ".unknown";
            mimeType = "application/octet-stream";
            break;
        }
        log.info("Certificate request with response-type " + csrResponse.getResponseType() + " for '" + username
                + "' was successful.");
    }

    /**
     * Action that requests a certificate from EJBCA using the given credentials and the Certificate Signing Request created by the browser.
     */
    public void createCertFromBrowser() {
        log.info("Recieved a browser generated certificate request of type " + certificateRequestType
                + " for username '" + username + "' from " + getRemoteAddress());
        if (log.isDebugEnabled()) {
            log.debug("certificateRequest: " + certificateRequest);
        }
        FacesContext context = FacesContext.getCurrentInstance();
        if (username == null || username.length() == 0 || password == null || password.length() == 0
                || certificateRequest == null || certificateRequest.length() == 0 || certificateRequestType == null
                || certificateRequestType.length() == 0) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.incompletefields"), null));
            return;
        }
        // Verify that we got a valid certificate request and determine response type
        byte[] buf = null;
        int requestType = Integer.parseInt(certificateRequestType);
        int responseType;
        switch (requestType) {
        case CertificateRequestRequest.REQUEST_TYPE_CRMF:
            responseType = CertificateRequestRequest.RESPONSE_TYPE_PKCS7;
            buf = Base64.decode(certificateRequest.getBytes());
            ASN1InputStream asn1InputStream = new ASN1InputStream(buf);
            try {
                // Verify that we can parse this as a CRMF object
                CertReqMessages.getInstance(asn1InputStream.readObject()).getCertReqMsg(0);
            } catch (IOException e) {
                context.addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.invalidreqdata"), null));
                log.error("", e);
            }
            break;
        case CertificateRequestRequest.REQUEST_TYPE_PKCS10:
            responseType = CertificateRequestRequest.RESPONSE_TYPE_PKCS7;
            try {
                if (!isWindowsNT5()) {
                    responseType = CertificateRequestRequest.RESPONSE_TYPE_UNSIGNEDPKCS7;
                }
                // Replace Vista PEM markers
                certificateRequest = certificateRequest.replaceAll(PEM_CSR_BEGIN_VISTA, PEM_CSR_BEGIN);
                certificateRequest = certificateRequest.replaceAll(PEM_CSR_END_VISTA, PEM_CSR_END);
                if (certificateRequest.indexOf(PEM_CSR_BEGIN) == -1) {
                    certificateRequest = PEM_CSR_BEGIN + "\n" + certificateRequest + "\n" + PEM_CSR_END;
                }
                buf = FileTools.getBytesFromPEM(certificateRequest.getBytes(), PEM_CSR_BEGIN, PEM_CSR_END);
                new PKCS10CertificationRequest(buf);
            } catch (Exception e) {
                log.error("", e);
                context.addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.invalidreqdata"), null));
                return;
            }
            break;
        case CertificateRequestRequest.REQUEST_TYPE_KEYGEN:
            responseType = CertificateRequestRequest.RESPONSE_TYPE_PKCS7;
            try {
                buf = Base64.decode(certificateRequest.getBytes());
                ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(buf));
                ASN1Sequence spkac = (ASN1Sequence) in.readObject();
                in.close();
                NetscapeCertRequest nscr = new NetscapeCertRequest(spkac);
                // Verify POPO, we don't care about the challenge, it's not important.
                nscr.setChallenge("challenge");
                if (nscr.verify("challenge") == false) {
                    context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            getMessage("enroll.invalidreqdata"), null));
                    return;
                }
            } catch (Exception e) {
                log.error("", e);
                context.addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.invalidreqdata"), null));
                return;
            }
            break;
        case -1:
            // This is a workaround to hide errors when we use the KeyGenServlet..
            return;
        default:
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.unknownrequesttype"), null));
            return;
        }
        // Request the certificate from the CA
        if (log.isDebugEnabled()) {
            log.debug("Got requestType " + requestType + " and is expecting responseType " + responseType
                    + " for user " + username);
        }
        ResponseData responseData = getRequestDispatcher().getCertificateResponse(username, password, requestType,
                buf, responseType);
        // Check if got a valid result
        if (responseData == null) {
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.noresponse"), null));
            log.error("Certificate request for '" + username + "' failed. No response from CA.");
            return;
        } else if (responseData.getErrorMessage() != null) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    getMessage("enroll.browsercert.couldnotcreate"), null));
            log.info("Certificate request for '" + username + "' failed. " + responseData.getErrorMessage());
            return;
        }
        // Handle response
        certificateResponseType = "" + responseData.getResponseType();
        switch (responseData.getResponseType()) {
        case CertificateRequestRequest.RESPONSE_TYPE_PKCS7:
            if (isInternetExplorer()) {
                // Working for XP+IE7
                certificateResponse = new String(Base64.encode(responseData.getResponseData(), false));
            } else {
                resource = new ByteArrayResource(responseData.getResponseData());
                mimeType = "application/x-x509-user-cert";
            }
            break;
        case CertificateRequestRequest.RESPONSE_TYPE_UNSIGNEDPKCS7:
            // Working for Vista+IE8
            certificateResponse = new String(Base64.encode(responseData.getResponseData(), false));
            try {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                String pkcs7 = PEM_PKCS7_BEGIN + "\n"
                        + new String(Base64.encode(responseData.getResponseData(), true)) + "\n" + PEM_PKCS7_END
                        + "\n";
                log.debug("pkcs7=" + pkcs7);
                CertPath certPath = cf.generateCertPath(new ByteArrayInputStream(responseData.getResponseData()),
                        "PKCS7");
                List<? extends Certificate> certList = certPath.getCertificates();
                Certificate caCert = certList.get(certList.size() - 1);
                String caCertificate = new String(Base64.encode(caCert.getEncoded(), false));
                resource = new ByteArrayResource(caCertificate.getBytes());
                mimeType = "application/x-x509-ca-cert";
            } catch (CertificateException e) {
                e.printStackTrace();
            }
            if (log.isDebugEnabled()) {
                log.debug("certificateResponse: " + certificateResponse);
            }
            break;
        default:
            context.addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, getMessage("enroll.unknownresponsetype"), null));
            log.error("Unknown result type: " + certificateResponseType);
            break;
        }
        log.info("Certificate request with response-type " + responseData.getResponseType() + " for '" + username
                + "' was successful.");
    }

    /**
     * Adds a JavaScript that removes the HTML from where it was called. Expects to be included inside a div with id "form:downloadDiv2".
     */
    public void removeLinks() {
        // Hide the installer links once the cert is installed 
        JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(),
                "if (document.getElementById('form:certificateInstalled').value == 'true') { document.getElementById('form:downloadLinkDiv2').innerHTML = 'Installed.';}");
    }

    /**
     * Action that does absolutely nothing. Used when we want a commandLink to just trigger an onclick or something similar.
     */
    public void noOp() {
    }

    /**
     * Get localized message from the message-bundle.
     */
    private String getMessage(String key) {
        String text = null;
        FacesContext context = FacesContext.getCurrentInstance();
        ResourceBundle bundle = ResourceBundle.getBundle(context.getApplication().getMessageBundle(),
                context.getViewRoot().getLocale(), Thread.currentThread().getContextClassLoader());
        try {
            text = bundle.getString(key);
        } catch (MissingResourceException e) {
            text = "?? key " + key + " not found ??";
        }
        return text;
    }

    private String getRemoteAddress() {
        return ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest())
                .getRemoteAddr();
    }
}