📄 dominohandler.cpp
字号:
// DominoHandler.cpp: implementation of the CDominoHandler class.
//////////////////////////////////////////////////////////////////////
/*
TODO:
如果服务器两项配置如下,可能会导致请求服务配置时输出UTF8的XML文档,而服务代理里输出的为GB2312,从而导致“没有可用服务”错误。
1、“Domino Web引擎”中“语言”-“缺省字符串资源语言”配置为“中文(简体)”
2、“Domino Web引擎”中“字符集”-“使用 UTF-8 输出”配置为“是”
测试-使用某用户通过IE登录系统后,直接访问地址:
http://xxxx/doDominoMinder.nsf/MinderService?OpenAgent&ver=2.002&Action=getservicecfg
*/
#include "stdafx.h"
#include "DominoMinder.h"
#include "DominoHandler.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// CDInternetSession
//////////////////////////////////////////////////////////////////////
CDInternetSession::CDInternetSession(LPCTSTR pszAppName, int nMethod)
: CInternetSession(pszAppName, 1, nMethod)
{
this->SetOption( INTERNET_OPTION_CONNECT_TIMEOUT, 1000*30 ); //好像无效:(
this->SetOption( INTERNET_OPTION_CONNECT_BACKOFF, 1000 );
this->SetOption( INTERNET_OPTION_CONNECT_RETRIES, 2 );
this->EnableStatusCallback(TRUE);
}
void CDInternetSession::OnStatusCallback(
DWORD dwContext, // 1
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLen )
{
//if (!bProgressMode) return;
//TODO: 两次 INTERNET_STATUS_HANDLE_CREATED?
char szStatus[128]; memset(szStatus,0,sizeof(szStatus));
switch ( dwInternetStatus )
{
case INTERNET_STATUS_RESOLVING_NAME:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_RESOLVING_NAME | %s\0", lpvStatusInformation );
break;
case INTERNET_STATUS_NAME_RESOLVED:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_NAME_RESOLVED | %s\0", lpvStatusInformation );
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_CONNECTING_TO_SERVER | %s\0", lpvStatusInformation );
break;
case INTERNET_STATUS_CONNECTED_TO_SERVER:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_CONNECTED_TO_SERVER | %s\0", lpvStatusInformation );
break;
case INTERNET_STATUS_SENDING_REQUEST:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_CONNECTED_TO_SERVER\0" );
break;
case INTERNET_STATUS_REQUEST_SENT:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_REQUEST_SENT\0" );
break;
case INTERNET_STATUS_RECEIVING_RESPONSE:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_RECEIVING_RESPONSE\0" );
break;
case INTERNET_STATUS_RESPONSE_RECEIVED:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_RESPONSE_RECEIVED\0" );
break;
case INTERNET_STATUS_CLOSING_CONNECTION:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_CLOSING_CONNECTION\0" );
break;
case INTERNET_STATUS_CONNECTION_CLOSED:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_CONNECTION_CLOSED\0" );
break;
case INTERNET_STATUS_HANDLE_CREATED:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_HANDLE_CREATED\0" );
break;
case INTERNET_STATUS_HANDLE_CLOSING:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_HANDLE_CLOSING\0" );
break;
case INTERNET_STATUS_COOKIE_SENT:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_COOKIE_SENT\0" );
break;
case INTERNET_STATUS_COOKIE_RECEIVED:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_COOKIE_RECEIVED\0" );
break;
case INTERNET_STATUS_REDIRECT:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_REDIRECT | %s\0", lpvStatusInformation );
break;
case INTERNET_STATUS_COOKIE_HISTORY:
sprintf( szStatus, "InternetStatus = INTERNET_STATUS_COOKIE_HISTORY\0" );
break;
default:
sprintf( szStatus, "InternetStatus = %d\0", dwInternetStatus );
}
#ifdef _DEBUG
if ( (dwInternetStatus==INTERNET_STATUS_REDIRECT) && (strlen(szStatus) > 0) ) {
CLog::WriteLine( szStatus );
}
#endif
}
/////////////////////////////////////////////////////////////////////
// CHttpRequestThread
/////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CHttpRequestThread, CWinThread);
CHttpRequestThread::~CHttpRequestThread()
{
//MessageBox(NULL, "CHttpRequestThread::~CHttpRequestThread", "", MB_OK);
m_pParm->out_bEnd = true;
}
//设置请求参数
void CHttpRequestThread::SetParm( HTTP_REQ_THREAD_PARM* pParm ){
//MessageBox(NULL, "CHttpRequestThread::SetParm", "", MB_OK);
m_pParm = pParm;
m_pParm->out_bEnd = false;
m_pParm->out_sRet = "";
m_pParm->out_nRet = 0;
m_pParm->out_bErr = false;
}
//发送请求
void CHttpRequestThread::sendRequest()
{
//MessageBox(NULL, "CHttpRequestThread::sendRequest", "", MB_OK);
CHttpFile* pHttpFile = NULL;
//连接服务器并发送请求
try {
if ( m_pParm->nHttpVerb == CHttpConnection::HTTP_VERB_POST ) {
pHttpFile = m_pParm->pHttpConnection->OpenRequest( m_pParm->nHttpVerb, m_pParm->sUrl );
pHttpFile->SendRequestEx( m_pParm->sPostData.GetLength() );
pHttpFile->WriteString( m_pParm->sPostData );
pHttpFile->EndRequest();
//CString strHeaders = "Content-Type: application/x-www-form-urlencoded";
//CString strHeaders = ""; //"Accept-Charset: GB2312";
//pHttpFile->SendRequest( strHeaders, (LPVOID)(LPCTSTR)(m_pParm->sPostData), sizeof(m_pParm->sPostData) );
} else {
pHttpFile = m_pParm->pHttpConnection->OpenRequest( m_pParm->nHttpVerb, m_pParm->sUrl );
pHttpFile->SendRequest();
}
} catch ( ... ) {
//MessageBox(NULL, "send request err", "", MB_OK );
}
//获得服务器返回代码
DWORD dwRet;
pHttpFile->QueryInfoStatusCode( dwRet );
m_pParm->out_nRet = dwRet;
//获得服务器返回内容
if ( dwRet == HTTP_STATUS_OK ) {
CString strData;
CString strDataFull = "";
while ( pHttpFile->ReadString(strData) ) {
strDataFull += strData;
}
//解析返回结果
IXMLDOMDocument *pXMLDoc;
VARIANT_BOOL bResult;
if(SUCCEEDED(CoInitialize(NULL)) && SUCCEEDED(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXMLDoc))) {
if(FAILED(pXMLDoc->loadXML( (_bstr_t)strDataFull, &bResult))) {
m_pParm->out_bErr = true;
m_pParm->out_sRet = "";
} else {
IXMLDOMElement* base;
pXMLDoc->get_documentElement(&base);
if ( base == NULL ) {
m_pParm->out_bErr = true;
m_pParm->out_sRet = "";
} else {
//如果有错误则解析错误
wchar_t* szName;
base->get_baseName( &szName );
if ( _bstr_t(szName) == _bstr_t("error") ) {
BSTR szErrMsg = NULL;
base->get_text(&szErrMsg); //DONE: 解码错误,中文都是“??” - servlet
m_pParm->out_sRet = szErrMsg;
m_pParm->out_bErr = true;
}
//否则返回Servlet返回的XML字符串
else {
m_pParm->out_sRet = strDataFull;
m_pParm->out_bErr = false;
}
//
base->Release();
}
//
pXMLDoc->Release();
}
}
} else {
m_pParm->out_bErr = true;
pHttpFile->QueryInfo( HTTP_QUERY_STATUS_TEXT, m_pParm->out_sRet );
//DONE: 如何获得Servlet使用sendError返回的内容? - 没办法!!
}
pHttpFile->Close();
#ifdef _DEBUG
CLog::WriteLine( dwRet, "HTTP_STATUS" );
CLog::WriteLine( m_pParm->out_sRet );
#else
if ( m_pParm->out_bErr && m_pParm->out_sRet!="" ) {
CLog::WriteLine( dwRet, "HTTP_STATUS" );
CLog::WriteLine( m_pParm->out_sRet );
CLog::WriteLine( "err" );
}
#endif
}
//发送请求
BOOL CHttpRequestThread::InitInstance() {
//MessageBox(NULL, "CHttpRequestThread::InitInstance", "", MB_OK);
sendRequest();
//avoid entering standard message loop by returning FALSE
return FALSE;
}
//////////////////////////////////////////////////////////////////////
// CDominoHandler
//////////////////////////////////////////////////////////////////////
CDominoHandler::CDominoHandler(CString strVersion, CString strServerName, CString strUserName, CString strPassword )
{
//设置服务器名和端口
int nIdx = strServerName.Find(":");
if ( nIdx == -1 ) {
m_strServerName = strServerName;
m_nPort = INTERNET_DEFAULT_HTTP_PORT;
} else {
m_strServerName = strServerName.Left( nIdx );
m_nPort = atoi( strServerName.Right( strServerName.GetLength() - nIdx -1 ) );
}
//初始化用户登陆名和密码
m_strUserName = strUserName;
m_strPassword = strPassword;
//初始化InternetSession
m_pInetSession = new CDInternetSession( NULL, 0 );
//初始化HttpConnection - try https
DWORD dwHttpRequestFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE
| INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_SECURE; // INTERNET_FLAG_EXISTING_CONNECT |
//m_pHttpConnection = m_pInetSession->GetHttpConnection( m_strServerName, dwHttpRequestFlags, m_nPort );
m_oReqParm.pHttpConnection = m_pInetSession->GetHttpConnection( m_strServerName, dwHttpRequestFlags, m_nPort );
//其它初始化
m_strServiceCfg = "";
m_nServiceCnt = -1;
this->ResetRequest();
//Domino服务器端Servlet的Class路径名 或 代理名
#ifdef DOMINO_SERVICE_AGENT
m_strDominoServicePath = DOMINO_AGENT_PATH + "&ver=" + strVersion;
#else
m_strDominoServicePath = DOMINO_SERVLET_PATH + "?ver=" + strVersion;
#endif
}
CDominoHandler::~CDominoHandler()
{
Logout();
m_pInetSession->Close();
m_pInetSession = NULL;
m_oReqParm.pHttpConnection = NULL;
}
//判断请求是否已完成
bool CDominoHandler::IsRequestFinished()
{
return m_oReqParm.out_bEnd;
}
//判断请求是否成功(有数据)
//TODO: 如果登录页面也是xml的呢? -- 暂不考虑
bool CDominoHandler::IsRequestSuccessed()
{
if ( m_oReqParm.out_bErr ) return false;
return true;
/*
if ( m_oReqParm.out_nRet == HTTP_STATUS_OK ) {
CString sRes = GetRequestResult();
if ( sRes.Find("<?xml", 0) == 0 ) {
return true;
}
}
return false;
*/
}
//发送请求失败的话(!IsRequestSuccessed),
//再判断是否是因为连接已超时==返回值为XML
bool CDominoHandler::IsSessionTimeout()
{
if ( m_oReqParm.out_bErr && m_oReqParm.out_nRet==HTTP_STATUS_OK && m_oReqParm.out_sRet=="" ) {
return true;
} else {
return false;
}
/*
CString sRes = GetRequestResult();
if ( sRes.Find("<?xml", 0) != 0 ) {
return false;
}
return true;
*/
}
//返回请求的返回值
CString CDominoHandler::GetRequestResult()
{
return m_oReqParm.out_sRet;
}
//获得服务器名
CString CDominoHandler::GetServerName()
{
CString sRet;
sRet.Format( "%s:%d", m_strServerName, m_nPort );
return sRet;
}
//从服务器取服务配置
//若bForceLogin==true,则失败的话不再重试
CString CDominoHandler::GetServiceCfg(bool bForceLogin)
{
#ifdef _DEBUG
if ( m_strServiceCfg == "" ) {
CLog::WriteLine( "请求服务配置:" + m_oReqParm.sUrl + ";服务器:"+ this->m_strServerName);
}
#endif
if ( m_strServiceCfg != "" ) {
return m_strServiceCfg;
} else {
m_oReqParm.nHttpVerb = CHttpConnection::HTTP_VERB_GET;
m_oReqParm.sPostData = "";
m_oReqParm.sUrl = m_strDominoServicePath + "&Action=getservicecfg";
sendRequest( true );
if ( IsRequestSuccessed() ) {
m_strServiceCfg = GetRequestResult();
m_nServiceCnt = parseService();
return m_strServiceCfg;
} else if ( !bForceLogin) { // IsSessionTimeout() &&
if ( Login() ) {
//防止进入死循环(用户名密码错误?),增加参数bForceLogin
return GetServiceCfg( true );
}
}
return "";
}
}
//采用Post方式登录Domino服务器
bool CDominoHandler::Login( CString strUserName, CString strPassword )
{
//退出先
Logout();
//重设服务请求计数器
ResetRequest();
//设置用户名/密码
if ( strUserName != "" ) m_strUserName = strUserName;
if ( strPassword != "" ) m_strPassword = strPassword;
//发送登陆请求
m_oReqParm.nHttpVerb = CHttpConnection::HTTP_VERB_POST;
m_oReqParm.sPostData = (
"%75%73%45%52%6E%41%6D%65=" + CURLEncode::URLEncode(m_strUserName) //usERnAme
+ "&%50%41%73%53%77%6F%52%64=" + CURLEncode::URLEncode(m_strPassword) //PAsSwoRd
+ "&%72%45%44%69%72%45%63%54%74%4F=" + CURLEncode::URLEncode(m_strDominoServicePath) //rEDirEcTtO
);
m_oReqParm.sUrl = "names.nsf?login";
//直接GET,过于容易泄漏用户/密码
//m_oReqParm.sUrl = "names.nsf?login&username=" + m_strUserName + "&password=" + m_strPassword + "&redirectto=" + DOMINO_SERVLET_PATH;
//m_oReqParm.nHttpVerb = CHttpConnection::HTTP_VERB_GET;
#ifdef _DEBUG
CLog::WriteLine( "用户登录:" + m_strUserName + ";服务器:"+ this->m_strServerName );
#endif
sendRequest( true );
//判断是否登陆成功
//if ( IsRequestSuccessed() ) CLog::WriteLine("login succ" + m_oReqParm.out_sRet);
return IsRequestSuccessed();
//m_oReqParm.sPostData = "Username=" + m_strUserName + "&password=" + m_strPassword + "&RedirectTo=" + DOMINO_SERVLET_PATH;
}
//注销
void CDominoHandler::Logout()
{
//有过返回结果,即访问过服务器,则发送注销登录命令
if ( GetRequestResult() != "" ) {
m_oReqParm.nHttpVerb = NULL;
m_oReqParm.sPostData = "";
m_oReqParm.sUrl = "names.nsf?logout";
#ifdef _DEBUG
CLog::WriteLine( "用户注销:" + m_strUserName + ";服务器:"+ this->m_strServerName );
#endif
sendRequest( true ); //若不等待线程结束,则可能造成退出时异常
}
}
//连接到服务器,请求服务结果,并解析后返回
CString CDominoHandler::GetServiceMsg( bool bForceLogin )
{
#ifdef _DEBUG
CLog::WriteLine( "请求服务:" + this->m_strServerName );
#endif
//初始化结果
m_rgstrServiceResultMsg[0] = "";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -