/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// // OmeaRequest.cpp : Implementation of COmeaRequest // // © JetBrains Inc, 2005 // Written by (H) Serge Baltic #include "StdAfx.h" #include "OmeaRequest.h" // COmeaRequest COmeaRequest::COmeaRequest() { m_nErrorCode = 0; m_pfnOnComplete = NULL; m_pfnOnError = NULL; m_bRunAsyncRequests = true; m_bAllowQueueing = true; } COmeaRequest::~COmeaRequest() { } void COmeaRequest::Start(CStringA sOmeaMethod, CStringA sOmeaData, DelegateOnComplete pfnOnComplete, DelegateOnError pfnOnError) { // Initialize the fields m_sOmeaMethod = sOmeaMethod; m_sOmeaData = sOmeaData; m_pfnOnComplete = pfnOnComplete; m_pfnOnError = pfnOnError; // Some cleanup m_nErrorCode = 0; // Start the request in asynchronous mode (parameters will be gotten via callbacks) InvokeServer(m_bRunAsyncRequests); } CStringA COmeaRequest::OnGetHostName() { return m_settings.GetProfileStringA(COmeaSettingStore::setOmeaRemotingHost); } int COmeaRequest::OnGetPort() { return m_settings.GetOmeaRemotingPortNumber(); } CStringA COmeaRequest::OnGetObjectName() { // Collect CStringA sObjectName; sObjectName.Format("/%s/%s/%s", m_settings.GetOmeaRemotingSecurityKey(), m_settings.GetProfileStringA(COmeaSettingStore::setOmeaRemotingFormatter), m_sOmeaMethod); return sObjectName; } CStringA COmeaRequest::OnGetContentType() { return "application/x-www-form-urlencoded"; } void COmeaRequest::OnLockObject() { AddRef(); } void COmeaRequest::OnUnlockObject() { Release(); // TODO: ensure it won't release prematurely } void COmeaRequest::OnComplete(XmlDocument xmlResponse) { ASSERT((m_pfnOnComplete != NULL) && "No callback defined"); if(m_pfnOnComplete != NULL) (this->*m_pfnOnComplete)(xmlResponse); } void COmeaRequest::OnError(CStringW sErrorMessage) { ASSERT((m_pfnOnError != NULL) && "No callback defined"); if(m_pfnOnError != NULL) (this->*m_pfnOnError)(sErrorMessage); } IStreamPtr COmeaRequest::OnPrepareDataToSend() { IStreamPtr oStream; // Create CHECK(CreateStreamOnHGlobal(NULL, TRUE, &oStream)); // Populate with data ULONG nWritten; COM_CHECK(oStream, Write((LPCSTR)m_sOmeaData, m_sOmeaData.GetLength(), &nWritten)); CHECK((int)nWritten == m_sOmeaData.GetLength() ? S_OK : E_FAIL); // Rewind to the beginning of data LARGE_INTEGER li; li.QuadPart = 0; oStream->Seek(li, STREAM_SEEK_SET, NULL); return oStream; } void COmeaRequest::OnValidateResponse(XmlDocument xmlResponse) { XmlElement xmlResult = xmlResponse->selectSingleNode(L"/result"); if(xmlResult == NULL) ThrowError(CJetIe::LoadString(IDS_OMEA_INVALID_FORMAT)); if((_bstr_t)xmlResult->getAttribute(L"status") != (_bstr_t)L"ok") { TRACE(L"Omea has reported an error in its response."); if((_bstr_t)xmlResult->getAttribute(L"status") == (_bstr_t)L"exception") { // Save the exception code as an error code TRACE(L"Omea has reported an error in its response, and the error has a status."); m_nErrorCode = (int)(_variant_t)xmlResponse->selectSingleNode(L"/result/struct[@name='exception']/int[@name='code']")->text; ThrowError(CJetIe::LoadString(IDS_OMEA_EXCEPTION) + L'\n' + (LPCWSTR)(_bstr_t)xmlResponse->selectSingleNode(L"/result/struct[@name='exception']/string[@name='message']")->text); } ThrowError(CJetIe::LoadString(IDS_OMEA_EXCEPTION)); } } bool COmeaRequest::OnWhetherCloseConnection() { return true; // Always close the connection } bool COmeaRequest::OnWhetherInvokeAgain() { return false; // No double-requests } void COmeaRequest::ThrowError(CStringW sError) { TRACE(L"" + sError + L'\n'); _com_issue_errorex(Error(sError), static_cast(this), __uuidof(IOmeaRequest)); } STDMETHODIMP COmeaRequest::SubscribeToFeed(BSTR URI) { try { Start("RSSPlugin.SubscribeToFeed.1", (CStringA)"url=" + CJetIe::UrlEncode((LPCWSTR)(_bstr_t)URI), &COmeaRequest::OnSyndicateComplete, &COmeaRequest::OnFailEnqueue); } COM_CATCH_RETURN(); return S_OK; } void COmeaRequest::OnSyndicateComplete(XmlDocument xmlResponse) { // Show success notification, if allowed if(m_settings.GetProfileInt(COmeaSettingStore::setShowSuccessNotifications)) CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_SYNDICATE_OK)); // As a request has succeeded, check if there's something else that could be submitted just now COmeaRequestQueue::SubmitQueue(); } void COmeaRequest::OnFailEnqueue(CStringW sErrorMessage) { // TODO: determine the cause of request failure, check the error code obtained from Omea // Try to queue the request; if prohibited, a popup will be shown COmeaRequestQueue::EnqueueRequest(m_sOmeaMethod, m_sOmeaData); } void COmeaRequest::OnFailDontQueue(CStringW sErrorMessage) { CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_FAIL_OMEA) + L'\n' + sErrorMessage, NULL, CPopupNotification::pmStop); } STDMETHODIMP COmeaRequest::CreateClipping(BSTR Subject, BSTR Text, BSTR SourceURI, VARIANT Silent) { // Extract the value of the Silent parameter bool bSilent = false; if(V_IS_MISSING(&Silent)) // The optional parameter is missing bSilent = false; else // Convert to Boolean and take the value { try { bSilent = (bool)(_variant_t)Silent; } catch(_com_error e) { COM_TRACE(); return Error(CJetIe::LoadString(IDS_E_INVALIDARG_SILENT)); } } // Prepare the request data CStringA sRequest = ""; sRequest += "subject=" + CJetIe::UrlEncode(Subject) + "&"; sRequest += "text=" + CJetIe::UrlEncode(Text) + "&"; sRequest += "sourceUrl=" + CJetIe::UrlEncode(SourceURI); try { if(bSilent) Start("Omea.CreateClippingSilent.1", sRequest, &COmeaRequest::OnClipSilentComplete, &COmeaRequest::OnFailEnqueue); else Start("Omea.CreateClipping.1", sRequest, &COmeaRequest::OnClipComplete, &COmeaRequest::OnFailEnqueue); } COM_CATCH_RETURN(); return S_OK; } void COmeaRequest::OnClipComplete(XmlDocument xmlResponse) { // Show success notification, if allowed if(m_settings.GetProfileInt(COmeaSettingStore::setShowSuccessNotifications)) CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_CLIP_OK)); // As a request has succeeded, check if there's something else that could be submitted just now COmeaRequestQueue::SubmitQueue(); } void COmeaRequest::OnClipSilentComplete(XmlDocument xmlResponse) { // Show success notification always CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_CLIP_OK)); // As a request has succeeded, check if there's something else that could be submitted just now COmeaRequestQueue::SubmitQueue(); } STDMETHODIMP COmeaRequest::Annotate(BSTR URI, BSTR Title) { try { Start("Favorites.AnnotateWeblink.1", (CStringA)"url=" + CJetIe::UrlEncode((LPCWSTR)(_bstr_t)URI) + "&title=" + CJetIe::UrlEncode((LPCWSTR)(_bstr_t)Title), &COmeaRequest::OnAnnotateComplete, &COmeaRequest::OnFailEnqueue); } COM_CATCH_RETURN(); return S_OK; } void COmeaRequest::OnAnnotateComplete(XmlDocument xmlResponse) { // Show success notification, if allowed if(m_settings.GetProfileInt(COmeaSettingStore::setShowSuccessNotifications)) CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_ANNOTATION_OK)); // As a request has succeeded, check if there's something else that could be submitted just now COmeaRequestQueue::SubmitQueue(); } STDMETHODIMP COmeaRequest::Action_Syndicate_Exec(VARIANT WebBrowser) { _bstr_t bsUri; // Take 1: if an element with focus is a link, then its target should be used for subscription, instead of the page url try { SHDocVw::IWebBrowser2Ptr oBrowser = (_variant_t)WebBrowser; BSTR bstr; MSHTMLLite::IHTMLDocument2Ptr oDoc = oBrowser->Document; CHECK(oDoc != NULL ? S_OK : E_FAIL); MSHTMLLite::IHTMLElementPtr oActive; COM_CHECK(oDoc, get_activeElement(&oActive)); CHECK(oActive != NULL ? S_OK : E_FAIL); bstr = NULL; COM_CHECK(oActive, get_tagName(&bstr)); _bstr_t bsTagName(bstr, false); // Take ownership and free on falling off scope if((bsTagName == (_bstr_t)L"A") || (bsTagName == (_bstr_t)L"a")) // An anchor { _variant_t vtHref; COM_CHECK(oActive, getAttribute(L"href", 0, &vtHref)); bsUri = vtHref; } TRACE(L"Url for subscription was taken from the target of the active element on the page: \"%s\".\n", (LPCWSTR)bsUri); } COM_CATCH_SILENT(); // Take 2: use page's URL for subscription if(bsUri.length() == 0) // Was not filled on the prev step { try { SHDocVw::IWebBrowser2Ptr oBrowser = (_variant_t)WebBrowser; bsUri = oBrowser->LocationURL; TRACE(L"Url for subscription was taken from the page address: \"%s\".\n", (LPCWSTR)bsUri); } COM_CATCH(); } // If no URL was obtained, show an error message and exit if(bsUri.length() == 0) { TRACE(L"no URI has been obtained to be used as a page address."); CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_SYNDICATE_NO_URI), NULL, CPopupNotification::pmStop); return S_OK; // Don't throw to the action executioner } // Now try to use the obtained URI try { COM_CHECK((IOmeaRequest*)this, SubscribeToFeed(bsUri)); } COM_CATCH_RETURN(); return S_OK; } STDMETHODIMP COmeaRequest::Action_Clip_Exec(VARIANT WebBrowser) { return ClipActionExecImpl(WebBrowser, FALSE); } STDMETHODIMP COmeaRequest::Action_ClipSilent_Exec(VARIANT WebBrowser) { return ClipActionExecImpl(WebBrowser, TRUE); } STDMETHODIMP COmeaRequest::ClipActionExecImpl(VARIANT WebBrowser, BOOL bSilent) { try { SHDocVw::IWebBrowser2Ptr oBrowser = (_variant_t)WebBrowser; // Retrieve the selected text MSHTMLLite::IHTMLDocument2Ptr oDoc = oBrowser->Document; if(oDoc == NULL) { // Not an HTML document currently opened in the window CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_CLIP_NO_TEXT), NULL, CPopupNotification::pmStop); return S_FALSE; } // TODO: show a message that cannot apply to non-HTML … or try to do something?.. // First, try to get the selection, if none available, test the whole document CStringW sSelection; if(!CJetIe::GetSelectedText(oDoc, NULL, &sSelection)) // Nothing has been selected, take the whole document { BSTR bstrSelection = 0; MSHTMLLite::IHTMLElementPtr oElem; COM_CHECK(oDoc, get_body(&oElem)); MSHTMLLite::IHTMLBodyElementPtr oBody = oElem; MSHTMLLite::IHTMLTxtRangePtr oRange; COM_CHECK(oBody, createTextRange((MSHTMLLite::IHTMLTxtRange**)&oRange)); COM_CHECK(oRange, get_htmlText(&bstrSelection)); _bstr_t bsSelection(bstrSelection, false); if((BSTR)bsSelection == NULL) // No text in the whole document { CJetIe::ShowPopupNotification(CJetIe::LoadString(IDS_CLIP_NO_TEXT), NULL, CPopupNotification::pmStop); return S_FALSE; } else sSelection = (LPCWSTR)bsSelection; } // Issue the request COM_CHECK((IOmeaRequest*)this, CreateClipping(oBrowser->LocationName, (_bstr_t)(LPCWSTR)sSelection, oBrowser->LocationURL, (_variant_t)(!!bSilent))); // TODO: decode the URL before passing it } COM_CATCH_RETURN(); return S_OK; } STDMETHODIMP COmeaRequest::Action_OmeaOptions_Exec(VARIANT WebBrowser) { HWND hwndParent = NULL; try { SHDocVw::IWebBrowser2Ptr oBrowser; try { oBrowser = (_variant_t)WebBrowser; } COM_CATCH_SILENT(); // NULL if unavailable hwndParent = oBrowser != NULL ? CJetIe::WindowFromBrowser(oBrowser) : NULL; // If executed from a browser, take its window as a parent for the dialog // Call Application for the dialog IApplicationPtr oApp; COM_CHECK((IOmeaRequest*)this, get_Application(&oApp)); COM_CHECK(oApp, ShowOptionsDialog((_variant_t)(long)(INT_PTR)hwndParent)); } catch(_com_error e) { CString sErr = COM_REASON_T(e); COM_TRACE(); ::MessageBox(hwndParent, CJetIe::LoadStringT(IDS_FAIL) + _T("\n") + sErr, CJetIe::LoadStringT(IDS_TITLE), MB_OK | MB_ICONSTOP); } return S_OK; } STDMETHODIMP COmeaRequest::Action_Annotate_Exec(IDispatch* Browser) { try { SHDocVw::IWebBrowser2Ptr oBrowser = Browser; COM_CHECK((IOmeaRequest*)this, Annotate(oBrowser->LocationURL, oBrowser->LocationName)); } COM_CATCH(); return S_OK; } STDMETHODIMP COmeaRequest::get_Async(VARIANT_BOOL *pVal) { if(pVal == NULL) return E_POINTER; *pVal = m_bRunAsyncRequests ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } STDMETHODIMP COmeaRequest::put_Async(VARIANT_BOOL newVal) { m_bRunAsyncRequests = newVal != VARIANT_FALSE; return S_OK; } STDMETHODIMP COmeaRequest::get_Application(IApplication **pVal) { // If this request was not explicitly invoked by a client and the client object has not been set, create a new one if(m_oApplication == NULL) m_oApplication.CreateInstance(__uuidof(COmeaApplication)); if(m_oApplication == NULL) { ASSERT(FALSE && "Could not create the Omea Application object."); *pVal = NULL; return E_FAIL; } return m_oApplication->QueryInterface(__uuidof(IApplication), (void**)pVal); } STDMETHODIMP COmeaRequest::put_Application(IApplication *newVal) { if(newVal == NULL) return E_POINTER; m_oApplication = newVal; return S_OK; } STDMETHODIMP COmeaRequest::get_AllowQueueing(VARIANT_BOOL *pVal) { if(pVal == NULL) return E_POINTER; *pVal = m_bAllowQueueing ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } STDMETHODIMP COmeaRequest::put_AllowQueueing(VARIANT_BOOL newVal) { m_bAllowQueueing = newVal != VARIANT_FALSE; return S_OK; } STDMETHODIMP COmeaRequest::SubmitRequestQueue() { try { // Ping Omea Start("System.ListAllMethods", "", &COmeaRequest::OnPingOmeaComplete, &COmeaRequest::OnFailNop); } COM_CATCH_RETURN(); return S_OK; } void COmeaRequest::OnPingOmeaComplete(XmlDocument xmlResponse) { xmlResponse = NULL; // Don't need it, release the reference // Try submitting the queue COmeaRequestQueue::SubmitQueueImpl(); } STDMETHODIMP COmeaRequest::SubmitRequest(BSTR MethodName, BSTR Parameters) { try { Start((LPCSTR)(_bstr_t)MethodName, (LPCSTR)(_bstr_t)Parameters, &COmeaRequest::OnCompleteNop, &COmeaRequest::OnFailNop); } COM_CATCH_RETURN(); return S_OK; } void COmeaRequest::OnCompleteNop(XmlDocument xmlResponse) { // Do nothing (NOP) } void COmeaRequest::OnFailNop(CStringW sErrorMessage) { // Do nothing (NOP) }