package com.bao.lc.site.s3.commands;
import java.io.FileOutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import org.apache.commons.chain.Context;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.Text;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.filters.StringFilter;
import org.htmlparser.tags.Div;
import org.htmlparser.tags.FormTag;
import org.htmlparser.tags.ScriptTag;
import org.htmlparser.tags.Span;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import com.bao.lc.AppConfig;
import com.bao.lc.ResMgr;
import com.bao.lc.bean.IDValuePair;
import com.bao.lc.bean.ResultCode;
import com.bao.lc.client.utils.HttpClientUtils;
import com.bao.lc.common.ScriptCodeFilter;
import com.bao.lc.common.URI2NameBuilder;
import com.bao.lc.httpcommand.BasicHttpCommand;
import com.bao.lc.httpcommand.params.HttpCommandParams;
import com.bao.lc.site.s3.params.TdPNames;
import com.bao.lc.site.s3.params.TdParams;
import com.bao.lc.util.AppUtils;
import com.bao.lc.util.MiscUtils;
public class DoLogin extends BasicHttpCommand
{
private static Log log = LogFactory.getLog(DoLogin.class);
private static final String JS_ERROR_MSG_REGEX = "var message = \"(.+?)\";";
private static final String JS_LOGIN_REGEX = "var isLogin(.*?)=(.+?)var u_name = '(.*?)';";
private String loginName = null;
@Override
protected IDValuePair postExecute(Context context) throws Exception
{
HttpResponse rsp = HttpCommandParams.getResponse(context);
String charset = HttpCommandParams.getCharset(rsp, context);
// Login page content
URI2NameBuilder ub = new URI2NameBuilder();
ub.uri(HttpCommandParams.getTargetRequestURI(context));
ub.addParamName("method").encoding(charset);
String userPage = HttpClientUtils.saveToString(rsp.getEntity(), charset, ub);
context.put(TdPNames._LOGIN_PAGE_CONTENT, userPage);
context.put(TdPNames._LOGIN_PAGE_ENCODING, charset);
// For manual check
IOUtils.write(userPage, new FileOutputStream(AppUtils.getTempFilePath("login.html")),
charset);
String user = MapUtils.getString(context, TdPNames.PARAM_USER);
// Login OK
if(isLogin(context, userPage, charset))
{
context.put(TdPNames._LOGIN_STATE, Boolean.TRUE);
String message = MessageFormat.format(ResMgr.getString("td.msg.user.login.ok"), user, loginName);
TdParams.getUI(context).info(message);
log.info("User [" + user + "] login successfully.");
return ResultCode.RC_OK;
}
// Login Failed
else
{
context.put(TdPNames._LOGIN_STATE, Boolean.FALSE);
log.info("User [" + user + "] login failed.");
//check failed reason.
IDValuePair rc = parseFailReason(context, userPage, charset);
return rc;
}
}
private boolean isLogin(Context context, String pageContent, String charset)
throws ParserException
{
boolean ret = false;
do
{
if(!isLoginByJS(context, pageContent, charset))
{
log.info("isLoginByJS failed");
break;
}
if(!isLoginByMenu(context, pageContent, charset))
{
log.info("isLoginByMenu failed");
break;
}
if(!isLoginByWelcomeMsg(context, pageContent, charset))
{
log.info("isLoginByWelcomeMsg failed");
break;
}
ret = true;
}
while(false);
return ret;
}
private boolean isLoginByJS(Context context, String pageContent, String charset)
throws ParserException
{
Parser parser = MiscUtils.createParser(pageContent, charset, log);
int flags = Pattern.MULTILINE | Pattern.DOTALL;
// 1. login form filters
NodeFilter scriptFilter = new ScriptCodeFilter(JS_LOGIN_REGEX, flags);
// parse
NodeList nodeList = parser.parse(scriptFilter);
if(nodeList.size() < 1)
{
log.info("Can't find login Javascript code. Assume login failed.");
return false;
}
ScriptTag js = (ScriptTag) nodeList.elementAt(0);
List<String> valueList = new ArrayList<String>();
int matchCount = MiscUtils.getRegexValue(js.getScriptCode().trim(), JS_LOGIN_REGEX,
valueList, true, flags);
if(matchCount != 1)
{
log.fatal("Unexpected: matchCount=" + matchCount);
return false;
}
String loginStatus = valueList.get(2).trim();
loginName = valueList.get(3).trim();
log.info("Javascript code: loginStatus=" + loginStatus + ", loginName=" + loginName);
if(StringUtils.equalsIgnoreCase(loginStatus, "true") && !loginName.isEmpty())
{
return true;
}
return false;
}
private boolean isLoginByMenu(Context context, String pageContent, String charset)
throws ParserException
{
Parser parser = MiscUtils.createParser(pageContent, charset, log);
// Set filters
List<NodeFilter> predicates = new ArrayList<NodeFilter>(3);
NodeFilter[] a = new NodeFilter[0];
predicates.add(new HasAttributeFilter("id", "left_down_z"));
predicates.add(new HasAttributeFilter("class", "left_down_z"));
predicates.add(new NodeClassFilter(Div.class));
NodeFilter filter = new AndFilter(predicates.toArray(a));
predicates.clear();
NodeList nodeList = parser.parse(filter);
if(nodeList.size() <= 0)
{
log.info("Can't find the DIV with id=left_down_z. Assume login failed.");
return false;
}
Div div = (Div) nodeList.elementAt(0);
if(div.getChildren() == null)
{
log.info("Can't find any children by the DIV.");
return false;
}
nodeList = div.getChildren();
predicates.add(new StringFilter(AppConfig.getInstance()
.getPropInternal("td.login.ok.menu1"), false, Locale.SIMPLIFIED_CHINESE));
predicates.add(new StringFilter(AppConfig.getInstance()
.getPropInternal("td.login.ok.menu2"), false, Locale.SIMPLIFIED_CHINESE));
predicates.add(new StringFilter(AppConfig.getInstance()
.getPropInternal("td.login.ok.menu3"), false, Locale.SIMPLIFIED_CHINESE));
int expectedMenuCount = predicates.size();
filter = new OrFilter(predicates.toArray(a));
predicates.clear();
NodeList menuList = nodeList.extractAllNodesThatMatch(filter, true);
log.info("Found menu count: " + menuList.size() + ", expected: " + expectedMenuCount);
if(menuList.size() < expectedMenuCount)
{
return false;
}
return true;
}
private boolean isLoginByWelcomeMsg(Context context, String pageContent, String charset)
throws ParserException
{
Parser parser = MiscUtils.createParser(pageContent, charset, log);
NodeFilter filter = new StringFilter(AppConfig.getInstance().getPropInternal(
"td.login.ok.msg1"), false, Locale.SIMPLIFIED_CHINESE);
NodeList msgList = parser.parse(filter);
log.info("Found message count: " + msgList.size());
if(msgList.size() <= 0)
{
return false;
}
Text msgText = (Text) msgList.elementAt(0);
String lastLoginMsg = msgText.getText();
TdParams.getUI(context).info(lastLoginMsg);
log.info("The welcome message: " + lastLoginMsg);
return true;
}
private IDValuePair parseFailReason(Context context, String pageContent, String charset) throws ParserException
{
Parser parser = MiscUtils.createParser(pageContent, charset, log);
// Set filters
List<NodeFilter> predicates = new ArrayList<NodeFilter>(3);
NodeFilter[] a = new NodeFilter[0];
// 1. login form filters
predicates.add(new HasAttributeFilter("id", "loginForm"));
predicates.add(new HasAttributeFilter("name", "loginForm"));
predicates.add(new NodeClassFilter(FormTag.class));
NodeFilter loginFormFilter = new AndFilter(predicates.toArray(a));
// 2. verification code error filters
predicates.clear();
predicates.add(new HasAttributeFilter("id", "randErr"));
predicates.add(new NodeClassFilter(Span.class));
NodeFilter vCodeErrorFilter = new AndFilter(predicates.toArray(a));
// 3. error message from java script code
NodeFilter scriptFilter = new ScriptCodeFilter(JS_ERROR_MSG_REGEX);
// Final filter
predicates.clear();
predicates.add(loginFormFilter);
predicates.add(vCodeErrorFilter);
predicates.add(scriptFilter);
NodeFilter finalFilter = new OrFilter(predicates.toArray(a));
// parse
NodeList nodeList = parser.parse(finalFilter);
FormTag loginForm = null;
Span vCodeSpan = null;
ScriptTag scriptErrorMsg = null;
for(int i = 0, size = nodeList.size(); i < size; i++)
{
Node node = nodeList.elementAt(i);
if(node instanceof FormTag)
{
loginForm = (FormTag)node;
}
else if(node instanceof Span)
{
vCodeSpan = (Span)node;
}
else if(node instanceof ScriptTag)
{
scriptErrorMsg = (ScriptTag)node;
}
}
IDValuePair rc = ResultCode.RC_TD_LOGIN_UNKOWN_ERROR;
String outErrorMsg = null;
do
{
//RC_TD_LOGIN_VCODE_ERROR
if(vCodeSpan != null)
{
rc = ResultCode.RC_TD_LOGIN_VCODE_ERROR;
outErrorMsg = vCodeSpan.toPlainTextString().trim();
break;
}
//Message error
if(scriptErrorMsg != null)
{
String srcCode = scriptErrorMsg.getScriptCode();
List<String> valueList = new ArrayList<String>();
int matchCount = MiscUtils.getRegexValue(srcCode, JS_ERROR_MSG_REGEX, valueList, true, 0);
if(matchCount > 0)
{
outErrorMsg = valueList.get(1);
}
if(outErrorMsg != null)
{
if(outErrorMsg.contains(AppConfig.getInstance().getPropInternal("td.login.nok.account")))
{
rc = ResultCode.RC_TD_LOGIN_ACCOUNT_ERROR;
}
else if(outErrorMsg.contains(AppConfig.getInstance().getPropInternal("td.login.nok.password")))
{
rc = ResultCode.RC_TD_LOGIN_PASSWORD_ERROR;
}
else
{
rc = ResultCode.RC_TD_LOGIN_OTHER_MSG_ERROR;
}
break;
}
}
if(loginForm == null)
{
log.error("Can't find the login form, error reason unkown.");
rc = ResultCode.RC_TD_LOGIN_NO_SUBMIT_FORM_ERROR;
}
outErrorMsg = "Unkown error.";
}
while(false);
String failMsg = "[" + outErrorMsg + "], rc=" + rc;
String message = MessageFormat.format(ResMgr.getString("td.msg.user.login.failed"),
MapUtils.getString(context, TdPNames.PARAM_USER), failMsg);
TdParams.getUI(context).error(message);
return rc;
}
}