diff options
author | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:44 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:55 +0300 |
commit | 931a7dc1ac0dbc7e6c1083583ced915e572f5b47 (patch) | |
tree | 9fe9a6448d44030e26aa7107ce16044ed413e0d0 /protocols/NewsAggregator/Src | |
parent | dd7d9954042254e66e3bbbec7195c6be8b1a0663 (diff) |
all protocols (even virtual ones) moved to the Protocols folder
Diffstat (limited to 'protocols/NewsAggregator/Src')
-rw-r--r-- | protocols/NewsAggregator/Src/Authentication.cpp | 101 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/CheckFeed.cpp | 473 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Icons.cpp | 53 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Menus.cpp | 83 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/NewsAggregator.cpp | 106 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Options.cpp | 1016 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Options.h | 162 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Services.cpp | 261 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Update.cpp | 139 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/Utils.cpp | 445 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/resource.h | 57 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/stdafx.cxx | 18 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/stdafx.h | 167 | ||||
-rw-r--r-- | protocols/NewsAggregator/Src/version.h | 13 |
14 files changed, 3094 insertions, 0 deletions
diff --git a/protocols/NewsAggregator/Src/Authentication.cpp b/protocols/NewsAggregator/Src/Authentication.cpp new file mode 100644 index 0000000000..35c5cb36bd --- /dev/null +++ b/protocols/NewsAggregator/Src/Authentication.cpp @@ -0,0 +1,101 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +void CreateAuthString(char *auth, MCONTACT hContact, CFeedEditor *pDlg) +{ + wchar_t *tlogin = nullptr, *tpass = nullptr; + if (hContact && g_plugin.getByte(hContact, "UseAuth")) { + tlogin = g_plugin.getWStringA(hContact, "Login"); + tpass = g_plugin.getWStringA(hContact, "Password"); + } + else if (pDlg && pDlg->m_useauth.IsChecked()) { + tlogin = pDlg->m_login.GetText(); + tpass = pDlg->m_password.GetText(); + } + char *user = mir_u2a(tlogin), *pass = mir_u2a(tpass); + + char str[MAX_PATH]; + int len = mir_snprintf(str, "%s:%s", user, pass); + mir_free(user); + mir_free(pass); + mir_free(tlogin); + mir_free(tpass); + + mir_snprintf(auth, 250, "Basic %s", ptrA(mir_base64_encode(str, (size_t)len))); +} + +CAuthRequest::CAuthRequest(CFeedEditor *pDlg, MCONTACT hContact) : + CSuper(g_plugin, IDD_AUTHENTICATION), + m_feedname(this, IDC_FEEDNAME), m_username(this, IDC_FEEDUSERNAME), + m_password(this, IDC_FEEDPASSWORD), m_ok(this, IDOK) +{ + m_pDlg = pDlg; + m_hContact = hContact; + m_ok.OnClick = Callback(this, &CAuthRequest::OnOk); +} + +bool CAuthRequest::OnInitDialog() +{ + if (m_pDlg) { + ptrW strfeedtitle(m_pDlg->m_feedtitle.GetText()); + + if (strfeedtitle) + m_feedname.SetText(strfeedtitle); + else { + ptrW strfeedurl(m_pDlg->m_feedurl.GetText()); + m_feedname.SetText(strfeedurl); + } + } + else if (m_hContact) { + ptrW ptszNick(g_plugin.getWStringA(m_hContact, "Nick")); + if (!ptszNick) + ptszNick = g_plugin.getWStringA(m_hContact, "URL"); + if (ptszNick) + m_feedname.SetText(ptszNick); + } + return true; +} + +void CAuthRequest::OnOk(CCtrlBase*) +{ + ptrW strfeedusername(m_username.GetText()); + if (!strfeedusername || mir_wstrcmp(strfeedusername, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter your username"), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + ptrA strfeedpassword(m_password.GetTextA()); + if (!strfeedpassword || mir_strcmp(strfeedpassword, "") == 0) { + MessageBox(m_hwnd, TranslateT("Enter your password"), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + if (m_pDlg) { + m_pDlg->m_useauth.SetState(1); + m_pDlg->m_login.Enable(1); + m_pDlg->m_password.Enable(1); + m_pDlg->m_login.SetText(strfeedusername); + m_pDlg->m_password.SetTextA(strfeedpassword); + } + else if (m_hContact) { + g_plugin.setByte(m_hContact, "UseAuth", 1); + g_plugin.setWString(m_hContact, "Login", strfeedusername); + g_plugin.setString(m_hContact, "Password", strfeedpassword); + } +} diff --git a/protocols/NewsAggregator/Src/CheckFeed.cpp b/protocols/NewsAggregator/Src/CheckFeed.cpp new file mode 100644 index 0000000000..87282451f4 --- /dev/null +++ b/protocols/NewsAggregator/Src/CheckFeed.cpp @@ -0,0 +1,473 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static CMStringA DetectEncoding(const TiXmlDocument &doc) +{ + auto *pChild = doc.FirstChild(); + if (pChild) + if (auto *pDecl = pChild->ToDeclaration()) + if (auto *pVal = pDecl->Value()) + if (!memcmp(pVal, "xml", 3)) { + const char *p1 = strstr(pVal, "encoding=\""), *p2 = 0; + if (p1) { + p1 += 10; + p2 = strchr(p1, '\"'); + } + if (p1 && p2) + return CMStringA(p1, int(p2-p1)); + } + + return CMStringA(); +} + +static wchar_t* EncodeResult(const char *szString, const CMStringA &szEncoding) +{ + if (szEncoding == "koi8-r") + return mir_a2u_cp(szString, 20866); + if (szEncoding == "windows-1251") + return mir_a2u_cp(szString, 1251); + + return mir_utf8decodeW(szString); +} + +static void SetAvatar(MCONTACT hContact, const char *pszValue) +{ + Utf2T url(pszValue); + g_plugin.setWString(hContact, "ImageURL", url); + + PROTO_AVATAR_INFORMATION ai = { 0 }; + ai.hContact = hContact; + + ptrW szNick(g_plugin.getWStringA(hContact, "Nick")); + if (szNick) { + wchar_t *ext = wcsrchr((wchar_t *)url, '.') + 1; + ai.format = ProtoGetAvatarFormat(ext); + + wchar_t *filename = szNick; + mir_snwprintf(ai.filename, L"%s\\%s.%s", tszRoot, filename, ext); + if (DownloadFile(url, ai.filename)) { + g_plugin.setWString(hContact, "ImagePath", ai.filename); + ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&ai, NULL); + } + else ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, (HANDLE)&ai, NULL); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// touches a feed to verify whether it exists + +LPCTSTR CheckFeed(wchar_t *tszURL, CFeedEditor *pEditDlg) +{ + Netlib_LogfW(hNetlibUser, L"Started validating feed %s.", tszURL); + char *szData = nullptr; + GetNewsData(tszURL, &szData, NULL, pEditDlg); + if (szData) { + TiXmlDocument doc; + int ret = doc.Parse(szData); + mir_free(szData); + if (ret == ERROR_SUCCESS) { + CMStringA codepage = DetectEncoding(doc); + + for (auto *it : TiXmlEnum(&doc)) { + auto *szNodeName = it->Name(); + const TiXmlElement *pNode; + if (!mir_strcmpi(szNodeName, "rss") || !mir_strcmpi(szNodeName, "rdf")) + pNode = it->FirstChildElement(); + else if (!mir_strcmpi(szNodeName, "feed")) + pNode = it; + else continue; + + for (auto *child : TiXmlFilter(pNode, "title")) { + wchar_t mes[MAX_PATH]; + mir_snwprintf(mes, TranslateT("%s\nis a valid feed's address."), tszURL); + MessageBox(pEditDlg->GetHwnd(), mes, TranslateT("News Aggregator"), MB_OK | MB_ICONINFORMATION); + return EncodeResult(child->GetText(), codepage); + } + } + } + } + + Netlib_LogfW(hNetlibUser, L"%s is not a valid feed's address.", tszURL); + wchar_t mes[MAX_PATH]; + mir_snwprintf(mes, TranslateT("%s\nis not a valid feed's address."), tszURL); + MessageBox(pEditDlg->GetHwnd(), mes, TranslateT("News Aggregator"), MB_OK | MB_ICONERROR); + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// loads a feed completely, with messages + +static void XmlToMsg(MCONTACT hContact, CMStringW &title, CMStringW &link, CMStringW &descr, CMStringW &author, CMStringW &comments, CMStringW &guid, CMStringW &category, time_t stamp) +{ + CMStringW message = g_plugin.getWStringA(hContact, "MsgFormat"); + if (!message) + message = TAGSDEFAULT; + + if (title.IsEmpty()) + message.Replace(L"#<title>#", TranslateT("empty")); + else + message.Replace(L"#<title>#", title); + + if (link.IsEmpty()) + message.Replace(L"#<link>#", TranslateT("empty")); + else + message.Replace(L"#<link>#", link); + + if (descr.IsEmpty()) + message.Replace(L"#<description>#", TranslateT("empty")); + else + message.Replace(L"#<description>#", descr); + + if (author.IsEmpty()) + message.Replace(L"#<author>#", TranslateT("empty")); + else + message.Replace(L"#<author>#", author); + + if (comments.IsEmpty()) + message.Replace(L"#<comments>#", TranslateT("empty")); + else + message.Replace(L"#<comments>#", comments); + + if (guid.IsEmpty()) + message.Replace(L"#<guid>#", TranslateT("empty")); + else + message.Replace(L"#<guid>#", guid); + + if (category.IsEmpty()) + message.Replace(L"#<category>#", TranslateT("empty")); + else + message.Replace(L"#<category>#", category); + + DBEVENTINFO olddbei = {}; + bool MesExist = false; + T2Utf pszTemp(message); + DWORD cbMemoLen = 10000, cbOrigLen = (DWORD)mir_strlen(pszTemp); + BYTE *pbBuffer = (BYTE*)mir_alloc(cbMemoLen); + for (MEVENT hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { + olddbei.cbBlob = db_event_getBlobSize(hDbEvent); + if (olddbei.cbBlob > cbMemoLen) + pbBuffer = (PBYTE)mir_realloc(pbBuffer, (size_t)(cbMemoLen = olddbei.cbBlob)); + olddbei.pBlob = pbBuffer; + db_event_get(hDbEvent, &olddbei); + + // there's no need to look for the elder events + if (stamp > 0 && olddbei.timestamp < (DWORD)stamp) + break; + + if ((DWORD)mir_strlen((char*)olddbei.pBlob) == cbOrigLen && !mir_strcmp((char*)olddbei.pBlob, pszTemp)) { + MesExist = true; + break; + } + } + mir_free(pbBuffer); + + if (!MesExist) { + if (stamp == 0) + stamp = time(0); + + T2Utf pszMessage(message); + + PROTORECVEVENT recv = { 0 }; + recv.timestamp = (DWORD)stamp; + recv.szMessage = pszMessage; + ProtoChainRecvMsg(hContact, &recv); + } +} + +void CheckCurrentFeed(MCONTACT hContact) +{ + // Check is disabled by the user? + if (!g_plugin.getByte(hContact, "CheckState", 1) != 0) + return; + + wchar_t *szURL = g_plugin.getWStringA(hContact, "URL"); + if (szURL == nullptr) + return; + + Netlib_LogfW(hNetlibUser, L"Started checking feed %s.", szURL); + + char *szData = nullptr; + GetNewsData(szURL, &szData, hContact, nullptr); + mir_free(szURL); + + if (szData == nullptr) + return; + + TiXmlDocument doc; + int ret = doc.Parse(szData); + mir_free(szData); + if (ret != ERROR_SUCCESS) + return; + + CMStringA codepage = DetectEncoding(doc); + + CMStringW szValue; + + for (auto *it : TiXmlEnum(&doc)) { + auto *szNodeName = it->Name(); + bool isRSS = !mir_strcmpi(szNodeName, "rss"), isAtom = !mir_strcmpi(szNodeName, "rdf"); + if (isRSS || isAtom) { + if (isRSS) { + if (auto *pVersion = it->Attribute("version")) { + char ver[MAX_PATH]; + mir_snprintf(ver, "RSS %s", pVersion); + g_plugin.setString(hContact, "MirVer", ver); + } + } + else if (isAtom) + g_plugin.setWString(hContact, "MirVer", L"RSS 1.0"); + + for (auto *child : TiXmlEnum(it->FirstChildElement())) { + auto *childName = child->Name(); + if (!mir_strcmpi(childName, "title")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "FirstName", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "link")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Homepage", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "description")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) { + ClearText(szValue, szChildText); + g_plugin.setWString(hContact, "About", szValue); + db_set_ws(hContact, "CList", "StatusMsg", szValue); + } + } + else if (!mir_strcmpi(childName, "language")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Language1", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "managingEditor")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "e-mail", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "category")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Interest0Text", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "copyright")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + db_set_s(hContact, "UserInfo", "MyNotes", _T2A(ClearText(szValue, szChildText))); + } + else if (!mir_strcmpi(childName, "image")) { + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + else if (!mir_strcmpi(childName, "lastBuildDate")) { + time_t stamp = DateToUnixTime(child->GetText(), 0); + double deltaupd = difftime(time(0), stamp); + double deltacheck = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck")); + if (deltaupd - deltacheck >= 0) { + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + return; + } + } + else if (!mir_strcmpi(childName, "item")) { + CMStringW title, link, descr, author, comments, guid, category; + time_t stamp = 0; + for (auto *itemval : TiXmlEnum(child)) { + auto *itemName = itemval->Name(); + ptrW value(EncodeResult(itemval->GetText(), codepage)); + + // We only use the first tag for now and ignore the rest. + if (!mir_strcmpi(itemName, "title")) + ClearText(title, value); + + else if (!mir_strcmpi(itemName, "link")) + ClearText(link, value); + + else if (!mir_strcmpi(itemName, "pubDate") || !mir_strcmpi(itemName, "date")) { + if (stamp == 0) + stamp = DateToUnixTime(itemval->GetText(), 0); + } + else if (!mir_strcmpi(itemName, "description") || !mir_strcmpi(itemName, "encoded")) + ClearText(descr, value); + + else if (!mir_strcmpi(itemName, "author") || !mir_strcmpi(itemName, "creator")) + ClearText(author, value); + + else if (!mir_strcmpi(itemName, "comments")) + ClearText(comments, value); + + else if (!mir_strcmpi(itemName, "guid")) + ClearText(guid, value); + + else if (!mir_strcmpi(itemName, "category")) + ClearText(category, value); + } + + XmlToMsg(hContact, title, link, descr, author, comments, guid, category, stamp); + } + } + } + else if (!mir_strcmpi(szNodeName, "feed")) { + g_plugin.setWString(hContact, "MirVer", L"Atom 3"); + for (auto *child : TiXmlEnum(it)) { + auto *szChildName = child->Name(); + if (!mir_strcmpi(szChildName, "title")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "FirstName", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "link")) { + if (!child->Attribute("rel", "self")) + if (auto *pHref = child->Attribute("href")) + g_plugin.setWString(hContact, "Homepage", Utf2T(pHref)); + } + else if (!mir_strcmpi(szChildName, "subtitle")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) { + ClearText(szValue, szChildText); + g_plugin.setWString(hContact, "About", szValue); + db_set_ws(hContact, "CList", "StatusMsg", szValue); + } + } + else if (!mir_strcmpi(szChildName, "language")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Language1", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "author")) { + for (auto *authorval : TiXmlFilter(child, "email")) { + g_plugin.setWString(hContact, "e-mail", ptrW(EncodeResult(authorval->GetText(), codepage))); + break; + } + } + else if (!mir_strcmpi(szChildName, "category")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Interest0Text", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "icon")) { + for (auto *imageval : TiXmlFilter(child, "url")) + SetAvatar(hContact, imageval->GetText()); + } + else if (!mir_strcmpi(szChildName, "updated")) { + time_t stamp = DateToUnixTime(child->GetText(), 1); + double deltaupd = difftime(time(0), stamp); + double deltacheck = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck")); + if (deltaupd - deltacheck >= 0) { + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + return; + } + } + else if (!mir_strcmpi(szChildName, "entry")) { + CMStringW title, link, descr, author, comments, guid, category; + time_t stamp = 0; + for (auto *itemval : TiXmlEnum(child)) { + LPCSTR szItemName = itemval->Name(); + if (!mir_strcmpi(szItemName, "title")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(title, szItemText); + } + else if (!mir_strcmpi(szItemName, "link")) { + if (auto *pszLink = itemval->Attribute("href")) + ClearText(link, ptrW(EncodeResult(pszLink, codepage))); + } + else if (!mir_strcmpi(szItemName, "updated")) { + if (stamp == 0) + stamp = DateToUnixTime(itemval->GetText(), 0); + } + else if (!mir_strcmpi(szItemName, "summary") || !mir_strcmpi(szItemName, "content")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(descr, szItemText); + } + else if (!mir_strcmpi(szItemName, "author")) { + for (auto *authorval : TiXmlFilter(itemval, "name")) { + ptrW szItemText(EncodeResult(authorval->GetText(), codepage)); + if (szItemText) + ClearText(author, szItemText); + break; + } + } + else if (!mir_strcmpi(szItemName, "comments")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(comments, szItemText); + } + else if (!mir_strcmpi(szItemName, "id")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(guid, szItemText); + } + else if (!mir_strcmpi(szItemName, "category")) { + if (auto *p = itemval->Attribute("term")) + ClearText(link, ptrW(EncodeResult(p, codepage))); + } + } + + XmlToMsg(hContact, title, link, descr, author, comments, guid, category, stamp); + } + } + } + } + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// downloads avatars from a given feed + +void CheckCurrentFeedAvatar(MCONTACT hContact) +{ + if (!g_plugin.getByte(hContact, "CheckState", 1)) + return; + + wchar_t *szURL = g_plugin.getWStringA(hContact, "URL"); + if (szURL == nullptr) + return; + + char *szData = nullptr; + GetNewsData(szURL, &szData, hContact, nullptr); + mir_free(szURL); + + if (szData == nullptr) + return; + + TiXmlDocument doc; + int ret = doc.Parse(szData); + mir_free(szData); + if (ret != ERROR_SUCCESS) + return; + + for (auto *it : TiXmlEnum(&doc)) { + auto *szNodeName = it->Name(); + if (!mir_strcmpi(szNodeName, "rss") || !mir_strcmpi(szNodeName, "rdf")) { + for (auto *child : TiXmlFilter(it->FirstChildElement(), "image")) + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + else if (!mir_strcmpi(szNodeName, "feed")) { + for (auto *child : TiXmlFilter(it, "icon")) + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + } +} diff --git a/protocols/NewsAggregator/Src/Icons.cpp b/protocols/NewsAggregator/Src/Icons.cpp new file mode 100644 index 0000000000..b9cdeb6166 --- /dev/null +++ b/protocols/NewsAggregator/Src/Icons.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static IconItem iconList[] = +{ + { LPGEN("Protocol icon"), "main", IDI_ICON}, + { LPGEN("Check All Feeds"), "checkall", IDI_CHECKALL}, + { LPGEN("Add Feed"), "addfeed", IDI_ADDFEED}, + { LPGEN("Import Feeds"), "importfeeds", IDI_IMPORTFEEDS}, + { LPGEN("Export Feeds"), "exportfeeds", IDI_EXPORTFEEDS}, + { LPGEN("Check Feed"), "checkfeed", IDI_CHECKALL}, + { LPGEN("Auto Update Enabled"), "enabled", IDI_ENABLED}, + { LPGEN("Auto Update Disabled"), "disabled", IDI_DISABLED} +}; + +void InitIcons() +{ + g_plugin.registerIcon(LPGEN("News Aggregator"), iconList, MODULENAME); +} + +HICON LoadIconEx(const char *name, bool big) +{ + char szSettingName[100]; + mir_snprintf(szSettingName, "%s_%s", MODULENAME, name); + return IcoLib_GetIcon(szSettingName, big); +} + +HANDLE GetIconHandle(const char *name) +{ + for (int i=0; i < _countof(iconList); i++) + if ( !mir_strcmp(iconList[i].szName, name)) + return iconList[i].hIcolib; + + return nullptr; +} diff --git a/protocols/NewsAggregator/Src/Menus.cpp b/protocols/NewsAggregator/Src/Menus.cpp new file mode 100644 index 0000000000..7770e843eb --- /dev/null +++ b/protocols/NewsAggregator/Src/Menus.cpp @@ -0,0 +1,83 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HGENMENU hService2[7]; + +void InitMenu() +{ + CMenuItem mi(&g_plugin); + mi.flags = CMIF_UNICODE | CMIF_NOTOFFLINE; + mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("News Aggregator"), 500099000); + Menu_ConfigureItem(mi.root, MCI_OPT_UID, "D9733E4F-1946-4390-8EB3-591E8687222E"); + + SET_UID(mi, 0x3ec91864, 0xefa7, 0x4994, 0xb7, 0x75, 0x6c, 0x96, 0xcb, 0x29, 0x2f, 0x93); + mi.position = 10100001; + if (g_plugin.getByte("AutoUpdate", 1)) + mi.name.w = LPGENW("Auto Update Enabled"); + else + mi.name.w = LPGENW("Auto Update Disabled"); + mi.hIcolibItem = GetIconHandle("main"); + mi.pszService = MS_NEWSAGGREGATOR_ENABLED; + hService2[0] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0x8076bb4d, 0x1e44, 0x43af, 0x97, 0x1e, 0x31, 0xd8, 0xa4, 0xe9, 0xb8, 0x37); + mi.position = 20100001; + mi.name.w = LPGENW("Check All Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_CHECKALLFEEDS; + hService2[1] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0xb876484d, 0x28aa, 0x4e03, 0x9e, 0x98, 0xed, 0xbc, 0xd1, 0xcf, 0x31, 0x80); + mi.position = 20100002; + mi.hIcolibItem = GetIconHandle("addfeed"); + mi.name.w = LPGENW("Add Feed"); + mi.pszService = MS_NEWSAGGREGATOR_ADDFEED; + hService2[2] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0x600bf2c2, 0xa974, 0x44d3, 0x98, 0xf9, 0xe6, 0x65, 0x7c, 0x1f, 0x63, 0x37); + mi.position = 20100003; + mi.hIcolibItem = GetIconHandle("importfeeds"); + mi.name.w = LPGENW("Import Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_IMPORTFEEDS; + hService2[3] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0xc09c8119, 0x64c2, 0x49bd, 0x81, 0xf, 0x54, 0x20, 0x69, 0xd7, 0x30, 0xcf); + mi.position = 20100004; + mi.hIcolibItem = GetIconHandle("exportfeeds"); + mi.name.w = LPGENW("Export Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_EXPORTFEEDS; + hService2[4] = Menu_AddMainMenuItem(&mi); + + // adding contact menu items + SET_UID(mi, 0x92be499c, 0x928c, 0x4789, 0x8f, 0x36, 0x28, 0xa2, 0x9f, 0xb7, 0x1a, 0x97); + mi.root = nullptr; + mi.position = -0x7FFFFFFA; + mi.hIcolibItem = GetIconHandle("checkfeed"); + mi.name.w = LPGENW("Check feed"); + mi.pszService = MS_NEWSAGGREGATOR_CHECKFEED; + hService2[5] = Menu_AddContactMenuItem(&mi, MODULENAME); + + SET_UID(mi, 0x41a70fbc, 0x9241, 0x44c0, 0x90, 0x90, 0x87, 0xd2, 0xc5, 0x9f, 0xc9, 0xac); + mi.name.w = LPGENW("Change feed"); + mi.pszService = MS_NEWSAGGREGATOR_CHANGEFEED; + hService2[6] = Menu_AddContactMenuItem(&mi, MODULENAME); + + Menu_ModifyItem(hService2[0], nullptr, GetIconHandle(g_plugin.getByte("AutoUpdate", 1) ? "enabled" : "disabled")); +} diff --git a/protocols/NewsAggregator/Src/NewsAggregator.cpp b/protocols/NewsAggregator/Src/NewsAggregator.cpp new file mode 100644 index 0000000000..8ee17dd97a --- /dev/null +++ b/protocols/NewsAggregator/Src/NewsAggregator.cpp @@ -0,0 +1,106 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HANDLE hPrebuildMenuHook = nullptr; +CDlgBase *pAddFeedDialog = nullptr, *pImportDialog = nullptr, *pExportDialog = nullptr; +wchar_t tszRoot[MAX_PATH] = {0}; +HANDLE hUpdateMutex; + +LIST<CFeedEditor> g_arFeeds(1, PtrKeySortT); + +CMPlugin g_plugin; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {56CC3F29-CCBF-4546-A8BA-9856248A412A} + {0x56cc3f29, 0xccbf, 0x4546, {0xa8, 0xba, 0x98, 0x56, 0x24, 0x8a, 0x41, 0x2a}} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) +{ + RegisterProtocol(PROTOTYPE_VIRTUAL); + SetUniqueId("URL"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Load() +{ + // Add options hook + HookEvent(ME_OPT_INITIALISE, OptInit); + HookEvent(ME_SYSTEM_MODULESLOADED, NewsAggrInit); + HookEvent(ME_SYSTEM_PRESHUTDOWN, NewsAggrPreShutdown); + + hUpdateMutex = CreateMutex(nullptr, FALSE, nullptr); + + CreateProtoServiceFunction(MODULENAME, PS_GETNAME, NewsAggrGetName); + CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, NewsAggrGetCaps); + CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, NewsAggrSetStatus); + CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, NewsAggrGetStatus); + CreateProtoServiceFunction(MODULENAME, PS_LOADICON, NewsAggrLoadIcon); + CreateProtoServiceFunction(MODULENAME, PSS_GETINFO, NewsAggrGetInfo); + CreateProtoServiceFunction(MODULENAME, PS_GETAVATARINFO, NewsAggrGetAvatarInfo); + CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, NewsAggrRecvMessage); + + CreateServiceFunction(MS_NEWSAGGREGATOR_CHECKALLFEEDS, CheckAllFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_ADDFEED, AddFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_IMPORTFEEDS, ImportFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_EXPORTFEEDS, ExportFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_CHECKFEED, CheckFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_CHANGEFEED, ChangeFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_ENABLED, EnableDisable); + + HOTKEYDESC hkd = {}; + hkd.dwFlags = HKD_UNICODE; + hkd.pszName = "NewsAggregator/CheckAllFeeds"; + hkd.szDescription.w = LPGENW("Check All Feeds"); + hkd.szSection.w = LPGENW("News Aggregator"); + hkd.pszService = MS_NEWSAGGREGATOR_CHECKALLFEEDS; + hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL+HKCOMB_A, 'O') | HKF_MIRANDA_LOCAL; + g_plugin.addHotkey(&hkd); + + InitIcons(); + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Unload() +{ + DestroyUpdateList(); + CloseHandle(hUpdateMutex); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Options.cpp b/protocols/NewsAggregator/Src/Options.cpp new file mode 100644 index 0000000000..6239ce15c1 --- /dev/null +++ b/protocols/NewsAggregator/Src/Options.cpp @@ -0,0 +1,1016 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +CExportFeed::CExportFeed() + : CSuper(g_plugin, IDD_FEEDEXPORT), + m_feedslist(this, IDC_FEEDSLIST), m_feedsexportlist(this, IDC_FEEDSEXPORTLIST), + m_addfeed(this, IDC_ADDFEED), m_removefeed(this, IDC_REMOVEFEED), + m_addallfeeds(this, IDC_ADDALLFEEDS), m_removeallfeeds(this, IDC_REMOVEALLFEEDS), + m_ok(this, IDOK) +{ + m_addfeed.OnClick = Callback(this, &CExportFeed::OnAddFeed); + m_removefeed.OnClick = Callback(this, &CExportFeed::OnRemoveFeed); + m_addallfeeds.OnClick = Callback(this, &CExportFeed::OnAddAllFeeds); + m_removeallfeeds.OnClick = Callback(this, &CExportFeed::OnRemoveAllFeeds); + m_ok.OnClick = Callback(this, &CExportFeed::OnOk); + + m_feedslist.OnDblClick = Callback(this, &CExportFeed::OnFeedsList); + m_feedsexportlist.OnDblClick = Callback(this, &CExportFeed::OnFeedsExportList); +} + +bool CExportFeed::OnInitDialog() +{ + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "ExportDlg"); + for (auto &hContact : Contacts(MODULENAME)) { + ptrW message(g_plugin.getWStringA(hContact, "Nick")); + if (message != nullptr) + m_feedslist.AddString(message); + } + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } + return true; +} + +void CExportFeed::OnAddFeed(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsexportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CExportFeed::OnRemoveFeed(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsexportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsexportlist.DeleteString(cursel); + if (!m_feedsexportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CExportFeed::OnAddAllFeeds(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int count = m_feedslist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(i, item, _countof(item)); + m_feedsexportlist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedslist.DeleteString(i); + m_addfeed.Disable(); + m_addallfeeds.Disable(); +} + +void CExportFeed::OnRemoveAllFeeds(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int count = m_feedsexportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(i, item, _countof(item)); + m_feedslist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedsexportlist.DeleteString(i); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); +} + +void CExportFeed::OnFeedsList(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsexportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CExportFeed::OnFeedsExportList(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsexportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsexportlist.DeleteString(cursel); + if (!m_feedsexportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +static const TiXmlElement* AdviceNode(const TiXmlElement *node) +{ + auto *tmpnode = node; + + // try to rest on the same level first + node = node->NextSiblingElement(); + if (node) + return node; + + do { + // go up one level + node = tmpnode->Parent()->ToElement(); + tmpnode = node; + node = node->NextSiblingElement(); + if (node) + return node; + } + while (mir_strcmpi(tmpnode->Name(), "body")); + + // nothing found or we reached body + return nullptr; +} + +void CExportFeed::OnOk(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + VARSW tszMirDir(L"%miranda_path%"); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = sizeof(ofn); + wchar_t tmp[MAX_PATH]; + mir_snwprintf(tmp, L"%s (*.opml)%c*.opml%c%c", TranslateT("OPML files"), 0, 0, 0); + ofn.lpstrFilter = tmp; + ofn.hwndOwner = nullptr; + ofn.lpstrFile = FileName; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; + ofn.lpstrInitialDir = tszMirDir; + *FileName = '\0'; + ofn.lpstrDefExt = L""; + if (!GetSaveFileName(&ofn)) + return; + + TiXmlDocument doc; + auto *hXml = doc.NewElement("opml"); doc.InsertEndChild(hXml); + hXml->SetAttribute("version", "1.0"); + + auto *xmlHeader = doc.NewElement("head"); hXml->InsertEndChild(xmlHeader); + auto *xmlTitle = doc.NewElement("title"); xmlTitle->SetText("Miranda NG NewsAggregator plugin export"); xmlHeader->InsertEndChild(xmlTitle); + + auto *xmlBody = doc.NewElement("body"); hXml->InsertEndChild(xmlBody); + + int count = m_feedsexportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(i, item, _countof(item)); + MCONTACT hContact = GetContactByNick(item); + wchar_t + *title = g_plugin.getWStringA(hContact, "Nick"), + *url = g_plugin.getWStringA(hContact, "URL"), + *siteurl = g_plugin.getWStringA(hContact, "Homepage"), + *group = db_get_wsa(hContact, "CList", "Group"); + + TiXmlElement *elem = xmlBody; + if (group) { + wchar_t *section = wcstok(group, L"\\"); + while (section != nullptr) { + TiXmlElement *existgroup = 0; + for (auto *it : TiXmlFilter(elem, "outline")) { + if (it->Attribute("title", T2Utf(section))) { + existgroup = (TiXmlElement*)it; + break; + } + } + + if (!existgroup) { + auto *pNew = doc.NewElement("outline"); + pNew->SetAttribute("title", section); pNew->SetAttribute("text", section); + elem->InsertEndChild(pNew); + elem = pNew; + } + else elem = existgroup; + + section = wcstok(nullptr, L"\\"); + } + } + + auto *pNew = doc.NewElement("outline"); elem->InsertEndChild(pNew); + pNew->SetAttribute("text", title); + pNew->SetAttribute("title", title); + pNew->SetAttribute("type", "rss"); + pNew->SetAttribute("xmlUrl", url); + pNew->SetAttribute("htmlUrl", siteurl); + + mir_free(title); + mir_free(url); + mir_free(siteurl); + mir_free(group); + } + + FILE *out = _wfopen(FileName, L"wb"); + if (out) { + tinyxml2::XMLPrinter printer(out); + doc.Print(&printer); + fclose(out); + } +} + +bool CExportFeed::OnClose() +{ + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, "ExportDlg"); + if (pExportDialog) + pExportDialog = nullptr; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CImportFeed::CImportFeed(CCtrlListView *m_feeds) + : CSuper(g_plugin, IDD_FEEDIMPORT), + m_importfile(this, IDC_IMPORTFILEPATH), m_browsefile(this, IDC_BROWSEIMPORTFILE), + m_feedslist(this, IDC_FEEDSLIST), m_feedsimportlist(this, IDC_FEEDSIMPORTLIST), + m_addfeed(this, IDC_ADDFEED), m_removefeed(this, IDC_REMOVEFEED), + m_addallfeeds(this, IDC_ADDALLFEEDS), m_removeallfeeds(this, IDC_REMOVEALLFEEDS), + m_ok(this, IDOK) +{ + m_list = m_feeds; + m_browsefile.OnClick = Callback(this, &CImportFeed::OnBrowseFile); + m_addfeed.OnClick = Callback(this, &CImportFeed::OnAddFeed); + m_removefeed.OnClick = Callback(this, &CImportFeed::OnRemoveFeed); + m_addallfeeds.OnClick = Callback(this, &CImportFeed::OnAddAllFeeds); + m_removeallfeeds.OnClick = Callback(this, &CImportFeed::OnRemoveAllFeeds); + m_ok.OnClick = Callback(this, &CImportFeed::OnOk); + + m_feedslist.OnDblClick = Callback(this, &CImportFeed::OnFeedsList); + m_feedsimportlist.OnDblClick = Callback(this, &CImportFeed::OnFeedsImportList); +} + +bool CImportFeed::OnInitDialog() +{ + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "ImportDlg"); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + m_addfeed.Disable(); + m_addallfeeds.Disable(); + return true; +} + +void CImportFeed::OnBrowseFile(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + VARSW tszMirDir(L"%miranda_path%"); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = sizeof(ofn); + wchar_t tmp[MAX_PATH]; + mir_snwprintf(tmp, L"%s (*.opml, *.xml)%c*.opml;*.xml%c%c", TranslateT("OPML files"), 0, 0, 0); + ofn.lpstrFilter = tmp; + ofn.hwndOwner = nullptr; + ofn.lpstrFile = FileName; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY; + ofn.lpstrInitialDir = tszMirDir; + *FileName = '\0'; + ofn.lpstrDefExt = L""; + if (!GetOpenFileName(&ofn)) + return; + + FILE *in = _wfopen(FileName, L"rb"); + if (in == nullptr) + return; + + TiXmlDocument doc; + int res = doc.LoadFile(in); + fclose(in); + if (res != 0) { + MessageBox(m_hwnd, TranslateT("Not valid import file."), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + + m_importfile.SetText(FileName); + + auto *node = TiXmlConst(&doc)["opml"]["body"]["outline"].ToElement(); + if (!node) + node = TiXmlConst(&doc)["body"]["outline"].ToElement(); + if (node == nullptr) { + MessageBox(m_hwnd, TranslateT("Not valid import file."), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + + while (node) { + auto *pszUrl = node->Attribute("xmlUrl"); + if (!pszUrl && node->NoChildren()) + node = AdviceNode(node); + else if (!pszUrl && !node->NoChildren()) + node = node->FirstChildElement(); + else if (pszUrl) { + if (auto *pszText = node->Attribute("text")) { + Utf2T text(pszText); + m_feedslist.AddString(text); + m_addfeed.Enable(); + m_addallfeeds.Enable(); + } + + node = AdviceNode(node); + } + } +} + +void CImportFeed::OnAddFeed(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsimportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CImportFeed::OnRemoveFeed(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsimportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsimportlist.DeleteString(cursel); + if (!m_feedsimportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CImportFeed::OnAddAllFeeds(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int count = m_feedslist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(i, item, _countof(item)); + m_feedsimportlist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedslist.DeleteString(i); + m_addfeed.Disable(); + m_addallfeeds.Disable(); +} + +void CImportFeed::OnRemoveAllFeeds(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int count = m_feedsimportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(i, item, _countof(item)); + m_feedslist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedsimportlist.DeleteString(i); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); +} + +void CImportFeed::OnFeedsList(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsimportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CImportFeed::OnFeedsImportList(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsimportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsimportlist.DeleteString(cursel); + if (!m_feedsimportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CImportFeed::OnOk(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + m_importfile.GetText(FileName, _countof(FileName)); + + FILE *in = _wfopen(FileName, L"rb"); + if (in == nullptr) + return; + + TiXmlDocument doc; + int res = doc.LoadFile(in); + fclose(in); + if (res != 0) + return; + + auto *node = TiXmlConst(&doc)["opml"]["body"]["outline"].ToElement(); + if (!node) + node = TiXmlConst(&doc)["body"]["outline"].ToElement(); + if (node == nullptr) + return; + + int count = m_feedsimportlist.GetCount(); + int DUPES = 0; + + while (node) { + auto *pszUrl = node->Attribute("xmlUrl"); + if (!pszUrl && node->NoChildren()) + node = AdviceNode(node); + else if (!pszUrl && !node->NoChildren()) + node = node->FirstChildElement(); + else if (pszUrl) { + wchar_t *text = nullptr, *url = nullptr, *siteurl = nullptr; + bool bNeedToImport = false; + + if (auto *pszText = node->Attribute("text")) { + text = mir_utf8decodeW(pszText); + + for (int j = 0; j < count; j++) { + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(j, item, _countof(item)); + if (!mir_wstrcmpi(item, text)) { + bNeedToImport = true; + break; + } + } + } + + if (auto *pszText = node->Attribute("xmlUrl")) { + url = mir_utf8decodeW(pszText); + if (GetContactByURL(url) && bNeedToImport) { + bNeedToImport = false; + DUPES++; + } + } + + if (auto *pszText = node->Attribute("htmlUrl")) + siteurl = mir_utf8decodeW(pszText); + + if (bNeedToImport && text && url && siteurl) { + CMStringW wszGroup; + auto *parent = node->Parent()->ToElement(); + while (mir_strcmpi(parent->Name(), "body")) { + if (auto *pszText = parent->Attribute("text")) { + if (!wszGroup.IsEmpty()) + wszGroup.Insert(0, L"\\"); + wszGroup.Insert(0, Utf2T(pszText)); + } + parent = parent->Parent()->ToElement(); + } + + MCONTACT hContact = db_add_contact(); + Proto_AddToContact(hContact, MODULENAME); + g_plugin.setWString(hContact, "Nick", text); + g_plugin.setWString(hContact, "URL", url); + g_plugin.setWString(hContact, "Homepage", siteurl); + g_plugin.setByte(hContact, "CheckState", 1); + g_plugin.setDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME); + g_plugin.setWString(hContact, "MsgFormat", TAGSDEFAULT); + g_plugin.setWord(hContact, "Status", Proto_GetStatus(MODULENAME)); + + if (m_list != nullptr) { + int iItem = m_list->AddItem(text, -1); + m_list->SetItem(iItem, 1, url); + m_list->SetCheckState(iItem, 1); + } + + if (!wszGroup.IsEmpty()) { + db_set_ws(hContact, "CList", "Group", wszGroup); + Clist_GroupCreate(0, wszGroup); + } + } + mir_free(text); + mir_free(url); + mir_free(siteurl); + + node = AdviceNode(node); + } + } + + wchar_t mes[MAX_PATH]; + if (DUPES) + mir_snwprintf(mes, TranslateT("Imported %d feed(s)\r\nNot imported %d duplicate(s)."), count - DUPES, DUPES); + else + mir_snwprintf(mes, TranslateT("Imported %d feed(s)."), count); + MessageBox(m_hwnd, mes, TranslateT("News Aggregator"), MB_OK | MB_ICONINFORMATION); +} + +bool CImportFeed::OnClose() +{ + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, "ImportDlg"); + if (pImportDialog) + pImportDialog = nullptr; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CFeedEditor::CFeedEditor(int iItem, CCtrlListView *m_feeds, MCONTACT Contact) + : CSuper(g_plugin, IDD_ADDFEED), + m_feedtitle(this, IDC_FEEDTITLE), m_feedurl(this, IDC_FEEDURL), + m_checktime(this, IDC_CHECKTIME), m_checktimespin(this, IDC_TIMEOUT_VALUE_SPIN, 999), + m_checkfeed(this, IDC_DISCOVERY), m_useauth(this, IDC_USEAUTH), + m_login(this, IDC_LOGIN), m_password(this, IDC_PASSWORD), + m_tagedit(this, IDC_TAGSEDIT), m_reset(this, IDC_RESET), + m_help(this, IDC_TAGHELP), m_ok(this, IDOK), m_iItem(iItem) +{ + m_list = m_feeds; + m_hContact = Contact; + m_checkfeed.OnClick = Callback(this, &CFeedEditor::OnCheckFeed); + m_useauth.OnChange = Callback(this, &CFeedEditor::OnUseAuth); + m_reset.OnClick = Callback(this, &CFeedEditor::OnReset); + m_help.OnClick = Callback(this, &CFeedEditor::OnHelp); + m_ok.OnClick = Callback(this, &CFeedEditor::OnOk); +} + +bool CFeedEditor::OnInitDialog() +{ + if (m_iItem == -1 && m_hContact == NULL) + SetWindowText(m_hwnd, TranslateT("Add Feed")); + else + SetWindowText(m_hwnd, TranslateT("Change Feed")); + m_checktime.SetMaxLength(3); + + if (m_iItem > -1 && m_hContact == 0) { + wchar_t SelNick[MAX_PATH], SelUrl[MAX_PACKAGE_NAME]; + m_list->GetItemText(m_iItem, 0, SelNick, _countof(SelNick)); + m_list->GetItemText(m_iItem, 1, SelUrl, _countof(SelNick)); + + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + if ((dbNick == NULL) || (mir_wstrcmp(dbNick, SelNick) != 0)) + continue; + + ptrW dbURL(g_plugin.getWStringA(hContact, "URL")); + if ((dbURL == NULL) || (mir_wstrcmp(dbURL, SelUrl) != 0)) + continue; + + m_hContact = hContact; + m_feedtitle.SetText(SelNick); + m_feedurl.SetText(SelUrl); + m_checktime.SetInt(g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)); + + ptrW szMsgFormat(g_plugin.getWStringA(hContact, "MsgFormat")); + if (szMsgFormat) + m_tagedit.SetText(szMsgFormat); + + if (g_plugin.getByte(hContact, "UseAuth", 0)) { + m_useauth.SetState(1); + m_login.Enable(); + m_password.Enable(); + + ptrW szLogin(g_plugin.getWStringA(hContact, "Login")); + if (szLogin) + m_login.SetText(szLogin); + + pass_ptrA pwd(g_plugin.getStringA(hContact, "Password")); + m_password.SetTextA(pwd); + } + g_arFeeds.insert(this); + Utils_RestoreWindowPositionNoSize(m_hwnd, hContact, MODULENAME, "ChangeDlg"); + break; + } + } + else if (m_iItem == -1 && m_hContact == NULL) { + m_feedurl.SetText(L"http://"); + m_tagedit.SetText(TAGSDEFAULT); + m_checktime.SetInt(DEFAULT_UPDATE_TIME); + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "AddDlg"); + } + else if (m_hContact != NULL) { + ptrW dbNick(g_plugin.getWStringA(m_hContact, "Nick")); + ptrW dbURL(g_plugin.getWStringA(m_hContact, "URL")); + + m_feedtitle.SetText(dbNick); + m_feedurl.SetText(dbURL); + m_checktime.SetInt(g_plugin.getDword(m_hContact, "UpdateTime", DEFAULT_UPDATE_TIME)); + + ptrW szMsgFormat(g_plugin.getWStringA(m_hContact, "MsgFormat")); + if (szMsgFormat) + m_tagedit.SetText(szMsgFormat); + + if (g_plugin.getByte(m_hContact, "UseAuth")) { + m_useauth.SetState(1); + m_login.Enable(); + m_password.Enable(); + + ptrW szLogin(g_plugin.getWStringA(m_hContact, "Login")); + if (szLogin) + m_login.SetText(szLogin); + + pass_ptrA pwd(g_plugin.getStringA(m_hContact, "Password")); + m_password.SetTextA(pwd); + } + g_arFeeds.insert(this); + Utils_RestoreWindowPositionNoSize(m_hwnd, m_hContact, MODULENAME, "ChangeDlg"); + } + return true; +} + +void CFeedEditor::OnCheckFeed(CCtrlBase*) +{ + m_checkfeed.Disable(); + m_checkfeed.SetText(TranslateT("Wait...")); + wchar_t *tszTitle = nullptr; + ptrW strfeedurl(m_feedurl.GetText()); + if (strfeedurl || mir_wstrcmp(strfeedurl, L"http://") != 0 || mir_wstrcmp(strfeedurl, L"") != 0) + tszTitle = (wchar_t*)CheckFeed(strfeedurl, this); + else + MessageBox(m_hwnd, TranslateT("Enter Feed URL"), TranslateT("Error"), MB_OK); + m_feedtitle.SetText(tszTitle); + mir_free(tszTitle); + m_checkfeed.Enable(); + m_checkfeed.SetText(TranslateT("Check Feed")); +} + +void CFeedEditor::OnReset(CCtrlBase*) +{ + if (MessageBox(m_hwnd, TranslateT("Are you sure?"), TranslateT("Tags Mask Reset"), MB_YESNO | MB_ICONWARNING) == IDYES) + m_tagedit.SetText(TAGSDEFAULT); +} + +void CFeedEditor::OnHelp(CCtrlBase*) +{ + CMStringW wszTagHelp; + wszTagHelp.Format(L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s", + L"#<title>#", TranslateT("The title of the item."), + L"#<description>#", TranslateT("The item synopsis."), + L"#<link>#", TranslateT("The URL of the item."), + L"#<author>#", TranslateT("Email address of the author of the item."), + L"#<comments>#", TranslateT("URL of a page for comments relating to the item."), + L"#<guid>#", TranslateT("A string that uniquely identifies the item."), + L"#<category>#", TranslateT("Specify one or more categories that the item belongs to.")); + MessageBox(m_hwnd, wszTagHelp, TranslateT("Feed Tag Help"), MB_OK); +} + +void CFeedEditor::OnOk(CCtrlBase*) +{ + ptrW strfeedtitle(m_feedtitle.GetText()); + if (!strfeedtitle || mir_wstrcmp(strfeedtitle, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter Feed name"), TranslateT("Error"), MB_OK); + return; + } + + ptrW strfeedurl(m_feedurl.GetText()); + if (!strfeedurl || mir_wstrcmp(strfeedurl, L"http://") == 0 || mir_wstrcmp(strfeedurl, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter Feed URL"), TranslateT("Error"), MB_OK); + return; + } + + ptrW strtagedit(m_tagedit.GetText()); + if (!strtagedit || mir_wstrcmp(strtagedit, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter message format"), TranslateT("Error"), MB_OK); + return; + } + + MCONTACT hContact; + if (m_iItem == -1 && m_hContact == NULL) { + hContact = db_add_contact(); + Proto_AddToContact(hContact, MODULENAME); + g_plugin.setByte(hContact, "CheckState", 1); + } + else hContact = m_hContact; + + g_plugin.setWString(hContact, "Nick", strfeedtitle); + g_plugin.setWString(hContact, "URL", strfeedurl); + g_plugin.setDword(hContact, "UpdateTime", m_checktime.GetInt()); + g_plugin.setWString(hContact, "MsgFormat", strtagedit); + g_plugin.setWord(hContact, "Status", Proto_GetStatus(MODULENAME)); + if (m_useauth.IsChecked()) { + g_plugin.setByte(hContact, "UseAuth", 1); + g_plugin.setWString(hContact, "Login", m_login.GetText()); + g_plugin.setString(hContact, "Password", m_password.GetTextA()); + } + else { + g_plugin.delSetting(hContact, "UseAuth"); + g_plugin.delSetting(hContact, "Login"); + g_plugin.delSetting(hContact, "Password"); + } + + if (m_iItem == -1 && m_list != nullptr && m_hContact == NULL) { + int iItem = m_list->AddItem(strfeedtitle, -1); + m_list->SetItem(iItem, 1, strfeedurl); + m_list->SetCheckState(iItem, 1); + } + else if (m_iItem > -1) { + m_list->SetItem(m_iItem, 0, strfeedtitle); + m_list->SetItem(m_iItem, 1, strfeedurl); + } +} + +bool CFeedEditor::OnClose() +{ + g_arFeeds.remove(this); + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, m_iItem == -1 ? "AddDlg" : "ChangeDlg"); + if (pAddFeedDialog == this) + pAddFeedDialog = nullptr; + return true; +} + +void CFeedEditor::OnUseAuth(CCtrlBase*) +{ + m_login.Enable(m_useauth.GetState()); + m_password.Enable(m_useauth.GetState()); +} + +void COptionsMain::UpdateList() +{ + for (auto &hContact : Contacts(MODULENAME)) { + UpdateListFlag = TRUE; + ptrW ptszNick(g_plugin.getWStringA(hContact, "Nick")); + if (ptszNick) { + int iItem = m_feeds.AddItem(ptszNick, -1); + + ptrW ptszURL(g_plugin.getWStringA(hContact, "URL")); + if (ptszURL) { + m_feeds.SetItem(iItem, 1, ptszURL); + m_feeds.SetCheckState(iItem, g_plugin.getByte(hContact, "CheckState", 1)); + } + } + } + UpdateListFlag = FALSE; +} + +COptionsMain::COptionsMain() : + CDlgBase(g_plugin, IDD_OPTIONS), + m_feeds(this, IDC_FEEDLIST), + m_add(this, IDC_ADD), + m_change(this, IDC_CHANGE), + m_delete(this, IDC_REMOVE), + m_import(this, IDC_IMPORT), + m_export(this, IDC_EXPORT), + m_checkonstartup(this, IDC_STARTUPRETRIEVE) +{ + CreateLink(m_checkonstartup, "StartupRetrieve", DBVT_BYTE, 1); + + m_add.OnClick = Callback(this, &COptionsMain::OnAddButtonClick); + m_change.OnClick = Callback(this, &COptionsMain::OnChangeButtonClick); + m_delete.OnClick = Callback(this, &COptionsMain::OnDeleteButtonClick); + m_import.OnClick = Callback(this, &COptionsMain::OnImportButtonClick); + m_export.OnClick = Callback(this, &COptionsMain::OnExportButtonClick); + + m_feeds.OnItemChanged = Callback(this, &COptionsMain::OnFeedListItemChanged); + m_feeds.OnDoubleClick = Callback(this, &COptionsMain::OnFeedListDoubleClick); + +} + +bool COptionsMain::OnInitDialog() +{ + CDlgBase::OnInitDialog(); + m_change.Disable(); + m_delete.Disable(); + m_feeds.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + m_feeds.AddColumn(0, TranslateT("Feed"), 160); + m_feeds.AddColumn(1, TranslateT("URL"), 276); + UpdateList(); + return true; +} + +bool COptionsMain::OnApply() +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + for (int i = 0; i < m_feeds.GetItemCount(); i++) { + wchar_t nick[MAX_PATH]; + m_feeds.GetItemText(i, 0, nick, _countof(nick)); + if (mir_wstrcmp(dbNick, nick) == 0) { + g_plugin.setByte(hContact, "CheckState", m_feeds.GetCheckState(i)); + if (!m_feeds.GetCheckState(i)) + db_set_b(hContact, "CList", "Hidden", 1); + else + db_unset(hContact, "CList", "Hidden"); + } + } + } + return true; +} + +void COptionsMain::OnAddButtonClick(CCtrlBase*) +{ + if (pAddFeedDialog == nullptr) { + pAddFeedDialog = new CFeedEditor(-1, &m_feeds, NULL); + pAddFeedDialog->SetParent(m_hwnd); + pAddFeedDialog->Show(); + } + else { + SetForegroundWindow(pAddFeedDialog->GetHwnd()); + SetFocus(pAddFeedDialog->GetHwnd()); + } +} + +void COptionsMain::OnChangeButtonClick(CCtrlBase*) +{ + int isel = m_feeds.GetSelectionMark(); + CFeedEditor *pDlg = nullptr; + for (auto &it : g_arFeeds) { + wchar_t nick[MAX_PATH], url[MAX_PATH]; + m_feeds.GetItemText(isel, 0, nick, _countof(nick)); + m_feeds.GetItemText(isel, 1, url, _countof(url)); + + ptrW dbNick(g_plugin.getWStringA(it->getContact(), "Nick")); + if ((dbNick == NULL) || (mir_wstrcmp(dbNick, nick) != 0)) + continue; + + ptrW dbURL(g_plugin.getWStringA(it->getContact(), "URL")); + if ((dbURL == NULL) || (mir_wstrcmp(dbURL, url) != 0)) + continue; + + pDlg = it; + } + + if (pDlg == nullptr) { + pDlg = new CFeedEditor(isel, &m_feeds, NULL); + pDlg->SetParent(m_hwnd); + pDlg->Show(); + } + else { + SetForegroundWindow(pDlg->GetHwnd()); + SetFocus(pDlg->GetHwnd()); + } +} + +void COptionsMain::OnDeleteButtonClick(CCtrlBase*) +{ + if (MessageBox(m_hwnd, TranslateT("Are you sure?"), TranslateT("Contact deleting"), MB_YESNO | MB_ICONWARNING) == IDYES) { + wchar_t nick[MAX_PATH], url[MAX_PATH]; + int isel = m_feeds.GetSelectionMark(); + m_feeds.GetItemText(isel, 0, nick, _countof(nick)); + m_feeds.GetItemText(isel, 1, url, _countof(url)); + + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + if (dbNick == NULL) + break; + if (mir_wstrcmp(dbNick, nick)) + continue; + + ptrW dbURL(g_plugin.getWStringA(hContact, "URL")); + if (dbURL == NULL) + break; + if (mir_wstrcmp(dbURL, url)) + continue; + + db_delete_contact(hContact); + m_feeds.DeleteItem(isel); + break; + } + } +} + +void COptionsMain::OnImportButtonClick(CCtrlBase*) +{ + if (pImportDialog == nullptr) { + pImportDialog = new CImportFeed(&m_feeds); + pImportDialog->Show(); + pImportDialog->SetParent(m_hwnd); + } +} + +void COptionsMain::OnExportButtonClick(CCtrlBase*) +{ + if (pExportDialog == nullptr) { + pExportDialog = new CExportFeed(); + pExportDialog->Show(); + pExportDialog->SetParent(m_hwnd); + } +} + +void COptionsMain::OnFeedListItemChanged(CCtrlListView::TEventInfo *evt) +{ + int isel = m_feeds.GetSelectionMark(); + if (isel == -1) { + m_change.Disable(); + m_delete.Disable(); + } + else { + m_change.Enable(); + m_delete.Enable(); + } + if (((evt->nmlv->uNewState ^ evt->nmlv->uOldState) & LVIS_STATEIMAGEMASK) && !UpdateListFlag) + NotifyChange(); +} + +void COptionsMain::OnFeedListDoubleClick(CCtrlBase*) +{ + int isel = m_feeds.GetHotItem(); + if (isel != -1) { + CFeedEditor *pDlg = new CFeedEditor(isel, &m_feeds, 0); + pDlg->SetParent(m_hwnd); + pDlg->Show(); + } +} + +int OptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; + odp.szGroup.w = LPGENW("Network"); + odp.szTitle.w = LPGENW("News Aggregator"); + odp.pDialog = new COptionsMain(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Options.h b/protocols/NewsAggregator/Src/Options.h new file mode 100644 index 0000000000..2389eb9bb9 --- /dev/null +++ b/protocols/NewsAggregator/Src/Options.h @@ -0,0 +1,162 @@ +#ifndef _OPTIONS_H_ +#define _OPTIONS_H_ + +class COptionsMain : public CDlgBase +{ +private: + CCtrlListView m_feeds; + CCtrlButton m_add; + CCtrlButton m_change; + CCtrlButton m_delete; + CCtrlButton m_import; + CCtrlButton m_export; + CCtrlCheck m_checkonstartup; + +protected: + bool OnInitDialog() override; + bool OnApply() override; + + void OnAddButtonClick(CCtrlBase*); + void OnChangeButtonClick(CCtrlBase*); + void OnDeleteButtonClick(CCtrlBase*); + void OnImportButtonClick(CCtrlBase*); + void OnExportButtonClick(CCtrlBase*); + + void OnFeedListItemChanged(CCtrlListView::TEventInfo *evt); + void OnFeedListDoubleClick(CCtrlBase*); + + void UpdateList(); + +public: + COptionsMain(); +}; + +class CFeedEditor : public CDlgBase +{ + friend class CAuthRequest; + +private: + typedef CDlgBase CSuper; + + int m_iItem; + CCtrlListView *m_list; + MCONTACT m_hContact; + + CCtrlEdit m_feedtitle; + CCtrlEdit m_feedurl; + CCtrlEdit m_checktime; + CCtrlSpin m_checktimespin; + CCtrlButton m_checkfeed; + CCtrlEdit m_tagedit; + CCtrlButton m_reset; + CCtrlButton m_help; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnCheckFeed(CCtrlBase*); + void OnReset(CCtrlBase*); + void OnHelp(CCtrlBase*); + void OnOk(CCtrlBase*); + void OnUseAuth(CCtrlBase*); + +public: + CCtrlCheck m_useauth; + CCtrlEdit m_login; + CCtrlEdit m_password; + + CFeedEditor(int iItem, CCtrlListView *m_list, MCONTACT Contact); + + __inline MCONTACT getContact() const { return m_hContact; } +}; + +class CImportFeed : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CCtrlListView *m_list; + + CCtrlEdit m_importfile; + CCtrlButton m_browsefile; + CCtrlListBox m_feedslist; + CCtrlListBox m_feedsimportlist; + CCtrlButton m_addfeed; + CCtrlButton m_removefeed; + CCtrlButton m_addallfeeds; + CCtrlButton m_removeallfeeds; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnBrowseFile(CCtrlBase*); + void OnAddFeed(CCtrlBase*); + void OnRemoveFeed(CCtrlBase*); + void OnAddAllFeeds(CCtrlBase*); + void OnRemoveAllFeeds(CCtrlBase*); + void OnOk(CCtrlBase*); + + void OnFeedsList(CCtrlBase*); + void OnFeedsImportList(CCtrlBase*); + +public: + CImportFeed(CCtrlListView *m_list); +}; + +class CExportFeed : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CCtrlListBox m_feedslist; + CCtrlListBox m_feedsexportlist; + CCtrlButton m_addfeed; + CCtrlButton m_removefeed; + CCtrlButton m_addallfeeds; + CCtrlButton m_removeallfeeds; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnAddFeed(CCtrlBase*); + void OnRemoveFeed(CCtrlBase*); + void OnAddAllFeeds(CCtrlBase*); + void OnRemoveAllFeeds(CCtrlBase*); + void OnOk(CCtrlBase*); + + void OnFeedsList(CCtrlBase*); + void OnFeedsExportList(CCtrlBase*); + +public: + CExportFeed(); +}; + +class CAuthRequest : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CFeedEditor *m_pDlg; + MCONTACT m_hContact; + + CCtrlBase m_feedname; + CCtrlEdit m_username; + CCtrlEdit m_password; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + + void OnOk(CCtrlBase*); + +public: + CAuthRequest(CFeedEditor *pDlg, MCONTACT hContact); +}; + +#endif //_OPTIONS_H_
\ No newline at end of file diff --git a/protocols/NewsAggregator/Src/Services.cpp b/protocols/NewsAggregator/Src/Services.cpp new file mode 100644 index 0000000000..b5b8c9f028 --- /dev/null +++ b/protocols/NewsAggregator/Src/Services.cpp @@ -0,0 +1,261 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +int g_nStatus = ID_STATUS_ONLINE; +UINT_PTR timerId = 0; +HANDLE hTBButton = nullptr, hNewsAggregatorFolder = nullptr; + +int OnFoldersChanged(WPARAM, LPARAM) +{ + FoldersGetCustomPathT(hNewsAggregatorFolder, tszRoot, MAX_PATH, L""); + return 0; +} + +int NewsAggrInit(WPARAM, LPARAM) +{ + if (hNewsAggregatorFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("News Aggregator"), MIRANDA_USERDATAT L"\\Avatars\\" _A2W(DEFAULT_AVATARS_FOLDER))) + FoldersGetCustomPathT(hNewsAggregatorFolder, tszRoot, MAX_PATH, L""); + else + mir_wstrncpy(tszRoot, VARSW(L"%miranda_userdata%\\Avatars\\" _A2W(DEFAULT_AVATARS_FOLDER)), _countof(tszRoot)); + + for (auto &hContact : Contacts(MODULENAME)) { + if (!g_plugin.getByte("StartupRetrieve", 1)) + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + g_plugin.setWord(hContact, "Status", ID_STATUS_ONLINE); + } + + NetlibInit(); + InitMenu(); + + HookEvent(ME_TTB_MODULELOADED, OnToolbarLoaded); + HookEvent(ME_FOLDERS_PATH_CHANGED, OnFoldersChanged); + + // timer for the first update + timerId = SetTimer(nullptr, 0, 10000, timerProc2); // first update is 10 sec after load + + return 0; +} + +int NewsAggrPreShutdown(WPARAM, LPARAM) +{ + KillTimer(nullptr, timerId); + NetlibUnInit(); + return 0; +} + +INT_PTR NewsAggrGetName(WPARAM wParam, LPARAM lParam) +{ + if(lParam) { + mir_strncpy((char *)lParam, MODULENAME, wParam); + return 0; + } + + return 1; +} + +INT_PTR NewsAggrGetCaps(WPARAM wp, LPARAM) +{ + switch(wp) { + case PFLAGNUM_1: + return PF1_IM | PF1_PEER2PEER; + case PFLAGNUM_3: + case PFLAGNUM_2: + return PF2_ONLINE; + case PFLAGNUM_4: + return PF4_AVATARS; + case PFLAG_UNIQUEIDTEXT: + return (INT_PTR) "News Feed"; + default: + return 0; + } +} + +INT_PTR NewsAggrSetStatus(WPARAM wp, LPARAM) +{ + int nStatus = (int)wp; + if ((ID_STATUS_ONLINE == nStatus) || (ID_STATUS_OFFLINE == nStatus)) { + int nOldStatus = g_nStatus; + if(nStatus != g_nStatus) { + g_nStatus = nStatus; + + for (auto &hContact : Contacts(MODULENAME)) + g_plugin.setWord(hContact, "Status", nStatus); + + ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)nOldStatus, (LPARAM)g_nStatus); + } + } + + return 0; +} + +INT_PTR NewsAggrGetStatus(WPARAM, LPARAM) +{ + return g_nStatus; +} + +INT_PTR NewsAggrLoadIcon(WPARAM wParam, LPARAM) +{ + return (LOWORD(wParam) == PLI_PROTOCOL) ? (INT_PTR)CopyIcon(LoadIconEx("main", FALSE)) : 0; +} + +static void __cdecl AckThreadProc(void *param) +{ + Sleep(100); + ProtoBroadcastAck(MODULENAME, (MCONTACT)param, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1); +} + +INT_PTR NewsAggrGetInfo(WPARAM, LPARAM lParam) +{ + CCSDATA *ccs = (CCSDATA *)lParam; + mir_forkthread(AckThreadProc, (void*)ccs->hContact); + return 0; +} + +INT_PTR CheckAllFeeds(WPARAM, LPARAM lParam) +{ + for (auto &hContact : Contacts(MODULENAME)) { + if (lParam && g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) + UpdateListAdd(hContact); + else if (!lParam) + UpdateListAdd(hContact); + } + if (!ThreadRunning) + mir_forkthread(UpdateThreadProc); + + return 0; +} + +INT_PTR AddFeed(WPARAM, LPARAM) +{ + if (pAddFeedDialog == nullptr) { + pAddFeedDialog = new CFeedEditor(-1, nullptr, NULL); + pAddFeedDialog->Show(); + } + else { + SetForegroundWindow(pAddFeedDialog->GetHwnd()); + SetFocus(pAddFeedDialog->GetHwnd()); + } + return 0; +} + +INT_PTR ChangeFeed(WPARAM hContact, LPARAM) +{ + CFeedEditor *pDlg = nullptr; + for (auto &it : g_arFeeds) + if (it->getContact() == hContact) + pDlg = it; + + if (pDlg == nullptr) { + pDlg = new CFeedEditor(-1, nullptr, (MCONTACT)hContact); + pDlg->Show(); + } + else { + SetForegroundWindow(pDlg->GetHwnd()); + SetFocus(pDlg->GetHwnd()); + } + return 0; +} + +INT_PTR ImportFeeds(WPARAM, LPARAM) +{ + if (pImportDialog == nullptr) + pImportDialog = new CImportFeed(nullptr); + pImportDialog->Show(); + return 0; +} + +INT_PTR ExportFeeds(WPARAM, LPARAM) +{ + if (pExportDialog == nullptr) + pExportDialog = new CExportFeed(); + pExportDialog->Show(); + return 0; +} + +INT_PTR CheckFeed(WPARAM hContact, LPARAM) +{ + if(IsMyContact((MCONTACT)hContact)) + UpdateListAdd((MCONTACT)hContact); + if ( !ThreadRunning) + mir_forkthread(UpdateThreadProc); + return 0; +} + +INT_PTR NewsAggrGetAvatarInfo(WPARAM wParam, LPARAM lParam) +{ + PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam; + if (!IsMyContact(pai->hContact)) + return GAIR_NOAVATAR; + + // if GAIF_FORCE is set, we are updating the feed + // otherwise, cached avatar is used + if ((wParam & GAIF_FORCE) && g_plugin.getDword(pai->hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) + UpdateListAdd(pai->hContact); + if (g_plugin.getByte("AutoUpdate", 1) != 0 && !ThreadRunning) + mir_forkthread(UpdateThreadProc, (void *)TRUE); + + ptrW ptszImageURL(g_plugin.getWStringA(pai->hContact, "ImageURL")); + return (ptszImageURL == nullptr) ? GAIR_NOAVATAR : GAIR_WAITFOR; +} + +INT_PTR NewsAggrRecvMessage(WPARAM, LPARAM lParam) +{ + PROTOACCOUNT *pa = Proto_GetAccount(MODULENAME); + if (pa && pa->ppro) { + CCSDATA *ccs = (CCSDATA*)lParam; + pa->ppro->PROTO_INTERFACE::RecvMsg(ccs->hContact, (PROTORECVEVENT*)ccs->lParam); + } + + return 0; +} + +void UpdateMenu(bool State) +{ + if (!State) // to enable auto-update + Menu_ModifyItem(hService2[0], LPGENW("Auto Update Enabled"), GetIconHandle("enabled")); + else // to disable auto-update + Menu_ModifyItem(hService2[0], LPGENW("Auto Update Disabled"), GetIconHandle("disabled")); + + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTBButton, State ? TTBST_PUSHED : 0); + g_plugin.setByte("AutoUpdate", !State); +} + +// update the newsaggregator auto-update menu item when click on it +INT_PTR EnableDisable(WPARAM, LPARAM) +{ + UpdateMenu(g_plugin.getByte("AutoUpdate", 1) != 0); + NewsAggrSetStatus(g_plugin.getByte("AutoUpdate", 1) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE, 0); + return 0; +} + +int OnToolbarLoaded(WPARAM, LPARAM) +{ + TTBButton ttb = {}; + ttb.name = LPGEN("Enable/disable auto update"); + ttb.pszService = MS_NEWSAGGREGATOR_ENABLED; + ttb.pszTooltipUp = LPGEN("Auto Update Enabled"); + ttb.pszTooltipDn = LPGEN("Auto Update Disabled"); + ttb.hIconHandleUp = GetIconHandle("enabled"); + ttb.hIconHandleDn = GetIconHandle("disabled"); + ttb.dwFlags = (g_plugin.getByte("AutoUpdate", 1) ? 0 : TTBBF_PUSHED) | TTBBF_ASPUSHBUTTON | TTBBF_VISIBLE; + hTBButton = g_plugin.addTTB(&ttb); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Update.cpp b/protocols/NewsAggregator/Src/Update.cpp new file mode 100644 index 0000000000..96990dbcd2 --- /dev/null +++ b/protocols/NewsAggregator/Src/Update.cpp @@ -0,0 +1,139 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +// check if Feed is currently updating +bool ThreadRunning; +UPDATELIST *UpdateListHead = nullptr; +UPDATELIST *UpdateListTail = nullptr; + +// main auto-update timer +void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD) +{ + // only run if it is not current updating and the auto update option is enabled + if (!ThreadRunning && !Miranda_IsTerminated()) { + bool HaveUpdates = FALSE; + for (auto &hContact : Contacts(MODULENAME)) { + if (g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) { + double diff = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck", 0)); + if (g_plugin.getByte("AutoUpdate", 1) != 0 && diff >= g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME) * 60) { + UpdateListAdd(hContact); + HaveUpdates = TRUE; + } + } + } + if (!ThreadRunning && HaveUpdates) + mir_forkthread(UpdateThreadProc); + } +} + +// temporary timer for first run +// when this is run, it kill the old startup timer and create the permenant one above +void CALLBACK timerProc2(HWND, UINT, UINT_PTR, DWORD) +{ + KillTimer(nullptr, timerId); + ThreadRunning = FALSE; + + if (g_plugin.getByte("AutoUpdate", 1) && !Miranda_IsTerminated()) { + if (g_plugin.getByte("StartupRetrieve", 1)) + CheckAllFeeds(0, 1); + timerId = SetTimer(nullptr, 0, 30000, (TIMERPROC)timerProc); + } +} + +void UpdateListAdd(MCONTACT hContact) +{ + UPDATELIST *newItem = (UPDATELIST*)mir_alloc(sizeof(UPDATELIST)); + newItem->hContact = hContact; + newItem->next = nullptr; + + WaitForSingleObject(hUpdateMutex, INFINITE); + + if (UpdateListTail == nullptr) + UpdateListHead = newItem; + else UpdateListTail->next = newItem; + UpdateListTail = newItem; + + ReleaseMutex(hUpdateMutex); +} + +MCONTACT UpdateGetFirst() +{ + MCONTACT hContact = NULL; + + WaitForSingleObject(hUpdateMutex, INFINITE); + + if (UpdateListHead != nullptr) { + UPDATELIST* Item = UpdateListHead; + hContact = Item->hContact; + UpdateListHead = Item->next; + mir_free(Item); + + if (UpdateListHead == nullptr) + UpdateListTail = nullptr; + } + + ReleaseMutex(hUpdateMutex); + + return hContact; +} + +void DestroyUpdateList(void) +{ + WaitForSingleObject(hUpdateMutex, INFINITE); + + // free the list one by one + UPDATELIST *temp = UpdateListHead; + while (temp != nullptr) { + UpdateListHead = temp->next; + mir_free(temp); + temp = UpdateListHead; + } + // make sure the entire list is clear + UpdateListTail = nullptr; + + ReleaseMutex(hUpdateMutex); +} + +void UpdateThreadProc(void *AvatarCheck) +{ + WaitForSingleObject(hUpdateMutex, INFINITE); + if (ThreadRunning) { + ReleaseMutex(hUpdateMutex); + return; + } + ThreadRunning = TRUE; // prevent 2 instance of this thread running + ReleaseMutex(hUpdateMutex); + + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + // update news by getting the first station from the queue until the queue is empty + while (UpdateListHead != nullptr && !Miranda_IsTerminated()) { + if (AvatarCheck != nullptr) + CheckCurrentFeedAvatar(UpdateGetFirst()); + else + CheckCurrentFeed(UpdateGetFirst()); + } + + // exit the update thread + ThreadRunning = FALSE; + + CoUninitialize(); +} diff --git a/protocols/NewsAggregator/Src/Utils.cpp b/protocols/NewsAggregator/Src/Utils.cpp new file mode 100644 index 0000000000..80248ba48f --- /dev/null +++ b/protocols/NewsAggregator/Src/Utils.cpp @@ -0,0 +1,445 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HNETLIBUSER hNetlibUser = nullptr; +HNETLIBCONN hNetlibHttp; +bool UpdateListFlag = FALSE; + +bool IsMyContact(MCONTACT hContact) +{ + const char *szProto = GetContactProto(hContact); + return szProto != nullptr && mir_strcmp(MODULENAME, szProto) == 0; +} + +void NetlibInit() +{ + NETLIBUSER nlu = {}; + nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; + nlu.szDescriptiveName.w = TranslateT("NewsAggregator HTTP connections"); + nlu.szSettingsModule = MODULENAME; + hNetlibUser = Netlib_RegisterUser(&nlu); +} + +void NetlibUnInit() +{ + Netlib_CloseHandle(hNetlibUser); + hNetlibUser = nullptr; +} + +void GetNewsData(wchar_t *tszUrl, char **szData, MCONTACT hContact, CFeedEditor *pEditDlg) +{ + Netlib_LogfW(hNetlibUser, L"Getting feed data %s.", tszUrl); + NETLIBHTTPREQUEST nlhr = { 0 }; + + // initialize the netlib request + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11 | NLHRF_REDIRECT; + if (wcsstr(tszUrl, L"https://") != nullptr) + nlhr.flags |= NLHRF_SSL; + char *szUrl = mir_u2a(tszUrl); + nlhr.szUrl = szUrl; + nlhr.nlc = hNetlibHttp; + + // change the header so the plugin is pretended to be IE 6 + WinXP + NETLIBHTTPHEADER headers[5]; + nlhr.headersCount = 4; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = NETLIB_USER_AGENT; + nlhr.headers[1].szName = "Cache-Control"; + nlhr.headers[1].szValue = "no-cache"; + nlhr.headers[2].szName = "Pragma"; + nlhr.headers[2].szValue = "no-cache"; + nlhr.headers[3].szName = "Connection"; + nlhr.headers[3].szValue = "close"; + char auth[256]; + if (g_plugin.getByte(hContact, "UseAuth", 0) || (pEditDlg && pEditDlg->m_useauth.IsChecked()) /*IsDlgButtonChecked(hwndDlg, IDC_USEAUTH)*/) { + nlhr.headersCount++; + nlhr.headers[4].szName = "Authorization"; + + CreateAuthString(auth, hContact, pEditDlg); + nlhr.headers[4].szValue = auth; + } + + // download the page + NETLIBHTTPREQUEST *nlhrReply = Netlib_HttpTransaction(hNetlibUser, &nlhr); + if (nlhrReply) { + // if the recieved code is 200 OK + if (nlhrReply->resultCode == 200 && nlhrReply->dataLength > 0) { + Netlib_LogfW(hNetlibUser, L"Code 200: Succeeded getting feed data %s.", tszUrl); + // allocate memory and save the retrieved data + *szData = (char *)mir_alloc((size_t)(nlhrReply->dataLength + 2)); + memcpy(*szData, nlhrReply->pData, (size_t)nlhrReply->dataLength); + (*szData)[nlhrReply->dataLength] = 0; + } + else if (nlhrReply->resultCode == 401) { + Netlib_LogfW(hNetlibUser, L"Code 401: feed %s needs auth data.", tszUrl); + + if (CAuthRequest(pEditDlg, hContact).DoModal() == IDOK) + GetNewsData(tszUrl, szData, hContact, pEditDlg); + } + else Netlib_LogfW(hNetlibUser, L"Code %d: Failed getting feed data %s.", nlhrReply->resultCode, tszUrl); + + Netlib_FreeHttpRequest(nlhrReply); + } + else Netlib_LogfW(hNetlibUser, L"Failed getting feed data %s, no response.", tszUrl); + + mir_free(szUrl); +} + +time_t DateToUnixTime(const char *stamp, bool FeedType) +{ + struct tm timestamp; + char date[9]; + int i, y; + time_t t; + + if (stamp == nullptr) + return 0; + + char *p = NEWSTR_ALLOCA(stamp); + + if (FeedType) { + // skip '-' chars + int si = 0, sj = 0; + while (true) { + if (p[si] == '-') + si++; + else if (!(p[sj++] = p[si++])) + break; + } + } + else { + char monthstr[4], timezonesign[2]; + int day, month = 0, year, hour, min, sec, timezoneh, timezonem; + if (strchr(p, ',')) { + strtok(p, ","); + p = strtok(nullptr, ","); + sscanf(p + 1, "%d %3s %d %d:%d:%d %1s%02d%02d", &day, &monthstr, &year, &hour, &min, &sec, &timezonesign, &timezoneh, &timezonem); + if (!mir_strcmpi(monthstr, "Jan")) + month = 1; + if (!mir_strcmpi(monthstr, "Feb")) + month = 2; + if (!mir_strcmpi(monthstr, "Mar")) + month = 3; + if (!mir_strcmpi(monthstr, "Apr")) + month = 4; + if (!mir_strcmpi(monthstr, "May")) + month = 5; + if (!mir_strcmpi(monthstr, "Jun")) + month = 6; + if (!mir_strcmpi(monthstr, "Jul")) + month = 7; + if (!mir_strcmpi(monthstr, "Aug")) + month = 8; + if (!mir_strcmpi(monthstr, "Sep")) + month = 9; + if (!mir_strcmpi(monthstr, "Oct")) + month = 10; + if (!mir_strcmpi(monthstr, "Nov")) + month = 11; + if (!mir_strcmpi(monthstr, "Dec")) + month = 12; + if (year < 2000) + year += 2000; + if (!mir_strcmp(timezonesign, "+")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour - timezoneh, min - timezonem, sec); + else if (!mir_strcmp(timezonesign, "-")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour + timezoneh, min + timezonem, sec); + else + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + else if (strchr(p, 'T')) { + sscanf(p, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &min, &sec); + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + else { + sscanf(p, "%d-%d-%d %d:%d:%d %1s%02d%02d", &year, &month, &day, &hour, &min, &sec, &timezonesign, &timezoneh, &timezonem); + if (!mir_strcmp(timezonesign, "+")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour - timezoneh, min - timezonem, sec); + else if (!mir_strcmp(timezonesign, "-")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour + timezoneh, min + timezonem, sec); + else + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + } + // Get the date part + for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++) + date[i] = *p; + + // Parse year + if (i == 6) { + // 2-digit year ( 1970-2069 ) + y = (date[0] - '0') * 10 + (date[1] - '0'); + if (y < 70) + y += 100; + } + else if (i == 8) { + // 4-digit year + y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0'; + y -= 1900; + } + else return 0; + + timestamp.tm_year = y; + // Parse month + timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1; + // Parse date + timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0'; + + // Skip any date/time delimiter + for (; *p != '\0' && !isdigit(*p); p++); + + // Parse time + if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3) + return 0; + + timestamp.tm_isdst = 0; // DST is already present in _timezone below + t = mktime(×tamp); + + _tzset(); + t -= (time_t)_timezone; + return (t >= 0) ? t : 0; +} + +bool DownloadFile(LPCTSTR tszURL, LPCTSTR tszLocal) +{ + NETLIBHTTPREQUEST nlhr = { 0 }; + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11; + char *szUrl = mir_u2a(tszURL); + nlhr.szUrl = szUrl; + NETLIBHTTPHEADER headers[4]; + nlhr.headersCount = 4; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = NETLIB_USER_AGENT; + nlhr.headers[1].szName = "Connection"; + nlhr.headers[1].szValue = "close"; + nlhr.headers[2].szName = "Cache-Control"; + nlhr.headers[2].szValue = "no-cache"; + nlhr.headers[3].szName = "Pragma"; + nlhr.headers[3].szValue = "no-cache"; + + bool ret = false; + NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(hNetlibUser, &nlhr); + if (pReply) { + if ((200 == pReply->resultCode) && (pReply->dataLength > 0)) { + char *date = nullptr, *size = nullptr; + for (int i = 0; i < pReply->headersCount; i++) { + if (!mir_strcmpi(pReply->headers[i].szName, "Last-Modified")) { + date = pReply->headers[i].szValue; + continue; + } + else if (!mir_strcmpi(pReply->headers[i].szName, "Content-Length")) { + size = pReply->headers[i].szValue; + continue; + } + } + if (date != nullptr && size != nullptr) { + wchar_t *tsize = mir_a2u(size); + struct _stat buf; + + int fh = _wopen(tszLocal, _O_RDONLY); + if (fh != -1) { + _fstat(fh, &buf); + time_t modtime = DateToUnixTime(date, 0); + time_t filemodtime = mktime(localtime(&buf.st_atime)); + if (modtime > filemodtime && buf.st_size != _wtoi(tsize)) { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + _close(fh); + } + else { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + mir_free(tsize); + } + else { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + } + Netlib_FreeHttpRequest(pReply); + } + + mir_free(szUrl); + + return ret; +} + +typedef HRESULT(MarkupCallback)(IHTMLDocument3 *, BSTR &message); + +HRESULT TestMarkupServices(BSTR bstrHtml, MarkupCallback *pCallback, BSTR &message) +{ + IHTMLDocument3 *pHtmlDocRoot = nullptr; + + // Create the root document -- a "workspace" for parsing. + HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pHtmlDocRoot)); + if (SUCCEEDED(hr) && pHtmlDocRoot) { + IPersistStreamInit *pPersistStreamInit = nullptr; + + hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pPersistStreamInit)); + if (SUCCEEDED(hr)) { + // Initialize the root document to a default state -- ready for parsing. + pPersistStreamInit->InitNew(); + + IMarkupServices *pMarkupServices = nullptr; + hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pMarkupServices)); + if (SUCCEEDED(hr)) { + IMarkupPointer *pMarkupBegin = nullptr; + IMarkupPointer *pMarkupEnd = nullptr; + + // These markup pointers indicate the insertion point. + hr = pMarkupServices->CreateMarkupPointer(&pMarkupBegin); + if (SUCCEEDED(hr)) + hr = pMarkupServices->CreateMarkupPointer(&pMarkupEnd); + + if (SUCCEEDED(hr) && pMarkupBegin && pMarkupEnd) { + IMarkupContainer *pMarkupContainer = nullptr; + + // Parse the string -- the markup container contains the parsed HTML. + // Markup pointers are updated to point to begining and end of new container. + hr = pMarkupServices->ParseString(bstrHtml, 0, &pMarkupContainer, pMarkupBegin, pMarkupEnd); + if (SUCCEEDED(hr) && pMarkupContainer) { + IHTMLDocument3 *pHtmlDoc = nullptr; + + // Retrieve the document interface to the markup container. + hr = pMarkupContainer->QueryInterface(IID_PPV_ARGS(&pHtmlDoc)); + if (SUCCEEDED(hr) && pHtmlDoc) { + // Invoke the user-defined action for this new fragment. + hr = pCallback(pHtmlDoc, message); + + // Clean up. + pHtmlDoc->Release(); + } + pMarkupContainer->Release(); + } + pMarkupEnd->Release(); + } + if (pMarkupBegin) + pMarkupBegin->Release(); + pMarkupServices->Release(); + } + pPersistStreamInit->Release(); + } + pHtmlDocRoot->Release(); + } + return hr; +} + +HRESULT TestDocumentText(IHTMLDocument3 *pHtmlDoc, BSTR &message) +{ + IHTMLDocument2 *pDoc = nullptr; + IHTMLElement *pElem = nullptr; + BSTR bstrId = SysAllocString(L"test"); + + HRESULT hr = pHtmlDoc->QueryInterface(IID_PPV_ARGS(&pDoc)); + if (SUCCEEDED(hr) && pDoc) { + hr = pDoc->get_body(&pElem); + if (SUCCEEDED(hr) && pElem) { + BSTR bstrText = nullptr; + pElem->get_innerText(&bstrText); + message = SysAllocString(bstrText); + SysFreeString(bstrText); + pElem->Release(); + } + + pDoc->Release(); + } + + SysFreeString(bstrId); + return hr; +} + +LPCTSTR ClearText(CMStringW &result, const wchar_t *message) +{ + BSTR bstrHtml = SysAllocString(message), bstrRes = SysAllocString(L""); + HRESULT hr = TestMarkupServices(bstrHtml, &TestDocumentText, bstrRes); + if (SUCCEEDED(hr)) + result = bstrRes; + else + result = message; + SysFreeString(bstrHtml); + SysFreeString(bstrRes); + + result.Replace(L"£", L"£"); //pound + result.Replace(L"²", L"²"); //sup2 + result.Replace(L"ä", L"ä"); //auml + result.Replace(L"é", L"é"); //latin small letter e with acute + result.Replace(L"ë", L"ë"); //euml + result.Replace(L"ö", L"ö"); //ouml + result.Replace(L"ž", L"ž"); //Latin Small Letter Z With Caron + result.Replace(L"ʙ", L"ʙ"); //latin letter small capital b + result.Replace(L"̆", L"˘"); //Combining Breve + result.Replace(L"́", L"´"); //Combining Acute Accent острое ударение + result.Replace(L"ο", L"ό"); // greek small letter omicron with tonos + result.Replace(L"ѣ", L"ѣ"); //Cyrillic Small Letter Yat + result.Replace(L"Ҝ", L"Ҝ"); //cyrillic capital letter ka with vertical stroke + result.Replace(L"​", L""); + result.Replace(L"‎", L""); //lrm + result.Replace(L"‏", L""); //rlm + result.Replace(L"‑", L"‑"); //Non-Breaking Hyphen + result.Replace(L"‣", L"‣"); //Triangular Bullet + result.Replace(L"−", L"−"); //minus + result.Replace(L"☺", L"☺"); //White Smiling Face + result.Replace(L"", L""); + + result.Trim(); + + return result; +} + +MCONTACT GetContactByNick(const wchar_t *nick) +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW contactNick(g_plugin.getWStringA(hContact, "Nick")); + if (!mir_wstrcmpi(contactNick, nick)) + return hContact; + } + return 0; +} + +MCONTACT GetContactByURL(const wchar_t *url) +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW contactURL(g_plugin.getWStringA(hContact, "URL")); + if (!mir_wstrcmpi(contactURL, url)) + return hContact; + } + return 0; +} diff --git a/protocols/NewsAggregator/Src/resource.h b/protocols/NewsAggregator/Src/resource.h new file mode 100644 index 0000000000..131a0844ba --- /dev/null +++ b/protocols/NewsAggregator/Src/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by c:\Temp\Miranda NG\plugins\NewsAggregator\res\Resource.rc +// +#define IDD_OPTIONS 101 +#define IDD_AUTHENTICATION 102 +#define IDI_ICON 109 +#define IDD_ADDFEED 110 +#define IDI_CHECKALL 111 +#define IDI_ADDFEED 112 +#define IDI_IMPORTFEEDS 113 +#define IDI_EXPORTFEEDS 114 +#define IDI_ENABLED 115 +#define IDI_DISABLED 116 +#define IDD_FEEDEXPORT 140 +#define IDD_FEEDIMPORT 141 +#define IDC_TIMEOUT_VALUE_SPIN 1035 +#define IDC_FEEDLIST 1036 +#define IDC_ADD 1037 +#define IDC_CHANGE 1038 +#define IDC_REMOVE 1039 +#define IDC_IMPORT 1040 +#define IDC_EXPORT 1041 +#define IDC_FEEDTITLE 1042 +#define IDC_FEEDURL 1043 +#define IDC_CHECKTIME 1044 +#define IDC_DISCOVERY 1045 +#define IDC_USEAUTH 1046 +#define IDC_LOGIN 1047 +#define IDC_PASSWORD 1048 +#define IDC_TAGSEDIT 1049 +#define IDC_RESET 1050 +#define IDC_TAGHELP 1051 +#define IDC_STARTUPRETRIEVE 1052 +#define IDC_FEEDUSERNAME 1105 +#define IDC_FEEDPASSWORD 1106 +#define IDC_FEEDSLIST 1108 +#define IDC_FEEDSEXPORTLIST 1109 +#define IDC_ADDFEED 1110 +#define IDC_REMOVEFEED 1111 +#define IDC_ADDALLFEEDS 1112 +#define IDC_REMOVEALLFEEDS 1113 +#define IDC_IMPORTFILEPATH 1114 +#define IDC_BROWSEIMPORTFILE 1115 +#define IDC_FEEDSIMPORTLIST 1117 +#define IDC_FEEDNAME 1124 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_COMMAND_VALUE 40075 +#define _APS_NEXT_CONTROL_VALUE 1053 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/protocols/NewsAggregator/Src/stdafx.cxx b/protocols/NewsAggregator/Src/stdafx.cxx new file mode 100644 index 0000000000..1b563fc866 --- /dev/null +++ b/protocols/NewsAggregator/Src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h"
\ No newline at end of file diff --git a/protocols/NewsAggregator/Src/stdafx.h b/protocols/NewsAggregator/Src/stdafx.h new file mode 100644 index 0000000000..819e96d69f --- /dev/null +++ b/protocols/NewsAggregator/Src/stdafx.h @@ -0,0 +1,167 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#pragma once + +// Windows Header Files: +#include <windows.h> +#include <commctrl.h> +#include <time.h> +#include <malloc.h> +#include <fcntl.h> +#include <io.h> +#include <sys/stat.h> +#include <mshtml.h> + +// Miranda header files +#include <newpluginapi.h> +#include <m_clist.h> +#include <m_langpack.h> +#include <m_options.h> +#include <m_protosvc.h> +#include <m_protoint.h> +#include <m_database.h> +#include <m_netlib.h> +#include <m_icolib.h> +#include <m_message.h> +#include <win2k.h> +#include <m_xml.h> +#include <m_avatars.h> +#include <m_hotkeys.h> +#include <m_gui.h> + +#include <m_folders.h> +#include <m_toptoolbar.h> + +#include "Options.h" +#include "version.h" +#include "resource.h" + +#define MODULENAME "NewsAggregator" +#define TAGSDEFAULT L"#<title>#\r\n#<link>#\r\n#<description>#" +#define DEFAULT_AVATARS_FOLDER "NewsAggregator" +#define DEFAULT_UPDATE_TIME 60 + +extern CDlgBase *pAddFeedDialog, *pImportDialog, *pExportDialog; +extern HNETLIBUSER hNetlibUser; +extern UINT_PTR timerId; +extern LIST<CFeedEditor> g_arFeeds; +// check if Feeds is currently updating +extern bool ThreadRunning; +extern bool UpdateListFlag; +extern wchar_t tszRoot[MAX_PATH]; + +struct CMPlugin : public PLUGIN<CMPlugin> +{ + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +//============ STRUCT USED TO MAKE AN UPDATE LIST ============ + +struct NEWSCONTACTLIST { + MCONTACT hContact; + struct NEWSCONTACTLIST *next; +}; + +typedef struct NEWSCONTACTLIST UPDATELIST; + +extern UPDATELIST *UpdateListHead; +extern UPDATELIST *UpdateListTail; + +void UpdateListAdd(MCONTACT hContact); +void UpdateThreadProc(void*); +void DestroyUpdateList(void); + +extern HANDLE hUpdateMutex; +extern HGENMENU hService2[7]; + +int NewsAggrInit(WPARAM wParam,LPARAM lParam); +int OptInit(WPARAM wParam, LPARAM lParam); +int NewsAggrPreShutdown(WPARAM wParam,LPARAM lParam); +void NetlibInit(); +void NetlibUnInit(); +void InitMenu(); +void InitIcons(); +HICON LoadIconEx(const char* name, bool big); +HANDLE GetIconHandle(const char* name); + +INT_PTR NewsAggrGetName(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetCaps(WPARAM wp, LPARAM lp); +INT_PTR NewsAggrSetStatus(WPARAM wp, LPARAM /*lp*/); +INT_PTR NewsAggrGetStatus(WPARAM/* wp*/, LPARAM/* lp*/); +INT_PTR NewsAggrLoadIcon(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetInfo(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetAvatarInfo(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrRecvMessage(WPARAM wParam, LPARAM lParam); + +INT_PTR CheckAllFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR AddFeed(WPARAM wParam, LPARAM lParam); +INT_PTR ChangeFeed(WPARAM wParam, LPARAM lParam); +INT_PTR ImportFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR ExportFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR CheckFeed(WPARAM wParam, LPARAM lParam); +INT_PTR EnableDisable(WPARAM wParam, LPARAM lParam); +int OnToolbarLoaded(WPARAM wParam, LPARAM lParam); +void CALLBACK timerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); +void CALLBACK timerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); + +bool IsMyContact(MCONTACT hContact); +void GetNewsData(wchar_t *szUrl, char **szData, MCONTACT hContact, CFeedEditor *pEditDlg); +time_t DateToUnixTime(const char *stamp, bool FeedType); +void CheckCurrentFeed(MCONTACT hContact); +void CheckCurrentFeedAvatar(MCONTACT hContact); +LPCTSTR CheckFeed(wchar_t* tszURL, CFeedEditor *pEditDlg); +void UpdateMenu(bool State); +LPCTSTR ClearText(CMStringW &value, const wchar_t *message); +bool DownloadFile(LPCTSTR tszURL, LPCTSTR tszLocal); +void CreateAuthString(char *auth, MCONTACT hContact, CFeedEditor *pDlg); +MCONTACT GetContactByNick(const wchar_t *nick); +MCONTACT GetContactByURL(const wchar_t *url); + +// =============== NewsAggregator SERVICES ================ +// Check all Feeds info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHECKALLFEEDS "NewsAggregator/CheckAllFeeds" + +// Add new Feed channel +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_ADDFEED "NewsAggregator/AddNewsFeed" + +// Change Feed channel +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHANGEFEED "NewsAggregator/ChangeNewsFeed" + +// Import Feed channels from file +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_IMPORTFEEDS "NewsAggregator/ImportFeeds" + +// Export Feed channels to file +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_EXPORTFEEDS "NewsAggregator/ExportFeeds" + +// Check Feed info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHECKFEED "NewsAggregator/CheckFeed" + +// Enable/disable getting feed info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_ENABLED "NewsAggregator/Enabled" diff --git a/protocols/NewsAggregator/Src/version.h b/protocols/NewsAggregator/Src/version.h new file mode 100644 index 0000000000..a7d2126e30 --- /dev/null +++ b/protocols/NewsAggregator/Src/version.h @@ -0,0 +1,13 @@ +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 1 +#define __RELEASE_NUM 0 +#define __BUILD_NUM 5 + +#include <stdver.h> + +#define __PLUGIN_NAME "News aggregator" +#define __FILENAME "NewsAggregator.dll" +#define __DESCRIPTION "RSS/Atom news aggregator." +#define __AUTHOR "Mataes, FREAK_THEMIGHTY" +#define __AUTHORWEB "https://miranda-ng.org/p/NewsAggregator/" +#define __COPYRIGHT "© 2012-19 Mataes, FREAK_THEMIGHTY" |