#include "StdAfx.h" #include "QuotesProviderGoogle.h" #include "resource.h" #include "HTTPSession.h" #include "Log.h" #include "DBUtils.h" #include "EconomicRateInfo.h" #include "ModuleInfo.h" #include "QuotesProviders.h" #include "IHTMLParser.h" #include "IHTMLEngine.h" #include "CommonOptionDlg.h" #include "QuotesProviderVisitor.h" CQuotesProviderGoogle::CQuotesProviderGoogle() { } CQuotesProviderGoogle::~CQuotesProviderGoogle() { } namespace { inline tstring make_contact_name(const tstring& rsSymbolFrom,const tstring& rsSymbolTo) { tostringstream o; o << rsSymbolFrom << _T("/") << rsSymbolTo; return o.str(); } inline bool is_rate_watched(HANDLE hContact, const CQuotesProviderBase::CQuote& from, const CQuotesProviderBase::CQuote& to) { tstring sFrom = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_ID); tstring sTo = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_ID); return ((0 == quotes_stricmp(from.GetID().c_str(),sFrom.c_str())) && (0 == quotes_stricmp(to.GetID().c_str(),sTo.c_str()))); } } bool CQuotesProviderGoogle::WatchForRate(const CRateInfo& ri, bool bWatch) { TContracts::const_iterator i = std::find_if(m_aContacts.begin(),m_aContacts.end(), boost::bind(is_rate_watched,_1,ri.m_from,ri.m_to)); if((true == bWatch) && (i == m_aContacts.end())) { tstring sName = make_contact_name(ri.m_from.GetSymbol(),ri.m_to.GetSymbol()); HANDLE hContact = CreateNewContact(sName); if(hContact) { DBWriteContactSettingTString(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_ID,ri.m_from.GetID().c_str()); DBWriteContactSettingTString(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_ID,ri.m_to.GetID().c_str()); if(false == ri.m_from.GetName().empty()) { DBWriteContactSettingTString(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_DESCRIPTION,ri.m_from.GetName().c_str()); } if(false == ri.m_to.GetName().empty()) { DBWriteContactSettingTString(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_DESCRIPTION,ri.m_to.GetName().c_str()); } return true; } } else if((false == bWatch) && (i != m_aContacts.end())) { HANDLE hContact = *i; {// for CCritSection CGuard cs(m_cs); m_aContacts.erase(i); } CallService(MS_DB_CONTACT_DELETE,reinterpret_cast(hContact),0); return true; } return false; } size_t CQuotesProviderGoogle::GetWatchedRateCount()const { return m_aContacts.size(); } bool CQuotesProviderGoogle::GetWatchedRateInfo(size_t nIndex,CRateInfo& rRateInfo) { if(nIndex < m_aContacts.size()) { HANDLE hContact = m_aContacts[nIndex]; tstring sSymbolFrom = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_ID); tstring sSymbolTo = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_ID); tstring sDescFrom = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_DESCRIPTION); tstring sDescTo = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_DESCRIPTION); rRateInfo.m_from = CQuote(sSymbolFrom,sSymbolFrom,sDescFrom); rRateInfo.m_to = CQuote(sSymbolTo,sSymbolTo,sDescTo); return true; } else { return false; } } namespace { tstring build_url(const tstring& rsURL,const tstring& from,const tstring& to,double dAmount) { tostringstream o; o << rsURL << _T("?a=") << std::fixed << dAmount << _T("&from=") << from << _T("&to=") << to; return o.str(); } tstring build_url(HANDLE hContact,const tstring& rsURL,double dAmount = 1.0) { tstring sFrom = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_ID); tstring sTo = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_ID); return build_url(rsURL,sFrom,sTo,dAmount); } typedef IHTMLNode::THTMLNodePtr THTMLNodePtr; bool parse_html_node(const THTMLNodePtr& pNode,double& rdRate) { tstring sID = pNode->GetAttribute(_T("id")); if((false == sID.empty()) && (0 == quotes_stricmp(sID.c_str(),_T("currency_converter_result")))) { size_t cChild = pNode->GetChildCount(); // assert(1 == cChild); if(cChild > 0) { THTMLNodePtr pChild = pNode->GetChildPtr(0); tstring sRate = pChild->GetText(); tistringstream input(sRate); input >> rdRate; return ((false == input.bad()) && (false == input.fail())); } } else { size_t cChild = pNode->GetChildCount(); for(size_t i = 0;i < cChild;++i) { THTMLNodePtr pChild = pNode->GetChildPtr(i); if(pChild && (true == parse_html_node(pChild,rdRate))) { return true; } } } return false; } bool parse_responce(const tstring& rsHTML,double& rdRate) { IHTMLEngine::THTMLParserPtr pHTMLParser = CModuleInfo::GetHTMLEngine()->GetParserPtr(); THTMLNodePtr pRoot = pHTMLParser->ParseString(rsHTML); if(pRoot) { return parse_html_node(pRoot,rdRate); } else { return false; } } } void CQuotesProviderGoogle::RefreshQuotes(TContracts& anContacts) { CHTTPSession http; tstring sURL = GetURL(); bool bUseExtendedStatus = CModuleInfo::GetInstance().GetExtendedStatusFlag(); for(TContracts::const_iterator i = anContacts.begin();i != anContacts.end() && IsOnline();++i) { HANDLE hContact = *i; if(bUseExtendedStatus) { SetContactStatus(hContact,ID_STATUS_OCCUPIED); } tstring sFullURL = build_url(hContact,sURL); // LogIt(Info,sFullURL); if((true == http.OpenURL(sFullURL)) && (true == IsOnline())) { tstring sHTML; if((true == http.ReadResponce(sHTML)) && (true == IsOnline())) { // LogIt(Info,sHTML); double dRate = 0.0; if((true == parse_responce(sHTML,dRate)) && (true == IsOnline())) { WriteContactRate(hContact,dRate); continue; } } } SetContactStatus(hContact,ID_STATUS_NA); } } namespace { inline tstring make_quote_name(const CQuotesProviderGoogle::CQuote& rQuote) { const tstring& rsDesc = rQuote.GetName(); return((false == rsDesc.empty()) ? rsDesc : rQuote.GetSymbol()); } CQuotesProviderGoogle* get_google_provider() { CModuleInfo::TQuotesProvidersPtr& pProviders = CModuleInfo::GetQuoteProvidersPtr(); const CQuotesProviders::TQuotesProviders& rapQuotesProviders = pProviders->GetProviders(); for(CQuotesProviders::TQuotesProviders::const_iterator i = rapQuotesProviders.begin();i != rapQuotesProviders.end();++i) { const CQuotesProviders::TQuotesProviderPtr& pProvider = *i; CQuotesProviderGoogle* pGoogle = dynamic_cast(pProvider.get()); if(pGoogle) { return pGoogle; } } assert(!"We should never get here!"); return NULL; } CQuotesProviderGoogle::CQuoteSection get_quotes() { const CQuotesProviderGoogle* pProvider = get_google_provider(); if(pProvider) { const CQuotesProviderGoogle::CQuoteSection& rQuotes = pProvider->GetQuotes(); if(rQuotes.GetSectionCount() > 0) { return rQuotes.GetSection(0); } } return CQuotesProviderGoogle::CQuoteSection(); } tstring make_rate_name(const CQuotesProviderGoogle::CQuote& rFrom, const CQuotesProviderGoogle::CQuote& rTo) { if((false == rFrom.GetName().empty()) && (false == rTo.GetName().empty())) { return make_contact_name(rFrom.GetName(),rTo.GetName()); } else { return make_contact_name(rFrom.GetSymbol(),rTo.GetSymbol()); } } typedef std::vector TWatchedRates; TWatchedRates g_aWatchedRates; bool is_equal_rate(const CQuotesProviderGoogle::CRateInfo& riL,const CQuotesProviderGoogle::CRateInfo& riR) { return ((0 == quotes_stricmp(riL.m_from.GetID().c_str(),riR.m_from.GetID().c_str())) && ((0 == quotes_stricmp(riL.m_to.GetID().c_str(),riR.m_to.GetID().c_str())))); } INT_PTR CALLBACK GoogleOptDlgProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam) { CQuotesProviderGoogle* pProvider = get_google_provider(); CCommonDlgProcData d(pProvider); CommonOptionDlgProc(hdlg,message,wParam,lParam,d); switch(message) { case WM_NOTIFY: { LPNMHDR pNMHDR = reinterpret_cast(lParam); switch(pNMHDR->code) { case PSN_APPLY: { if(pProvider) { TWatchedRates aTemp(g_aWatchedRates); TWatchedRates aRemove; size_t cWatchedRates = pProvider->GetWatchedRateCount(); for(size_t i = 0;i < cWatchedRates;++i) { CQuotesProviderGoogle::CRateInfo ri; if(true == pProvider->GetWatchedRateInfo(i,ri)) { TWatchedRates::iterator it = std::find_if(aTemp.begin(),aTemp.end(), boost::bind(is_equal_rate,_1,boost::cref(ri))); if(it == aTemp.end()) { aRemove.push_back(ri); } else { aTemp.erase(it); } } } std::for_each(aRemove.begin(),aRemove.end(),boost::bind(&CQuotesProviderGoogle::WatchForRate,pProvider,_1,false)); std::for_each(aTemp.begin(),aTemp.end(),boost::bind(&CQuotesProviderGoogle::WatchForRate,pProvider,_1,true)); pProvider->RefreshAll(); } } break; } } break; case WM_INITDIALOG: { TranslateDialogDefault(hdlg); g_aWatchedRates.clear(); HWND hcbxFrom = ::GetDlgItem(hdlg,IDC_COMBO_CONVERT_FROM); HWND hcbxTo = ::GetDlgItem(hdlg,IDC_COMBO_CONVERT_INTO); const CQuotesProviderGoogle::CQuoteSection& rSection = get_quotes(); size_t cQuotes = rSection.GetQuoteCount(); for(size_t i = 0;i < cQuotes;++i) { const CQuotesProviderGoogle::CQuote& rQuote = rSection.GetQuote(i); tstring sName = make_quote_name(rQuote); LPCTSTR pszName = sName.c_str(); ::SendMessage(hcbxFrom,CB_ADDSTRING,0,reinterpret_cast(pszName)); ::SendMessage(hcbxTo,CB_ADDSTRING,0,reinterpret_cast(pszName)); } CQuotesProviderGoogle* pProvider = get_google_provider(); if(pProvider) { size_t cWatchedRates = pProvider->GetWatchedRateCount(); for(size_t i = 0;i < cWatchedRates;++i) { CQuotesProviderGoogle::CRateInfo ri; if(true == pProvider->GetWatchedRateInfo(i,ri)) { g_aWatchedRates.push_back(ri); tstring sRate = make_rate_name(ri.m_from,ri.m_to); LPCTSTR pszRateName = sRate.c_str(); ::SendMessage(::GetDlgItem(hdlg,IDC_LIST_RATES),LB_ADDSTRING,0,reinterpret_cast(pszRateName)); } } } ::EnableWindow(::GetDlgItem(hdlg,IDC_BUTTON_ADD),FALSE); ::EnableWindow(::GetDlgItem(hdlg,IDC_BUTTON_REMOVE),FALSE); } return TRUE; case WM_COMMAND: switch(HIWORD(wParam)) { case CBN_SELCHANGE: switch(LOWORD(wParam)) { case IDC_COMBO_REFRESH_RATE: break; case IDC_COMBO_CONVERT_FROM: case IDC_COMBO_CONVERT_INTO: { int nFrom = static_cast(::SendMessage(::GetDlgItem(hdlg,IDC_COMBO_CONVERT_FROM),CB_GETCURSEL,0,0)); int nTo = static_cast(::SendMessage(::GetDlgItem(hdlg,IDC_COMBO_CONVERT_INTO),CB_GETCURSEL,0,0)); bool bEnableAddButton = ((CB_ERR != nFrom) && (CB_ERR != nTo) && (nFrom != nTo)); EnableWindow(GetDlgItem(hdlg,IDC_BUTTON_ADD),bEnableAddButton); } break; case IDC_LIST_RATES: { int nSel = ::SendMessage(::GetDlgItem(hdlg,IDC_LIST_RATES),LB_GETCURSEL,0,0); ::EnableWindow(::GetDlgItem(hdlg,IDC_BUTTON_REMOVE),(LB_ERR != nSel)); } break; } break; case BN_CLICKED: switch(LOWORD(wParam)) { case IDC_BUTTON_ADD: { size_t nFrom = static_cast(::SendMessage(::GetDlgItem(hdlg,IDC_COMBO_CONVERT_FROM),CB_GETCURSEL,0,0)); size_t nTo = static_cast(::SendMessage(::GetDlgItem(hdlg,IDC_COMBO_CONVERT_INTO),CB_GETCURSEL,0,0)); if((CB_ERR != nFrom) && (CB_ERR != nTo) && (nFrom != nTo)) { const CQuotesProviderGoogle::CQuoteSection& rSection = get_quotes(); size_t cQuotes = rSection.GetQuoteCount(); if((nFrom < cQuotes) && (nTo < cQuotes)) { CQuotesProviderGoogle::CRateInfo ri; ri.m_from = rSection.GetQuote(nFrom); ri.m_to = rSection.GetQuote(nTo); g_aWatchedRates.push_back(ri); tstring sRate = make_rate_name(ri.m_from,ri.m_to); LPCTSTR pszRateName = sRate.c_str(); ::SendMessage(::GetDlgItem(hdlg,IDC_LIST_RATES),LB_ADDSTRING,0,reinterpret_cast(pszRateName)); PropSheet_Changed(::GetParent(hdlg),hdlg); } } } break; case IDC_BUTTON_REMOVE: { HWND hWnd = ::GetDlgItem(hdlg,IDC_LIST_RATES); int nSel = ::SendMessage(hWnd,LB_GETCURSEL,0,0); if(LB_ERR != nSel) { if((LB_ERR != ::SendMessage(hWnd, LB_DELETESTRING,nSel,0)) && (nSel < static_cast(g_aWatchedRates.size()))) { TWatchedRates::iterator i = g_aWatchedRates.begin(); std::advance(i,nSel); g_aWatchedRates.erase(i); PropSheet_Changed(::GetParent(hdlg),hdlg); } } nSel = ::SendMessage(hWnd,LB_GETCURSEL,0,0); ::EnableWindow(::GetDlgItem(hdlg,IDC_BUTTON_REMOVE),(LB_ERR != nSel)); } break; } break; // case LBN_SELCHANGE: // switch(LOWORD(lParam)) // { // case IDC_LIST_RATES: // { // int nSel = ::SendMessage(::GetDlgItem(hdlg,IDC_LIST_RATES),LB_GETCURSEL,0,0); // ::EnableWindow(::GetDlgItem(hdlg,IDC_BUTTON_REMOVE),(-1 != nSel)); // } // } // break; } break; } return FALSE; } } void CQuotesProviderGoogle::ShowPropertyPage(WPARAM wp,OPTIONSDIALOGPAGE& odp) { odp.pszTemplate = MAKEINTRESOURCEA(IDD_DIALOG_OPT_GOOGLE); odp.pfnDlgProc = GoogleOptDlgProc; // #if MIRANDA_VER >= 0x0600 //odp.ptszTab = TranslateTS(const_cast(GetInfo().m_sName.c_str())); odp.ptszTab = const_cast(GetInfo().m_sName.c_str()); // #else // tostringstream o; // o << TranslateTS(QUOTES_PROTOCOL_NAME) << _T(" - ") << TranslateTS(GetInfo().m_sName.c_str()); // tstring sTitle = o.str(); // odp.ptszTitle = TranslateTS(const_cast(sTitle.c_str())); // #endif CallService(MS_OPT_ADDPAGE,wp,reinterpret_cast(&odp)); } void CQuotesProviderGoogle::Accept(CQuotesProviderVisitor& visitor)const { CQuotesProviderBase::Accept(visitor); visitor.Visit(*this); } double CQuotesProviderGoogle::Convert(double dAmount,const CQuote& from,const CQuote& to)const { tstring sFullURL = build_url(GetURL(),from.GetID(),to.GetID(),dAmount); // LogIt(Info,sFullURL); CHTTPSession http; if((true == http.OpenURL(sFullURL))) { tstring sHTML; if((true == http.ReadResponce(sHTML))) { // LogIt(Info,sHTML); double dResult = 0.0; if((true == parse_responce(sHTML,dResult))) { return dResult; } else { throw std::runtime_error(Translate("Error occurred during html parsing.")); } } else { throw std::runtime_error(Translate("Error occurred during site access.")); } } else { throw std::runtime_error(Translate("Error occurred during site access.")); } return 0.0; } namespace { bool is_equal_ids(HANDLE hContact,const tstring& rsFromID,const tstring& rsToID) { tstring sFrom = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_FROM_ID); tstring sTo = Quotes_DBGetStringT(hContact,QUOTES_PROTOCOL_NAME,DB_STR_TO_ID); return ((0 == quotes_stricmp(rsFromID.c_str(),sFrom.c_str())) && (0 == quotes_stricmp(rsToID.c_str(),sTo.c_str()))); } } HANDLE CQuotesProviderGoogle::GetContactByID(const tstring& rsFromID,const tstring& rsToID)const { CGuard cs(m_cs); TContracts::const_iterator i = std::find_if(m_aContacts.begin(),m_aContacts.end(), boost::bind(is_equal_ids,_1,boost::cref(rsFromID),boost::cref(rsToID))); if(i != m_aContacts.end()) { return *i; } else { return NULL; } }