/*******************************************************************************
 * Copyright (c), 2001, 2002 N2 Broadband, Inc.  All Rights Reserved.
 *
 * This module contains unpublished, confidential, proprietary
 * material.  The use and dissemination of this material are
 * governed by a license.  The above copyright notice does not
 * evidence any actual or intended publication of this material.
 *
 * Author:  Drake H. Henderson
 * Created:  11-12-01
 *
 ******************************************************************************/
package com.n2bb.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.n2bb.util.N2bbSettings;
import com.n2bb.util.N2bbException;

public class N2bbEmail {

  private Log n2bbLog = LogFactory.getLog(N2bbSettings.N2BB_LOG);

  private String user = null;
  private String host = null;

  private int pos = 0;

  /* supports user@domain.ext or "user"@domain.ext
   *
   */
  public N2bbEmail(String email) throws N2bbException {
    n2bbLog.debug("enter email... '" + email + "'");

    if (email == null || email.trim().equals(""))
      throw new N2bbException("error.email.noAddress");

    email = email.trim();

    StringBuffer userTemp = new StringBuffer();
    StringBuffer hostTemp = new StringBuffer();

    // <mailbox> ::= <local-part> "@" <domain>
    try {
      // <local-part> ::= <dot-string> | <quoted-string>
      if (email.charAt(pos) == '\"') {
        try {
          userTemp.append(validateQuotedUser(email));
        } catch (N2bbException e) {
          throw e;
        }
      } else {
        try {
          userTemp.append(validateUnquotedUser(email));
        } catch (N2bbException e) {
          throw e;
        }
      }

      n2bbLog.debug("user... '" + userTemp.toString() + "'");

      if (userTemp.toString().length() == 0)
        throw new N2bbException("error.email.noUser");

      // n2bbLog.debug("N2bbEmail pos... " + pos);
      // n2bbLog.debug("N2bbEmail len... " + email.length());

      if ((pos == email.length()) || email.charAt(pos) != '@')
        throw new N2bbException("error.email.noAtAfterUser");
      pos++; // skip @ sign

      // <domain> ::=  <element> | <element> "." <domain>
      // <element> ::= <name> | "#" <number> | "[" <dotnum> "]"
      // only supporting <name> "." <a>

      if ((pos == email.length()) || email.charAt(pos) == '.')// no domain

        throw new N2bbException("error.email.noDomain");

      if (email.indexOf(".", pos) == -1)
        throw new N2bbException("error.email.noDotAfterDomain");

      try {
        hostTemp.append(validateDomain(email));
      } catch (N2bbException e) {
        throw e;
      }

      n2bbLog.debug("domain without ext... '" + hostTemp.toString() + "'");

      // n2bbLog.debug("N2bbEmail pos... " + pos);
      // n2bbLog.debug("N2bbEmail len... " + email.length());

      if (pos == email.length() - 1) // no ext
        throw new N2bbException("error.email.noExt");

      // add dot to domain
      hostTemp.append('.');
      pos++;

      try {
        hostTemp.append(validateExtension(email));
      } catch (N2bbException e) {
        throw e;
      }

      n2bbLog.debug("domain with ext... '" + hostTemp.toString() + "'");

    } catch (N2bbException e) {
      n2bbLog.debug("n2bb exception - message... " + e.getMessage());
      throw e;
    }
    catch (Exception e) {
      n2bbLog.error("exception - message... " + e.getMessage(), e);
      throw new N2bbException("error.email.validationFailed");
    }

    user = userTemp.toString();
    host = hostTemp.toString();
  }

  private String validateQuotedUser(String email) throws N2bbException {
    StringBuffer userTemp = new StringBuffer();

    try {
      if (email.indexOf('\"', 2) == -1) { // no closing quote or no text between quotes
        n2bbLog.debug("email has no closing quote or no text between quotes");
        throw new N2bbException("error.email.noQuote");
      }

      userTemp.append('\"');
      pos++;

      // <quoted-string> ::=  """ <qtext> """
      // <qtext> ::=  "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>
      while (true) {
        if (email.charAt(pos) == '\"') {
          userTemp.append('\"');
          pos++;
          break;
        }
        /*          // not supporting escape characters (the '"\" <x>' part of <qtext>)
                    // <x> ::= any one of the 128 ASCII characters (no exceptions)
                    if (email.charAt(pos) == '\\') {
                        userTemp.append('\\');
                        pos++;
                        char c = address.charAt(pos);
                        if (c < 0 || c > 128) {
                            throw new N2bbException("", "Email has an unallowed character at position " + (pos + 1));
                        }
                        userTemp.append(c);
                        pos++;
                    } else {
        */
        // <q> ::= any one of the 128 ASCII characters except <CR>, <LF>, quote ("), or backslash (\)
        char c = email.charAt(pos);
        if (c <= 0 || c == '\n' || c == '\r' || c == '\"' || c == '\\') {
          throw new N2bbException("error.email.invalidQuote");
        }
        userTemp.append(c);
        pos++;
        //          }
      }
    }
    catch (N2bbException e) {
      n2bbLog.debug("n2bb exception - message... " + e.getMessage());
      throw e;
    }
    catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.email.validationFailed");
    }
    return userTemp.toString();
  }

  private String validateUnquotedUser(String email) throws N2bbException {

    //  original specialChars...
    //    char[] specialChars = {'<', '>', '(', ')', '[', ']', '\\', '.', ',', ';', ':', '@', '\"'};
    
    //  allow '.' ...
    char[] specialChars = {'<', '>', '(', ')', '[', ']', '\\', ',', ';', ':', '@', '\"'};

    StringBuffer userTemp = new StringBuffer();

    try {
      if (email.indexOf("@") == -1) { // no @
        n2bbLog.debug("email has no @ sign");
        throw new N2bbException("error.email.noAtAfterUser");
      }

      // <dot-string> ::= <string> | <string> "." <dot-string>
      // <string> ::= <char> | <char> <string>
      // <char> ::= <c> | "\" <x>
      while (true) {
        /*          // not supporting escape characters (the '"\" <x>' part of <char>)
                    if (email.charAt(pos) == '\\') {
                        userTemp.append('\\');
                        pos++;
                        // <x> ::= any one of the 128 ASCII characters (no exceptions)
                        char c = email.charAt(pos);
                        if (c < 0 || c > 128) {
                            throw new N2bbException("", "Email has an unallowed character at position " + (pos + 1));
                        }
                        userTemp.append(x);
                        pos++;
        */
        //          } else
        if (email.charAt(pos) == '@') {
          break;
        } else {
          // <c> ::= any one of the 128 ASCII characters, but not any <special> or <SP>
          // <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "."
          //      | "," | ";" | ":" | "@"  """ | the control
          //      characters (ASCII codes 0 through 31 inclusive and 127)
          // <SP> ::= the space character (ASCII code 32)
          char c = email.charAt(pos);
          if (c <= 31 || c == 127 || c == ' ') {
            throw new N2bbException("error.email.invalidUnQuote");
          }

          for (int i = 0; i < specialChars.length; i++) {
            if (c == specialChars[i]) {
              throw new N2bbException("error.email.invalidUnQuote");
            }
          }
          userTemp.append(c);
          pos++;
        }
      }

      if (email.charAt(email.length() - 1) == '.')
        throw new N2bbException("error.email.userNameDot");

    }
    catch (N2bbException e) {
      n2bbLog.debug("n2bb exception - message... " + e.getMessage());
      throw e;
    }
    catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.email.validationFailed");
    }

    return userTemp.toString();
  }

  private String validateDomain(String email) throws N2bbException {

    StringBuffer domainTemp = new StringBuffer();
    String domain = "";
    // <name> ::= <a> <ldh-str> <let-dig>
    // <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
    // <let-dig> ::= <a> | <d>
    // <let-dig-hyp> ::= <a> | <d> | "-"
    // <a> ::= any one of the 52 alphabetic characters A through Z
    //  in upper case and a through z in lower case
    // <d> ::= any one of the ten digits 0 through 9
    try {
      while (true) {
        char c = email.charAt(pos);
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-')) {
          domainTemp.append(c);
          pos++;
          continue;
        } else if (c == '.') {
          // bug 1253 - allow subdomains
          if (email.charAt(pos - 1) == '.')
            throw new N2bbException("error.email.consecutiveDots");

          if (email.indexOf(".", pos + 1) == -1)// have reached the last dot
            break;

          domainTemp.append(c);
          pos++;
          continue;
          // end bug 1253
          
          //break;
        } else
          throw new N2bbException("error.email.invalidDomain");
      }

      domain = domainTemp.toString();
      if (domain.startsWith("-") || domain.endsWith("-")) {
        throw new N2bbException("error.email.domainHyphen");
      }
    } catch (N2bbException e) {
      n2bbLog.debug("n2bb exception - message... " + e.getMessage());
      throw e;
    }
    catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.email.validationFailed");
    }

    return domain;
  }

  private String validateExtension(String email) throws N2bbException {

    StringBuffer extTemp = new StringBuffer();
    String ext = "";

    // <a> ::= any one of the 52 alphabetic characters A through Z
    //  in upper case and a through z in lower case
    try {
      while (true) {
        if (pos == email.length())
          break;
        char c = email.charAt(pos);
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
          extTemp.append(c);
          pos++;
          continue;
        } else
          throw new N2bbException("error.email.invalidExt");
      }

      ext = extTemp.toString();

    } catch (N2bbException e) {
      n2bbLog.debug("n2bb exception - message... " + e.getMessage());
      throw e;
    }
    catch (Exception e) {
      n2bbLog.error(e.getMessage(), e);
      throw new N2bbException("error.email.validationFailed");
    }

    return ext;
  }


}

